[WIP,nftables] src: add ct timeout support

Message ID 20180613231104.14712-1-harshasharmaiitr@gmail.com
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series
  • [WIP,nftables] src: add ct timeout support
Related show

Commit Message

Harsha Sharma June 13, 2018, 11:11 p.m.
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\ established 132\; }
%nft add rule filter output ct timeout set test-tcp
%nft list ruleset

table ip filter {
	ct timeout test-tcp {
		protocol tcp
		l3proto ip
		syn_sent 120
		syn_recv 60
		established 132
		fin_wait 120
		close_wait 60
		last_ack 30
		time_wait 120
		close 10
		syn_sent2 120
		retrans 300
	}

	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 <harshasharmaiitr@gmail.com>
---
 include/linux/netfilter/nf_tables.h |  13 +++-
 include/rule.h                      |  17 +++++
 src/evaluate.c                      |   4 ++
 src/netlink.c                       |  19 ++++++
 src/parser_bison.y                  | 130 +++++++++++++++++++++++++++++++++++-
 src/rule.c                          |  67 ++++++++++++++++++-
 src/scanner.l                       |  13 ++++
 src/statement.c                     |   4 ++
 8 files changed, 264 insertions(+), 3 deletions(-)

Comments

Harsha Sharma June 13, 2018, 11:20 p.m. | #1
Hello,

On Thu, Jun 14, 2018 at 1:11 AM, Harsha Sharma
<harshasharmaiitr@gmail.com> wrote:
> 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\ established 132\; }
> %nft add rule filter output ct timeout set test-tcp
> %nft list ruleset
>
> table ip filter {
>         ct timeout test-tcp {
>                 protocol tcp
>                 l3proto ip
>                 syn_sent 120
>                 syn_recv 60
>                 established 132
>                 fin_wait 120
>                 close_wait 60
>                 last_ack 30
>                 time_wait 120
>                 close 10
>                 syn_sent2 120
>                 retrans 300
>         }
>
>         chain output {
>                 ct timeout set "test-tcp"
>         }
> }
>
> %nft delete rule filter output handle <handle>
> %nft delete ct timeout filter test-tcp

Any suggestions on the syntax are welcome.

> Signed-off-by: Harsha Sharma <harshasharmaiitr@gmail.com>
> ---
>  include/linux/netfilter/nf_tables.h |  13 +++-
>  include/rule.h                      |  17 +++++
>  src/evaluate.c                      |   4 ++
>  src/netlink.c                       |  19 ++++++
>  src/parser_bison.y                  | 130 +++++++++++++++++++++++++++++++++++-
>  src/rule.c                          |  67 ++++++++++++++++++-
>  src/scanner.l                       |  13 ++++
>  src/statement.c                     |   4 ++
>  8 files changed, 264 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
> index 88e0ca1..8e7c757 100644
> --- a/include/linux/netfilter/nf_tables.h
> +++ b/include/linux/netfilter/nf_tables.h
> @@ -958,6 +958,7 @@ enum nft_socket_keys {
>   * @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,
> @@ -983,6 +984,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)
> @@ -1373,13 +1375,22 @@ 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_CT_TIMEOUT  6
> +#define __NFT_OBJECT_MAX       7
>  #define NFT_OBJECT_MAX         (__NFT_OBJECT_MAX - 1)
>
>  /**
> diff --git a/include/rule.h b/include/rule.h
> index 909ff36..e52e9b3 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,12 @@ struct ct_helper {
>         uint8_t l4proto;
>  };
>
> +struct ct_timeout {
> +       uint16_t l3proto;
> +       uint8_t l4proto;
> +       uint32_t *timeout;
> +};
> +
>  struct limit {
>         uint64_t        rate;
>         uint64_t        unit;
> @@ -336,6 +344,7 @@ struct obj {
>                 struct quota            quota;
>                 struct ct_helper        ct_helper;
>                 struct limit            limit;
> +               struct ct_timeout       ct_timeout;
>         };
>  };
>
> @@ -462,6 +471,7 @@ enum cmd_obj {
>         CMD_OBJ_LIMITS,
>         CMD_OBJ_FLOWTABLE,
>         CMD_OBJ_FLOWTABLES,
> +       CMD_OBJ_CT_TIMEOUT,
>  };
>
>  struct markup {
> @@ -617,4 +627,11 @@ enum udata_set_elem_flags {
>         SET_ELEM_F_INTERVAL_OPEN        = 0x1,
>  };
>
> +struct timeout_protocol {
> +       uint32_t attr_max;
> +       const char *const *state_to_name;
> +};
> +
> +extern struct timeout_protocol timeout_protocol[IPPROTO_MAX];
> +
>  #endif /* NFTABLES_RULE_H */
> diff --git a/src/evaluate.c b/src/evaluate.c
> index c4ee3cc..69f3230 100644
> --- a/src/evaluate.c
> +++ b/src/evaluate.c
> @@ -3130,6 +3130,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:
> @@ -3157,6 +3158,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:
> @@ -3289,6 +3291,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 864947b..9eaf253 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,18 @@ 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);
> +               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 +1451,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(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 33915ed..7961981 100644
> --- a/src/parser_bison.y
> +++ b/src/parser_bison.y
> @@ -12,6 +12,7 @@
>
>  #include <stddef.h>
>  #include <stdio.h>
> +#include <stdlib.h>
>  #include <inttypes.h>
>  #include <syslog.h>
>  #include <netinet/ip.h>
> @@ -154,6 +155,7 @@ int nft_lex(void *, void *, void *);
>         struct handle_spec      handle_spec;
>         struct position_spec    position_spec;
>         const struct exthdr_desc *exthdr_desc;
> +       struct ct_timeout       *ct_timeout;
>  }
>
>  %token TOKEN_EOF 0             "end of file"
> @@ -502,6 +504,18 @@ int nft_lex(void *, void *, void *);
>
>  %token EXTHDR                  "exthdr"
>
> +%token T_ESTABLISHED           "established"
> +%token T_SYN_SENT              "syn_sent"
> +%token T_SYN_RECV              "syn_recv"
> +%token T_FIN_WAIT              "fin_wait"
> +%token T_CLOSE_WAIT            "close_wait"
> +%token T_LAST_ACK              "last_ack"
> +%token T_TIME_WAIT             "time_wait"
> +%token T_CLOSE                 "close"
> +%token T_LISTEN                "listen"
> +
> +%token T_REPLIED               "replied"
> +%token T_UNREPLIED             "unreplied"
>  %type <string>                 identifier type_identifier string comment_spec
>  %destructor { xfree($$); }     identifier type_identifier string comment_spec
>
> @@ -545,7 +559,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
> @@ -741,6 +755,9 @@ int nft_lex(void *, void *, void *);
>  %type <val>                    exthdr_key
>
>  %type <val>                    ct_l4protoname ct_obj_type
> +%type <val>                    tcp_states tcp_state
> +%type <val>                    udp_states udp_state
> +%type <val>                    timeout_states
>
>  %%
>
> @@ -949,6 +966,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);
> @@ -1030,6 +1051,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);
> @@ -1222,6 +1247,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
> @@ -1453,6 +1482,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
> @@ -1747,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
> @@ -3169,12 +3216,69 @@ 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         :       tcp_states
> +                       |       udp_states
> +                       ;
> +
> +tcp_states             :       tcp_states      tcp_state
> +                       |       tcp_state
> +                       ;
> +
> +tcp_state              :       T_SYN_SENT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_SYN_SENT;
> +                       }

I want to parse multiple states in this way :

> +                       |       T_SYN_RECV
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_SYN_RECV;
> +                       }
> +                       |       T_ESTABLISHED
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_ESTABLISHED;
> +                       }
> +                       |       T_FIN_WAIT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_FIN_WAIT;
> +                       }
> +                       |       T_CLOSE_WAIT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE_WAIT;
> +                       }
> +                       |       T_LAST_ACK
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_LAST_ACK;
> +                       }
> +                       |       T_TIME_WAIT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_TIME_WAIT;
> +                       }
> +                       |       T_CLOSE
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE;
> +                       }
> +                       ;
> +
> +udp_states             :       udp_states      udp_state
> +                       |       udp_state
> +                       ;
> +
> +udp_state              :       T_REPLIED
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_UDP_REPLIED;
> +                       }
> +                       |       T_UNREPLIED
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_UDP_UNREPLIED;
> +                       }
> +                       ;
> +
>  ct_helper_config               :       TYPE    QUOTED_STRING   PROTOCOL        ct_l4protoname  stmt_separator
>                         {
>                                 struct ct_helper *ct;
> @@ -3196,6 +3300,24 @@ ct_helper_config         :       TYPE    QUOTED_STRING   PROTOCOL        ct_l4protoname  stmt_separator
>                         }
>                         ;
>
> +ct_timeout_config              :       PROTOCOL        ct_l4protoname  timeout_states  NUM      stmt_separator
> +                       {
> +                               struct ct_timeout *ct;
> +                               int l4proto = $2;
> +                               int type = $3;
> +                               size_t timeout_array_size = sizeof(uint32_t) * (timeout_protocol[l4proto].attr_max);
> +
> +                               ct = &$<obj>0->ct_timeout;
> +                               ct->l4proto = l4proto;
> +                               ct->timeout = calloc(1, timeout_array_size);
> +                               ct->timeout[type] = $4;
> +                       }
> +                       |       L3PROTOCOL      family_spec_explicit    stmt_separator
> +                       {
> +                               $<obj>0->ct_timeout.l3proto = $2;
> +                       }
> +                       ;
> +
>  ct_obj_alloc           :
>                         {
>                                 $$ = obj_alloc(&@$);
> @@ -3670,6 +3792,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
>                         ;
>
> @@ -3718,6 +3841,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 56b956a..295fc0a 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -29,6 +29,36 @@
>  #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",
> +};
> +
> +struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
> +       [IPPROTO_TCP]   = {
> +               .attr_max       = NFTA_CT_TIMEOUT_ATTR_TCP_MAX,
> +               .state_to_name  = tcp_state_to_name,
> +       },
> +       [IPPROTO_UDP]   = {
> +               .attr_max       = NFTA_CT_TIMEOUT_ATTR_UDP_MAX,
> +               .state_to_name  = udp_state_to_name,
> +       },
> +};
> +
>  void handle_free(struct handle *h)
>  {
>         xfree(h->table.name);
> @@ -1089,6 +1119,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;
> @@ -1183,6 +1214,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:
> @@ -1267,7 +1299,11 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
>         case CMD_OBJ_QUOTA:
>                 return netlink_delete_obj(ctx, cmd, NFT_OBJECT_QUOTA);
>         case CMD_OBJ_CT_HELPER:
> -               return netlink_delete_obj(ctx, cmd, NFT_OBJECT_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:
> @@ -1418,6 +1454,17 @@ static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx)
>                 nft_print(octx, "%d\n", l4);
>  }
>
> +static void print_proto_timeout_policy(uint8_t l4, uint32_t *timeout, struct output_ctx *octx)
> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < timeout_protocol[l4].attr_max; ++i) {
> +               if (timeout[i]) {
> +                       nft_print(octx, "\t\t%s %u\n", timeout_protocol[l4].state_to_name[i], timeout[i]);
> +               }
> +       }
> +}
> +
>  static void obj_print_data(const struct obj *obj,
>                            struct print_fmt_options *opts,
>                            struct output_ctx *octx)
> @@ -1465,6 +1512,18 @@ static void obj_print_data(const struct obj *obj,
>                 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, "\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;
> @@ -1511,6 +1570,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)
> @@ -1525,6 +1585,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)
> @@ -1875,6 +1936,7 @@ 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:
>         case CMD_OBJ_LIMIT:
>         case CMD_OBJ_LIMITS:
>                 return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
> @@ -2092,6 +2154,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/scanner.l b/src/scanner.l
> index 416bd27..330d966 100644
> --- a/src/scanner.l
> +++ b/src/scanner.l
> @@ -547,6 +547,19 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
>
>  "exthdr"               { return EXTHDR; }
>
> +"established"          { return T_ESTABLISHED; }
> +"syn_sent"             { return T_SYN_SENT; }
> +"syn_recv"             { return T_SYN_RECV; }
> +"fin_wait"             { return T_FIN_WAIT; }
> +"close_wait"           { return T_CLOSE_WAIT; }
> +"last_ack"             { return T_LAST_ACK; }
> +"time_wait"            { return T_TIME_WAIT; }
> +"close"                { return T_CLOSE; }
> +"listen"               { return T_LISTEN; }
> +
> +"replied"              { return T_REPLIED; }
> +"unreplied"            { return T_UNREPLIED; }
> +
>  {addrstring}           {
>                                 yylval->string = xstrdup(yytext);
>                                 return STRING;
> diff --git a/src/statement.c b/src/statement.c
> index 6f5e666..3f88729 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 majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Harsha Sharma June 13, 2018, 11:26 p.m. | #2
Hello,

On Thu, Jun 14, 2018 at 1:11 AM, Harsha Sharma
<harshasharmaiitr@gmail.com> wrote:
> 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\ established 132\; }
> %nft add rule filter output ct timeout set test-tcp
> %nft list ruleset
>
> table ip filter {
>         ct timeout test-tcp {
>                 protocol tcp
>                 l3proto ip
>                 syn_sent 120
>                 syn_recv 60
>                 established 132
>                 fin_wait 120
>                 close_wait 60
>                 last_ack 30
>                 time_wait 120
>                 close 10
>                 syn_sent2 120
>                 retrans 300
>         }
>
>         chain output {
>                 ct timeout set "test-tcp"
>         }
> }
>
> %nft delete rule filter output handle <handle>
> %nft delete ct timeout filter test-tcp

Any suggestions for changes in syntax are welcome.

> Signed-off-by: Harsha Sharma <harshasharmaiitr@gmail.com>
> ---
>  include/linux/netfilter/nf_tables.h |  13 +++-
>  include/rule.h                      |  17 +++++
>  src/evaluate.c                      |   4 ++
>  src/netlink.c                       |  19 ++++++
>  src/parser_bison.y                  | 130 +++++++++++++++++++++++++++++++++++-
>  src/rule.c                          |  67 ++++++++++++++++++-
>  src/scanner.l                       |  13 ++++
>  src/statement.c                     |   4 ++
>  8 files changed, 264 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
> index 88e0ca1..8e7c757 100644
> --- a/include/linux/netfilter/nf_tables.h
> +++ b/include/linux/netfilter/nf_tables.h
> @@ -958,6 +958,7 @@ enum nft_socket_keys {
>   * @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,
> @@ -983,6 +984,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)
> @@ -1373,13 +1375,22 @@ 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_CT_TIMEOUT  6
> +#define __NFT_OBJECT_MAX       7
>  #define NFT_OBJECT_MAX         (__NFT_OBJECT_MAX - 1)
>
>  /**
> diff --git a/include/rule.h b/include/rule.h
> index 909ff36..e52e9b3 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,12 @@ struct ct_helper {
>         uint8_t l4proto;
>  };
>
> +struct ct_timeout {
> +       uint16_t l3proto;
> +       uint8_t l4proto;
> +       uint32_t *timeout;
> +};
> +
>  struct limit {
>         uint64_t        rate;
>         uint64_t        unit;
> @@ -336,6 +344,7 @@ struct obj {
>                 struct quota            quota;
>                 struct ct_helper        ct_helper;
>                 struct limit            limit;
> +               struct ct_timeout       ct_timeout;
>         };
>  };
>
> @@ -462,6 +471,7 @@ enum cmd_obj {
>         CMD_OBJ_LIMITS,
>         CMD_OBJ_FLOWTABLE,
>         CMD_OBJ_FLOWTABLES,
> +       CMD_OBJ_CT_TIMEOUT,
>  };
>
>  struct markup {
> @@ -617,4 +627,11 @@ enum udata_set_elem_flags {
>         SET_ELEM_F_INTERVAL_OPEN        = 0x1,
>  };
>
> +struct timeout_protocol {
> +       uint32_t attr_max;
> +       const char *const *state_to_name;
> +};
> +
> +extern struct timeout_protocol timeout_protocol[IPPROTO_MAX];
> +
>  #endif /* NFTABLES_RULE_H */
> diff --git a/src/evaluate.c b/src/evaluate.c
> index c4ee3cc..69f3230 100644
> --- a/src/evaluate.c
> +++ b/src/evaluate.c
> @@ -3130,6 +3130,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:
> @@ -3157,6 +3158,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:
> @@ -3289,6 +3291,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 864947b..9eaf253 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,18 @@ 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);
> +               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 +1451,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(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 33915ed..7961981 100644
> --- a/src/parser_bison.y
> +++ b/src/parser_bison.y
> @@ -12,6 +12,7 @@
>
>  #include <stddef.h>
>  #include <stdio.h>
> +#include <stdlib.h>
>  #include <inttypes.h>
>  #include <syslog.h>
>  #include <netinet/ip.h>
> @@ -154,6 +155,7 @@ int nft_lex(void *, void *, void *);
>         struct handle_spec      handle_spec;
>         struct position_spec    position_spec;
>         const struct exthdr_desc *exthdr_desc;
> +       struct ct_timeout       *ct_timeout;
>  }
>
>  %token TOKEN_EOF 0             "end of file"
> @@ -502,6 +504,18 @@ int nft_lex(void *, void *, void *);
>
>  %token EXTHDR                  "exthdr"
>
> +%token T_ESTABLISHED           "established"
> +%token T_SYN_SENT              "syn_sent"
> +%token T_SYN_RECV              "syn_recv"
> +%token T_FIN_WAIT              "fin_wait"
> +%token T_CLOSE_WAIT            "close_wait"
> +%token T_LAST_ACK              "last_ack"
> +%token T_TIME_WAIT             "time_wait"
> +%token T_CLOSE                 "close"
> +%token T_LISTEN                "listen"
> +
> +%token T_REPLIED               "replied"
> +%token T_UNREPLIED             "unreplied"
>  %type <string>                 identifier type_identifier string comment_spec
>  %destructor { xfree($$); }     identifier type_identifier string comment_spec
>
> @@ -545,7 +559,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
> @@ -741,6 +755,9 @@ int nft_lex(void *, void *, void *);
>  %type <val>                    exthdr_key
>
>  %type <val>                    ct_l4protoname ct_obj_type
> +%type <val>                    tcp_states tcp_state
> +%type <val>                    udp_states udp_state
> +%type <val>                    timeout_states
>
>  %%
>
> @@ -949,6 +966,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);
> @@ -1030,6 +1051,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);
> @@ -1222,6 +1247,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
> @@ -1453,6 +1482,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
> @@ -1747,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
> @@ -3169,12 +3216,69 @@ 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         :       tcp_states
> +                       |       udp_states
> +                       ;
> +
> +tcp_states             :       tcp_states      tcp_state
> +                       |       tcp_state
> +                       ;
> +
> +tcp_state              :       T_SYN_SENT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_SYN_SENT;
> +                       }

I want to parse multiple states like this :
tcp_state            :        T_SYN_SENT     NUM

But, I'm not sure how to parse both type of state and timeout policy
value for multiple states. (for something like this " established 100
close 10").
Thanks.

> +                       |       T_SYN_RECV
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_SYN_RECV;
> +                       }
> +                       |       T_ESTABLISHED
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_ESTABLISHED;
> +                       }
> +                       |       T_FIN_WAIT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_FIN_WAIT;
> +                       }
> +                       |       T_CLOSE_WAIT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE_WAIT;
> +                       }
> +                       |       T_LAST_ACK
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_LAST_ACK;
> +                       }
> +                       |       T_TIME_WAIT
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_TIME_WAIT;
> +                       }
> +                       |       T_CLOSE
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE;
> +                       }
> +                       ;
> +
> +udp_states             :       udp_states      udp_state
> +                       |       udp_state
> +                       ;
> +
> +udp_state              :       T_REPLIED
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_UDP_REPLIED;
> +                       }
> +                       |       T_UNREPLIED
> +                       {
> +                               $$ = NFTA_CT_TIMEOUT_ATTR_UDP_UNREPLIED;
> +                       }
> +                       ;
> +
>  ct_helper_config               :       TYPE    QUOTED_STRING   PROTOCOL        ct_l4protoname  stmt_separator
>                         {
>                                 struct ct_helper *ct;
> @@ -3196,6 +3300,24 @@ ct_helper_config         :       TYPE    QUOTED_STRING   PROTOCOL        ct_l4protoname  stmt_separator
>                         }
>                         ;
>
> +ct_timeout_config              :       PROTOCOL        ct_l4protoname  timeout_states  NUM      stmt_separator
> +                       {
> +                               struct ct_timeout *ct;
> +                               int l4proto = $2;
> +                               int type = $3;
> +                               size_t timeout_array_size = sizeof(uint32_t) * (timeout_protocol[l4proto].attr_max);
> +
> +                               ct = &$<obj>0->ct_timeout;
> +                               ct->l4proto = l4proto;
> +                               ct->timeout = calloc(1, timeout_array_size);
> +                               ct->timeout[type] = $4;
> +                       }
> +                       |       L3PROTOCOL      family_spec_explicit    stmt_separator
> +                       {
> +                               $<obj>0->ct_timeout.l3proto = $2;
> +                       }
> +                       ;
> +
>  ct_obj_alloc           :
>                         {
>                                 $$ = obj_alloc(&@$);
> @@ -3670,6 +3792,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
>                         ;
>
> @@ -3718,6 +3841,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 56b956a..295fc0a 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -29,6 +29,36 @@
>  #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",
> +};
> +
> +struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
> +       [IPPROTO_TCP]   = {
> +               .attr_max       = NFTA_CT_TIMEOUT_ATTR_TCP_MAX,
> +               .state_to_name  = tcp_state_to_name,
> +       },
> +       [IPPROTO_UDP]   = {
> +               .attr_max       = NFTA_CT_TIMEOUT_ATTR_UDP_MAX,
> +               .state_to_name  = udp_state_to_name,
> +       },
> +};
> +
>  void handle_free(struct handle *h)
>  {
>         xfree(h->table.name);
> @@ -1089,6 +1119,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;
> @@ -1183,6 +1214,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:
> @@ -1267,7 +1299,11 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
>         case CMD_OBJ_QUOTA:
>                 return netlink_delete_obj(ctx, cmd, NFT_OBJECT_QUOTA);
>         case CMD_OBJ_CT_HELPER:
> -               return netlink_delete_obj(ctx, cmd, NFT_OBJECT_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:
> @@ -1418,6 +1454,17 @@ static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx)
>                 nft_print(octx, "%d\n", l4);
>  }
>
> +static void print_proto_timeout_policy(uint8_t l4, uint32_t *timeout, struct output_ctx *octx)
> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < timeout_protocol[l4].attr_max; ++i) {
> +               if (timeout[i]) {
> +                       nft_print(octx, "\t\t%s %u\n", timeout_protocol[l4].state_to_name[i], timeout[i]);
> +               }
> +       }
> +}
> +
>  static void obj_print_data(const struct obj *obj,
>                            struct print_fmt_options *opts,
>                            struct output_ctx *octx)
> @@ -1465,6 +1512,18 @@ static void obj_print_data(const struct obj *obj,
>                 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, "\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;
> @@ -1511,6 +1570,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)
> @@ -1525,6 +1585,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)
> @@ -1875,6 +1936,7 @@ 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:
>         case CMD_OBJ_LIMIT:
>         case CMD_OBJ_LIMITS:
>                 return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
> @@ -2092,6 +2154,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/scanner.l b/src/scanner.l
> index 416bd27..330d966 100644
> --- a/src/scanner.l
> +++ b/src/scanner.l
> @@ -547,6 +547,19 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
>
>  "exthdr"               { return EXTHDR; }
>
> +"established"          { return T_ESTABLISHED; }
> +"syn_sent"             { return T_SYN_SENT; }
> +"syn_recv"             { return T_SYN_RECV; }
> +"fin_wait"             { return T_FIN_WAIT; }
> +"close_wait"           { return T_CLOSE_WAIT; }
> +"last_ack"             { return T_LAST_ACK; }
> +"time_wait"            { return T_TIME_WAIT; }
> +"close"                { return T_CLOSE; }
> +"listen"               { return T_LISTEN; }
> +
> +"replied"              { return T_REPLIED; }
> +"unreplied"            { return T_UNREPLIED; }
> +
>  {addrstring}           {
>                                 yylval->string = xstrdup(yytext);
>                                 return STRING;
> diff --git a/src/statement.c b/src/statement.c
> index 6f5e666..3f88729 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 majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 88e0ca1..8e7c757 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -958,6 +958,7 @@  enum nft_socket_keys {
  * @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,
@@ -983,6 +984,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)
@@ -1373,13 +1375,22 @@  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_CT_TIMEOUT	6
+#define __NFT_OBJECT_MAX	7
 #define NFT_OBJECT_MAX		(__NFT_OBJECT_MAX - 1)
 
 /**
diff --git a/include/rule.h b/include/rule.h
index 909ff36..e52e9b3 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,12 @@  struct ct_helper {
 	uint8_t l4proto;
 };
 
+struct ct_timeout {
+	uint16_t l3proto;
+	uint8_t l4proto;
+	uint32_t *timeout;
+};
+
 struct limit {
 	uint64_t	rate;
 	uint64_t	unit;
@@ -336,6 +344,7 @@  struct obj {
 		struct quota		quota;
 		struct ct_helper	ct_helper;
 		struct limit		limit;
+		struct ct_timeout	ct_timeout;
 	};
 };
 
@@ -462,6 +471,7 @@  enum cmd_obj {
 	CMD_OBJ_LIMITS,
 	CMD_OBJ_FLOWTABLE,
 	CMD_OBJ_FLOWTABLES,
+	CMD_OBJ_CT_TIMEOUT,
 };
 
 struct markup {
@@ -617,4 +627,11 @@  enum udata_set_elem_flags {
 	SET_ELEM_F_INTERVAL_OPEN	= 0x1,
 };
 
+struct timeout_protocol {
+	uint32_t attr_max;
+	const char *const *state_to_name;
+};
+
+extern struct timeout_protocol timeout_protocol[IPPROTO_MAX];
+
 #endif /* NFTABLES_RULE_H */
diff --git a/src/evaluate.c b/src/evaluate.c
index c4ee3cc..69f3230 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3130,6 +3130,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:
@@ -3157,6 +3158,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:
@@ -3289,6 +3291,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 864947b..9eaf253 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,18 @@  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);
+		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 +1451,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(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 33915ed..7961981 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -12,6 +12,7 @@ 
 
 #include <stddef.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <inttypes.h>
 #include <syslog.h>
 #include <netinet/ip.h>
@@ -154,6 +155,7 @@  int nft_lex(void *, void *, void *);
 	struct handle_spec	handle_spec;
 	struct position_spec	position_spec;
 	const struct exthdr_desc *exthdr_desc;
+	struct ct_timeout	*ct_timeout;
 }
 
 %token TOKEN_EOF 0		"end of file"
@@ -502,6 +504,18 @@  int nft_lex(void *, void *, void *);
 
 %token EXTHDR			"exthdr"
 
+%token T_ESTABLISHED		"established"
+%token T_SYN_SENT		"syn_sent"
+%token T_SYN_RECV		"syn_recv"
+%token T_FIN_WAIT		"fin_wait"
+%token T_CLOSE_WAIT		"close_wait"
+%token T_LAST_ACK		"last_ack"
+%token T_TIME_WAIT		"time_wait"
+%token T_CLOSE			"close"
+%token T_LISTEN 		"listen"
+
+%token T_REPLIED		"replied"
+%token T_UNREPLIED		"unreplied"
 %type <string>			identifier type_identifier string comment_spec
 %destructor { xfree($$); }	identifier type_identifier string comment_spec
 
@@ -545,7 +559,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
@@ -741,6 +755,9 @@  int nft_lex(void *, void *, void *);
 %type <val>			exthdr_key
 
 %type <val>			ct_l4protoname ct_obj_type
+%type <val>			tcp_states tcp_state
+%type <val>			udp_states udp_state
+%type <val>			timeout_states
 
 %%
 
@@ -949,6 +966,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);
@@ -1030,6 +1051,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);
@@ -1222,6 +1247,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
@@ -1453,6 +1482,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
@@ -1747,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
@@ -3169,12 +3216,69 @@  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		:	tcp_states
+			|	udp_states
+			;
+
+tcp_states		:	tcp_states	tcp_state
+			|	tcp_state
+			;
+
+tcp_state		:	T_SYN_SENT
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_SYN_SENT;
+			}
+			|	T_SYN_RECV
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_SYN_RECV;
+			}
+			|	T_ESTABLISHED
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_ESTABLISHED;
+			}
+			|	T_FIN_WAIT
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_FIN_WAIT;
+			}
+			|	T_CLOSE_WAIT
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE_WAIT;
+			}
+			|	T_LAST_ACK
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_LAST_ACK;
+			}
+			|	T_TIME_WAIT
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_TIME_WAIT;
+			}
+			|	T_CLOSE
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE;
+			}
+			;
+
+udp_states		:	udp_states	udp_state
+			|	udp_state
+			;
+
+udp_state		:	T_REPLIED
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_UDP_REPLIED;
+			}
+			|	T_UNREPLIED
+			{
+				$$ = NFTA_CT_TIMEOUT_ATTR_UDP_UNREPLIED;
+			}
+			;
+
 ct_helper_config		:	TYPE	QUOTED_STRING	PROTOCOL	ct_l4protoname	stmt_separator
 			{
 				struct ct_helper *ct;
@@ -3196,6 +3300,24 @@  ct_helper_config		:	TYPE	QUOTED_STRING	PROTOCOL	ct_l4protoname	stmt_separator
 			}
 			;
 
+ct_timeout_config		:	PROTOCOL	ct_l4protoname	timeout_states	NUM	 stmt_separator
+			{
+				struct ct_timeout *ct;
+				int l4proto = $2;
+				int type = $3;
+				size_t timeout_array_size = sizeof(uint32_t) * (timeout_protocol[l4proto].attr_max);
+
+				ct = &$<obj>0->ct_timeout;
+				ct->l4proto = l4proto;
+				ct->timeout = calloc(1, timeout_array_size);
+				ct->timeout[type] = $4;
+			}
+			|	L3PROTOCOL	family_spec_explicit	stmt_separator
+			{
+				$<obj>0->ct_timeout.l3proto = $2;
+			}
+			;
+
 ct_obj_alloc		:
 			{
 				$$ = obj_alloc(&@$);
@@ -3670,6 +3792,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
 			;
 
@@ -3718,6 +3841,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 56b956a..295fc0a 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -29,6 +29,36 @@ 
 #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",
+};
+
+struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
+	[IPPROTO_TCP]   = {
+		.attr_max       = NFTA_CT_TIMEOUT_ATTR_TCP_MAX,
+		.state_to_name  = tcp_state_to_name,
+	},
+	[IPPROTO_UDP]   = {
+		.attr_max       = NFTA_CT_TIMEOUT_ATTR_UDP_MAX,
+		.state_to_name  = udp_state_to_name,
+	},
+};
+
 void handle_free(struct handle *h)
 {
 	xfree(h->table.name);
@@ -1089,6 +1119,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;
@@ -1183,6 +1214,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:
@@ -1267,7 +1299,11 @@  static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_QUOTA:
 		return netlink_delete_obj(ctx, cmd, NFT_OBJECT_QUOTA);
 	case CMD_OBJ_CT_HELPER:
-		return netlink_delete_obj(ctx, cmd, NFT_OBJECT_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:
@@ -1418,6 +1454,17 @@  static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx)
 		nft_print(octx, "%d\n", l4);
 }
 
+static void print_proto_timeout_policy(uint8_t l4, uint32_t *timeout, struct output_ctx *octx)
+{
+	unsigned int i;
+
+	for (i = 0; i < timeout_protocol[l4].attr_max; ++i) {
+		if (timeout[i]) {
+			nft_print(octx, "\t\t%s %u\n", timeout_protocol[l4].state_to_name[i], timeout[i]);
+		}
+	}
+}
+
 static void obj_print_data(const struct obj *obj,
 			   struct print_fmt_options *opts,
 			   struct output_ctx *octx)
@@ -1465,6 +1512,18 @@  static void obj_print_data(const struct obj *obj,
 		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, "\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;
@@ -1511,6 +1570,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)
@@ -1525,6 +1585,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)
@@ -1875,6 +1936,7 @@  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:
 	case CMD_OBJ_LIMIT:
 	case CMD_OBJ_LIMITS:
 		return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
@@ -2092,6 +2154,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/scanner.l b/src/scanner.l
index 416bd27..330d966 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -547,6 +547,19 @@  addrstring	({macaddr}|{ip4addr}|{ip6addr})
 
 "exthdr"		{ return EXTHDR; }
 
+"established"		{ return T_ESTABLISHED; }
+"syn_sent"		{ return T_SYN_SENT; }
+"syn_recv"		{ return T_SYN_RECV; }
+"fin_wait"		{ return T_FIN_WAIT; }
+"close_wait"		{ return T_CLOSE_WAIT; }
+"last_ack"		{ return T_LAST_ACK; }
+"time_wait"		{ return T_TIME_WAIT; }
+"close"		{ return T_CLOSE; }
+"listen"		{ return T_LISTEN; }
+
+"replied"		{ return T_REPLIED; }
+"unreplied"		{ return T_UNREPLIED; }
+
 {addrstring}		{
 				yylval->string = xstrdup(yytext);
 				return STRING;
diff --git a/src/statement.c b/src/statement.c
index 6f5e666..3f88729 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));