diff mbox series

[nft,v4] src: add synproxy stateful object support

Message ID 20190911214543.7029-1-ffmancera@riseup.net
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series [nft,v4] src: add synproxy stateful object support | expand

Commit Message

Fernando F. Mancera Sept. 11, 2019, 9:45 p.m. UTC
Add support for "synproxy" stateful object. For example (for TCP port 80 and
using maps with saddr):

table ip foo {
	synproxy https-synproxy {
		mss 1460
		wscale 7
		timestamp sack-perm
	}

	synproxy other-synproxy {
		mss 1460
		wscale 5
	}

	chain bar {
		tcp dport 80 synproxy name "https-synproxy"
		synproxy name ip saddr map { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" }
	}
}

Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
v1: initial patch
v2: fix a typo
v3: one argument per line
v4: add python tests
---
 include/linux/netfilter/nf_tables.h |   3 +-
 include/rule.h                      |  11 +++
 src/evaluate.c                      |   5 ++
 src/json.c                          |  20 ++++-
 src/mnl.c                           |   8 ++
 src/netlink.c                       |   8 ++
 src/parser_bison.y                  | 124 +++++++++++++++++++++++++++-
 src/parser_json.c                   |  22 ++++-
 src/rule.c                          |  50 +++++++++++
 src/scanner.l                       |   1 +
 src/statement.c                     |   1 +
 tests/py/ip/objects.t               |   4 +
 12 files changed, 251 insertions(+), 6 deletions(-)

Comments

Fernando F. Mancera Sept. 11, 2019, 9:51 p.m. UTC | #1
Hello,

I have one comment on this patch. As you can see there is no json output
test because right now it is not possible to create the json output of
the synproxy object reference.

I have been trying it but as we are supporting "synproxy" without
arguments in the statement expression, then I don't know how to deal
with the synproxy object reference json parsing.

Have you any hint on how should we deal with that? Thanks! :-)

On 9/11/19 11:45 PM, Fernando Fernandez Mancera wrote:
> Add support for "synproxy" stateful object. For example (for TCP port 80 and
> using maps with saddr):
> 
> table ip foo {
> 	synproxy https-synproxy {
> 		mss 1460
> 		wscale 7
> 		timestamp sack-perm
> 	}
> 
> 	synproxy other-synproxy {
> 		mss 1460
> 		wscale 5
> 	}
> 
> 	chain bar {
> 		tcp dport 80 synproxy name "https-synproxy"
> 		synproxy name ip saddr map { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" }
> 	}
> }
> 
> Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
> ---
> v1: initial patch
> v2: fix a typo
> v3: one argument per line
> v4: add python tests
> ---
>  include/linux/netfilter/nf_tables.h |   3 +-
>  include/rule.h                      |  11 +++
>  src/evaluate.c                      |   5 ++
>  src/json.c                          |  20 ++++-
>  src/mnl.c                           |   8 ++
>  src/netlink.c                       |   8 ++
>  src/parser_bison.y                  | 124 +++++++++++++++++++++++++++-
>  src/parser_json.c                   |  22 ++++-
>  src/rule.c                          |  50 +++++++++++
>  src/scanner.l                       |   1 +
>  src/statement.c                     |   1 +
>  tests/py/ip/objects.t               |   4 +
>  12 files changed, 251 insertions(+), 6 deletions(-)
> 
> diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
> index 0ff932d..ed8881a 100644
> --- a/include/linux/netfilter/nf_tables.h
> +++ b/include/linux/netfilter/nf_tables.h
> @@ -1481,7 +1481,8 @@ enum nft_ct_expectation_attributes {
>  #define NFT_OBJECT_CT_TIMEOUT	7
>  #define NFT_OBJECT_SECMARK	8
>  #define NFT_OBJECT_CT_EXPECT	9
> -#define __NFT_OBJECT_MAX	10
> +#define NFT_OBJECT_SYNPROXY	10
> +#define __NFT_OBJECT_MAX	11
>  #define NFT_OBJECT_MAX		(__NFT_OBJECT_MAX - 1)
>  
>  /**
> diff --git a/include/rule.h b/include/rule.h
> index 0ef6aac..2708cbe 100644
> --- a/include/rule.h
> +++ b/include/rule.h
> @@ -399,6 +399,12 @@ struct limit {
>  	uint32_t	flags;
>  };
>  
> +struct synproxy {
> +	uint16_t	mss;
> +	uint8_t		wscale;
> +	uint32_t	flags;
> +};
> +
>  struct secmark {
>  	char		ctx[NFT_SECMARK_CTX_MAXLEN];
>  };
> @@ -426,6 +432,7 @@ struct obj {
>  		struct ct_timeout	ct_timeout;
>  		struct secmark		secmark;
>  		struct ct_expect	ct_expect;
> +		struct synproxy		synproxy;
>  	};
>  };
>  
> @@ -529,6 +536,8 @@ enum cmd_ops {
>   * @CMD_OBJ_FLOWTABLES:	flow tables
>   * @CMD_OBJ_SECMARK:	secmark
>   * @CMD_OBJ_SECMARKS:	multiple secmarks
> + * @CMD_OBJ_SYNPROXY:	synproxy
> + * @CMD_OBJ_SYNPROXYS:	multiple synproxys
>   */
>  enum cmd_obj {
>  	CMD_OBJ_INVALID,
> @@ -561,6 +570,8 @@ enum cmd_obj {
>  	CMD_OBJ_SECMARK,
>  	CMD_OBJ_SECMARKS,
>  	CMD_OBJ_CT_EXPECT,
> +	CMD_OBJ_SYNPROXY,
> +	CMD_OBJ_SYNPROXYS,
>  };
>  
>  struct markup {
> diff --git a/src/evaluate.c b/src/evaluate.c
> index 29fe966..a56cd2a 100644
> --- a/src/evaluate.c
> +++ b/src/evaluate.c
> @@ -3743,6 +3743,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
>  	case CMD_OBJ_CT_TIMEOUT:
>  	case CMD_OBJ_SECMARK:
>  	case CMD_OBJ_CT_EXPECT:
> +	case CMD_OBJ_SYNPROXY:
>  		return obj_evaluate(ctx, cmd->object);
>  	default:
>  		BUG("invalid command object type %u\n", cmd->obj);
> @@ -3766,6 +3767,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
>  	case CMD_OBJ_LIMIT:
>  	case CMD_OBJ_SECMARK:
>  	case CMD_OBJ_CT_EXPECT:
> +	case CMD_OBJ_SYNPROXY:
>  		return 0;
>  	default:
>  		BUG("invalid command object type %u\n", cmd->obj);
> @@ -3911,6 +3913,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
>  		return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_SECMARK);
>  	case CMD_OBJ_CT_EXPECT:
>  		return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_EXPECT);
> +	case CMD_OBJ_SYNPROXY:
> +		return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_SYNPROXY);
>  	case CMD_OBJ_COUNTERS:
>  	case CMD_OBJ_QUOTAS:
>  	case CMD_OBJ_CT_HELPERS:
> @@ -3918,6 +3922,7 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
>  	case CMD_OBJ_SETS:
>  	case CMD_OBJ_FLOWTABLES:
>  	case CMD_OBJ_SECMARKS:
> +	case CMD_OBJ_SYNPROXYS:
>  		if (cmd->handle.table.name == NULL)
>  			return 0;
>  		if (table_lookup(&cmd->handle, &ctx->nft->cache) == NULL)
> diff --git a/src/json.c b/src/json.c
> index 55ce053..6adc801 100644
> --- a/src/json.c
> +++ b/src/json.c
> @@ -282,8 +282,8 @@ static json_t *obj_print_json(const struct obj *obj)
>  {
>  	const char *rate_unit = NULL, *burst_unit = NULL;
>  	const char *type = obj_type_name(obj->type);
> +	json_t *root, *tmp, *flags;
>  	uint64_t rate, burst;
> -	json_t *root, *tmp;
>  
>  	root = json_pack("{s:s, s:s, s:s, s:I}",
>  			"family", family2str(obj->handle.family),
> @@ -368,6 +368,24 @@ static json_t *obj_print_json(const struct obj *obj)
>  						    json_string(burst_unit));
>  		}
>  
> +		json_object_update(root, tmp);
> +		json_decref(tmp);
> +		break;
> +	case NFT_OBJECT_SYNPROXY:
> +		flags = json_array();
> +		tmp = json_pack("{s:i, s:i}",
> +				"mss", obj->synproxy.mss,
> +				"wscale", obj->synproxy.wscale);
> +		if (obj->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP)
> +			json_array_append_new(flags, json_string("timestamp"));
> +		if (obj->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM)
> +			json_array_append_new(flags, json_string("sack-perm"));
> +
> +		if (json_array_size(flags) > 0)
> +			json_object_set_new(tmp, "flags", flags);
> +		else
> +			json_decref(flags);
> +
>  		json_object_update(root, tmp);
>  		json_decref(tmp);
>  		break;
> diff --git a/src/mnl.c b/src/mnl.c
> index 8031bd6..57ff89f 100644
> --- a/src/mnl.c
> +++ b/src/mnl.c
> @@ -1036,6 +1036,14 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd,
>  		nftnl_obj_set_str(nlo, NFTNL_OBJ_SECMARK_CTX,
>  				  obj->secmark.ctx);
>  		break;
> +	case NFT_OBJECT_SYNPROXY:
> +		nftnl_obj_set_u16(nlo, NFTNL_OBJ_SYNPROXY_MSS,
> +				  obj->synproxy.mss);
> +		nftnl_obj_set_u8(nlo, NFTNL_OBJ_SYNPROXY_WSCALE,
> +				 obj->synproxy.wscale);
> +		nftnl_obj_set_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS,
> +				  obj->synproxy.flags);
> +		break;
>  	default:
>  		BUG("Unknown type %d\n", obj->type);
>  		break;
> diff --git a/src/netlink.c b/src/netlink.c
> index f8e1120..1e669e5 100644
> --- a/src/netlink.c
> +++ b/src/netlink.c
> @@ -1030,6 +1030,14 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
>  		obj->ct_expect.size =
>  			nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_EXPECT_SIZE);
>  		break;
> +	case NFT_OBJECT_SYNPROXY:
> +		obj->synproxy.mss =
> +			nftnl_obj_get_u16(nlo, NFTNL_OBJ_SYNPROXY_MSS);
> +		obj->synproxy.wscale =
> +			nftnl_obj_get_u8(nlo, NFTNL_OBJ_SYNPROXY_WSCALE);
> +		obj->synproxy.flags =
> +			nftnl_obj_get_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS);
> +		break;
>  	}
>  	obj->type = type;
>  
> diff --git a/src/parser_bison.y b/src/parser_bison.y
> index b7db1a2..3fccea6 100644
> --- a/src/parser_bison.y
> +++ b/src/parser_bison.y
> @@ -151,6 +151,7 @@ int nft_lex(void *, void *, void *);
>  	struct counter		*counter;
>  	struct quota		*quota;
>  	struct secmark		*secmark;
> +	struct synproxy		*synproxy;
>  	struct ct		*ct;
>  	struct limit		*limit;
>  	const struct datatype	*datatype;
> @@ -461,6 +462,7 @@ int nft_lex(void *, void *, void *);
>  %token COUNTERS			"counters"
>  %token QUOTAS			"quotas"
>  %token LIMITS			"limits"
> +%token SYNPROXYS		"synproxys"
>  %token HELPERS			"helpers"
>  
>  %token LOG			"log"
> @@ -592,7 +594,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 ct_timeout_block ct_expect_block limit_block secmark_block
> +%type <obj>			obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block ct_expect_block limit_block secmark_block synproxy_block
>  %destructor { obj_free($$); }	obj_block_alloc
>  
>  %type <list>			stmt_list
> @@ -700,8 +702,8 @@ int nft_lex(void *, void *, void *);
>  %type <expr>			and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
>  %destructor { expr_free($$); }	and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
>  
> -%type <obj>			counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj
> -%destructor { obj_free($$); }	counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj
> +%type <obj>			counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
> +%destructor { obj_free($$); }	counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
>  
>  %type <expr>			relational_expr
>  %destructor { expr_free($$); }	relational_expr
> @@ -787,6 +789,9 @@ int nft_lex(void *, void *, void *);
>  %destructor { xfree($$); }	limit_config
>  %type <secmark>			secmark_config
>  %destructor { xfree($$); }	secmark_config
> +%type <synproxy>		synproxy_config
> +%destructor { xfree($$); }	synproxy_config
> +%type <val>			synproxy_ts	synproxy_sack
>  
>  %type <expr>			tcp_hdr_expr
>  %destructor { expr_free($$); }	tcp_hdr_expr
> @@ -1012,6 +1017,10 @@ add_cmd			:	TABLE		table_spec
>  			{
>  				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
>  			}
> +			|	SYNPROXY	obj_spec	synproxy_obj
> +			{
> +				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
> +			}
>  			;
>  
>  replace_cmd		:	RULE		ruleid_spec	rule
> @@ -1105,6 +1114,10 @@ create_cmd		:	TABLE		table_spec
>  			{
>  				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3);
>  			}
> +			|	SYNPROXY	obj_spec	synproxy_obj
> +			{
> +				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
> +			}
>  			;
>  
>  insert_cmd		:	RULE		rule_position	rule
> @@ -1189,6 +1202,14 @@ delete_cmd		:	TABLE		table_spec
>  			{
>  				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
>  			}
> +			|	SYNPROXY	obj_spec
> +			{
> +				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
> +			}
> +			|	SYNPROXY	objid_spec
> +			{
> +				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
> +			}
>  			;
>  
>  get_cmd			:	ELEMENT		set_spec	set_block_expr
> @@ -1273,6 +1294,18 @@ list_cmd		:	TABLE		table_spec
>  			{
>  				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &$2, &@$, NULL);
>  			}
> +			|	SYNPROXYS	ruleset_spec
> +			{
> +				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$2, &@$, NULL);
> +			}
> +			|	SYNPROXYS	TABLE	table_spec
> +			{
> +				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL);
> +			}
> +			|	SYNPROXY	obj_spec
> +			{
> +				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
> +			}
>  			|	RULESET		ruleset_spec
>  			{
>  				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
> @@ -1592,6 +1625,17 @@ table_block		:	/* empty */	{ $$ = $<table>-1; }
>  				list_add_tail(&$4->list, &$1->objs);
>  				$$ = $1;
>  			}
> +			|	table_block	SYNPROXY	obj_identifier
> +					obj_block_alloc '{'	synproxy_block	'}'
> +					stmt_separator
> +			{
> +				$4->location = @3;
> +				$4->type = NFT_OBJECT_SYNPROXY;
> +				handle_merge(&$4->handle, &$3);
> +				handle_free(&$3);
> +				list_add_tail(&$4->list, &$1->objs);
> +				$$ = $1;
> +			}
>  			;
>  
>  chain_block_alloc	:	/* empty */
> @@ -1928,6 +1972,16 @@ secmark_block		:	/* empty */	{ $$ = $<obj>-1; }
>  			}
>  			;
>  
> +synproxy_block		:	/* empty */	{ $$ = $<obj>-1; }
> +			|	synproxy_block	common_block
> +			|	synproxy_block	stmt_separator
> +			|	synproxy_block	synproxy_config
> +			{
> +				$1->synproxy = *$2;
> +				$$ = $1;
> +			}
> +			;
> +
>  type_identifier		:	STRING	{ $$ = $1; }
>  			|	MARK	{ $$ = xstrdup("mark"); }
>  			|	DSCP	{ $$ = xstrdup("dscp"); }
> @@ -2788,6 +2842,12 @@ synproxy_stmt_alloc	:	SYNPROXY
>  			{
>  				$$ = synproxy_stmt_alloc(&@$);
>  			}
> +			|	SYNPROXY	NAME	stmt_expr
> +			{
> +				$$ = objref_stmt_alloc(&@$);
> +				$$->objref.type = NFT_OBJECT_SYNPROXY;
> +				$$->objref.expr = $3;
> +			}
>  			;
>  
>  synproxy_args		:	synproxy_arg
> @@ -2817,6 +2877,64 @@ synproxy_arg		:	MSS	NUM
>  			}
>  			;
>  
> +synproxy_config		:	MSS	NUM	WSCALE	NUM	synproxy_ts	synproxy_sack
> +			{
> +				struct synproxy *synproxy;
> +				uint32_t flags = 0;
> +
> +				synproxy = xzalloc(sizeof(*synproxy));
> +				synproxy->mss = $2;
> +				flags |= NF_SYNPROXY_OPT_MSS;
> +				synproxy->wscale = $4;
> +				flags |= NF_SYNPROXY_OPT_WSCALE;
> +				if ($5)
> +					flags |= $5;
> +				if ($6)
> +					flags |= $6;
> +				synproxy->flags = flags;
> +				$$ = synproxy;
> +			}
> +			|	MSS	NUM	stmt_separator	WSCALE	NUM	stmt_separator	synproxy_ts	synproxy_sack
> +			{
> +				struct synproxy *synproxy;
> +				uint32_t flags = 0;
> +
> +				synproxy = xzalloc(sizeof(*synproxy));
> +				synproxy->mss = $2;
> +				flags |= NF_SYNPROXY_OPT_MSS;
> +				synproxy->wscale = $5;
> +				flags |= NF_SYNPROXY_OPT_WSCALE;
> +				if ($7)
> +					flags |= $7;
> +				if ($8)
> +					flags |= $8;
> +				synproxy->flags = flags;
> +				$$ = synproxy;
> +			}
> +			;
> +
> +synproxy_obj		:	synproxy_config
> +			{
> +				$$ = obj_alloc(&@$);
> +				$$->type = NFT_OBJECT_SYNPROXY;
> +				$$->synproxy = *$1;
> +			}
> +			;
> +
> +synproxy_ts		:	/* empty */	{ $$ = 0; }
> +			|	TIMESTAMP
> +			{
> +				$$ = NF_SYNPROXY_OPT_TIMESTAMP;
> +			}
> +			;
> +
> +synproxy_sack		:	/* empty */	{ $$ = 0; }
> +			|	SACKPERM
> +			{
> +				$$ = NF_SYNPROXY_OPT_SACK_PERM;
> +			}
> +			;
> +
>  primary_stmt_expr	:	symbol_expr		{ $$ = $1; }
>  			|	integer_expr		{ $$ = $1; }
>  			|	boolean_expr		{ $$ = $1; }
> diff --git a/src/parser_json.c b/src/parser_json.c
> index 183d9c9..bcac5e1 100644
> --- a/src/parser_json.c
> +++ b/src/parser_json.c
> @@ -3019,8 +3019,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
>  	const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes";
>  	uint32_t l3proto = NFPROTO_UNSPEC;
>  	struct handle h = { 0 };
> +	int inv = 0, flags = 0;
>  	struct obj *obj;
> -	int inv = 0;
> +	json_t *jflags;
>  
>  	if (json_unpack_err(ctx, root, "{s:s, s:s}",
>  			    "family", &family,
> @@ -3196,6 +3197,25 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
>  		obj->limit.unit = seconds_from_unit(tmp);
>  		obj->limit.flags = inv ? NFT_LIMIT_F_INV : 0;
>  		break;
> +	case CMD_OBJ_SYNPROXY:
> +		obj->type = NFT_OBJECT_SYNPROXY;
> +		if (json_unpack_err(ctx, root, "{s:i, s:i}",
> +				    "mss", &obj->synproxy.mss,
> +				    "wscale", &obj->synproxy.wscale)) {
> +			obj_free(obj);
> +			return NULL;
> +		}
> +		obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
> +		obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
> +		if (!json_unpack(root, "{s:o}", "flags", &jflags)) {
> +			flags = json_parse_synproxy_flags(ctx, jflags);
> +			if (flags < 0) {
> +				obj_free(obj);
> +				return NULL;
> +			}
> +			obj->synproxy.flags |= flags;
> +		}
> +		break;
>  	default:
>  		BUG("Invalid CMD '%d'", cmd_obj);
>  	}
> diff --git a/src/rule.c b/src/rule.c
> index 1912513..5bb1c1d 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -32,6 +32,7 @@
>  #include <linux/netfilter.h>
>  #include <linux/netfilter_arp.h>
>  #include <linux/netfilter_ipv4.h>
> +#include <linux/netfilter/nf_synproxy.h>
>  #include <net/if.h>
>  #include <linux/netfilter_bridge.h>
>  
> @@ -1451,6 +1452,7 @@ void cmd_free(struct cmd *cmd)
>  		case CMD_OBJ_CT_EXPECT:
>  		case CMD_OBJ_LIMIT:
>  		case CMD_OBJ_SECMARK:
> +		case CMD_OBJ_SYNPROXY:
>  			obj_free(cmd->object);
>  			break;
>  		case CMD_OBJ_FLOWTABLE:
> @@ -1542,6 +1544,7 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
>  	case CMD_OBJ_CT_EXPECT:
>  	case CMD_OBJ_LIMIT:
>  	case CMD_OBJ_SECMARK:
> +	case CMD_OBJ_SYNPROXY:
>  		return mnl_nft_obj_add(ctx, cmd, flags);
>  	case CMD_OBJ_FLOWTABLE:
>  		return mnl_nft_flowtable_add(ctx, cmd, flags);
> @@ -1627,6 +1630,8 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
>  		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_LIMIT);
>  	case CMD_OBJ_SECMARK:
>  		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SECMARK);
> +	case CMD_OBJ_SYNPROXY:
> +		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SYNPROXY);
>  	case CMD_OBJ_FLOWTABLE:
>  		return mnl_nft_flowtable_del(ctx, cmd);
>  	default:
> @@ -1778,6 +1783,22 @@ static void print_proto_timeout_policy(uint8_t l4, const uint32_t *timeout,
>  	nft_print(octx, " }%s", opts->stmt_separator);
>  }
>  
> +static const char *synproxy_sack_to_str(const uint32_t flags)
> +{
> +        if (flags & NF_SYNPROXY_OPT_SACK_PERM)
> +                return "sack-perm";
> +
> +        return "";
> +}
> +
> +static const char *synproxy_timestamp_to_str(const uint32_t flags)
> +{
> +        if (flags & NF_SYNPROXY_OPT_TIMESTAMP)
> +                return "timestamp";
> +
> +        return "";
> +}
> +
>  static void obj_print_data(const struct obj *obj,
>  			   struct print_fmt_options *opts,
>  			   struct output_ctx *octx)
> @@ -1911,6 +1932,30 @@ static void obj_print_data(const struct obj *obj,
>  		nft_print(octx, "%s", opts->nl);
>  		}
>  		break;
> +	case NFT_OBJECT_SYNPROXY: {
> +		uint32_t flags = obj->synproxy.flags;
> +		const char *sack_str = synproxy_sack_to_str(flags);
> +		const char *ts_str = synproxy_timestamp_to_str(flags);
> +
> +		nft_print(octx, " %s {", obj->handle.obj.name);
> +		if (nft_output_handle(octx))
> +			nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
> +
> +		if (flags & NF_SYNPROXY_OPT_MSS) {
> +			nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
> +			nft_print(octx, "mss %u", obj->synproxy.mss);
> +		}
> +		if (flags & NF_SYNPROXY_OPT_WSCALE) {
> +			nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
> +			nft_print(octx, "wscale %u", obj->synproxy.wscale);
> +		}
> +		if (flags & (NF_SYNPROXY_OPT_TIMESTAMP | NF_SYNPROXY_OPT_SACK_PERM)) {
> +			nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
> +			nft_print(octx, "%s %s", ts_str, sack_str);
> +		}
> +		nft_print(octx, "%s", opts->stmt_separator);
> +		}
> +		break;
>  	default:
>  		nft_print(octx, " unknown {%s", opts->nl);
>  		break;
> @@ -1924,6 +1969,7 @@ static const char * const obj_type_name_array[] = {
>  	[NFT_OBJECT_LIMIT]	= "limit",
>  	[NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
>  	[NFT_OBJECT_SECMARK]	= "secmark",
> +	[NFT_OBJECT_SYNPROXY]	= "synproxy",
>  	[NFT_OBJECT_CT_EXPECT]	= "ct expectation",
>  };
>  
> @@ -1941,6 +1987,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {
>  	[NFT_OBJECT_LIMIT]	= CMD_OBJ_LIMIT,
>  	[NFT_OBJECT_CT_TIMEOUT] = CMD_OBJ_CT_TIMEOUT,
>  	[NFT_OBJECT_SECMARK]	= CMD_OBJ_SECMARK,
> +	[NFT_OBJECT_SYNPROXY]	= CMD_OBJ_SYNPROXY,
>  	[NFT_OBJECT_CT_EXPECT]	= CMD_OBJ_CT_EXPECT,
>  };
>  
> @@ -2308,6 +2355,9 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
>  	case CMD_OBJ_SECMARK:
>  	case CMD_OBJ_SECMARKS:
>  		return do_list_obj(ctx, cmd, NFT_OBJECT_SECMARK);
> +	case CMD_OBJ_SYNPROXY:
> +	case CMD_OBJ_SYNPROXYS:
> +		return do_list_obj(ctx, cmd, NFT_OBJECT_SYNPROXY);
>  	case CMD_OBJ_FLOWTABLES:
>  		return do_list_flowtables(ctx, cmd);
>  	default:
> diff --git a/src/scanner.l b/src/scanner.l
> index fdf84ba..3de5a9e 100644
> --- a/src/scanner.l
> +++ b/src/scanner.l
> @@ -330,6 +330,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
>  "counters"		{ return COUNTERS; }
>  "quotas"		{ return QUOTAS; }
>  "limits"		{ return LIMITS; }
> +"synproxys"		{ return SYNPROXYS; }
>  
>  "log"			{ return LOG; }
>  "prefix"		{ return PREFIX; }
> diff --git a/src/statement.c b/src/statement.c
> index 12689ee..5aa5b1e 100644
> --- a/src/statement.c
> +++ b/src/statement.c
> @@ -209,6 +209,7 @@ static const char *objref_type[NFT_OBJECT_MAX + 1] = {
>  	[NFT_OBJECT_LIMIT]	= "limit",
>  	[NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
>  	[NFT_OBJECT_SECMARK]	= "secmark",
> +	[NFT_OBJECT_SYNPROXY]	= "synproxy",
>  	[NFT_OBJECT_CT_EXPECT]	= "ct expectation",
>  };
>  
> diff --git a/tests/py/ip/objects.t b/tests/py/ip/objects.t
> index 35d0110..4f4f909 100644
> --- a/tests/py/ip/objects.t
> +++ b/tests/py/ip/objects.t
> @@ -50,3 +50,7 @@ ct timeout set "cttime1";ok
>  %ctexpect5 type ct expectation { protocol udp; dport 9876; timeout 2m; size 12; l3proto ip; };ok
>  
>  ct expectation set "ctexpect1";ok
> +
> +# synproxy
> +%synproxy1 type synproxy mss 1460 wscale 7;ok
> +%synproxy2 type synproxy mss 1460 wscale 7 timestamp sack-perm;ok
>
diff mbox series

Patch

diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 0ff932d..ed8881a 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1481,7 +1481,8 @@  enum nft_ct_expectation_attributes {
 #define NFT_OBJECT_CT_TIMEOUT	7
 #define NFT_OBJECT_SECMARK	8
 #define NFT_OBJECT_CT_EXPECT	9
-#define __NFT_OBJECT_MAX	10
+#define NFT_OBJECT_SYNPROXY	10
+#define __NFT_OBJECT_MAX	11
 #define NFT_OBJECT_MAX		(__NFT_OBJECT_MAX - 1)
 
 /**
diff --git a/include/rule.h b/include/rule.h
index 0ef6aac..2708cbe 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -399,6 +399,12 @@  struct limit {
 	uint32_t	flags;
 };
 
+struct synproxy {
+	uint16_t	mss;
+	uint8_t		wscale;
+	uint32_t	flags;
+};
+
 struct secmark {
 	char		ctx[NFT_SECMARK_CTX_MAXLEN];
 };
@@ -426,6 +432,7 @@  struct obj {
 		struct ct_timeout	ct_timeout;
 		struct secmark		secmark;
 		struct ct_expect	ct_expect;
+		struct synproxy		synproxy;
 	};
 };
 
@@ -529,6 +536,8 @@  enum cmd_ops {
  * @CMD_OBJ_FLOWTABLES:	flow tables
  * @CMD_OBJ_SECMARK:	secmark
  * @CMD_OBJ_SECMARKS:	multiple secmarks
+ * @CMD_OBJ_SYNPROXY:	synproxy
+ * @CMD_OBJ_SYNPROXYS:	multiple synproxys
  */
 enum cmd_obj {
 	CMD_OBJ_INVALID,
@@ -561,6 +570,8 @@  enum cmd_obj {
 	CMD_OBJ_SECMARK,
 	CMD_OBJ_SECMARKS,
 	CMD_OBJ_CT_EXPECT,
+	CMD_OBJ_SYNPROXY,
+	CMD_OBJ_SYNPROXYS,
 };
 
 struct markup {
diff --git a/src/evaluate.c b/src/evaluate.c
index 29fe966..a56cd2a 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3743,6 +3743,7 @@  static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_CT_TIMEOUT:
 	case CMD_OBJ_SECMARK:
 	case CMD_OBJ_CT_EXPECT:
+	case CMD_OBJ_SYNPROXY:
 		return obj_evaluate(ctx, cmd->object);
 	default:
 		BUG("invalid command object type %u\n", cmd->obj);
@@ -3766,6 +3767,7 @@  static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_LIMIT:
 	case CMD_OBJ_SECMARK:
 	case CMD_OBJ_CT_EXPECT:
+	case CMD_OBJ_SYNPROXY:
 		return 0;
 	default:
 		BUG("invalid command object type %u\n", cmd->obj);
@@ -3911,6 +3913,8 @@  static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
 		return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_SECMARK);
 	case CMD_OBJ_CT_EXPECT:
 		return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_EXPECT);
+	case CMD_OBJ_SYNPROXY:
+		return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_SYNPROXY);
 	case CMD_OBJ_COUNTERS:
 	case CMD_OBJ_QUOTAS:
 	case CMD_OBJ_CT_HELPERS:
@@ -3918,6 +3922,7 @@  static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_SETS:
 	case CMD_OBJ_FLOWTABLES:
 	case CMD_OBJ_SECMARKS:
+	case CMD_OBJ_SYNPROXYS:
 		if (cmd->handle.table.name == NULL)
 			return 0;
 		if (table_lookup(&cmd->handle, &ctx->nft->cache) == NULL)
diff --git a/src/json.c b/src/json.c
index 55ce053..6adc801 100644
--- a/src/json.c
+++ b/src/json.c
@@ -282,8 +282,8 @@  static json_t *obj_print_json(const struct obj *obj)
 {
 	const char *rate_unit = NULL, *burst_unit = NULL;
 	const char *type = obj_type_name(obj->type);
+	json_t *root, *tmp, *flags;
 	uint64_t rate, burst;
-	json_t *root, *tmp;
 
 	root = json_pack("{s:s, s:s, s:s, s:I}",
 			"family", family2str(obj->handle.family),
@@ -368,6 +368,24 @@  static json_t *obj_print_json(const struct obj *obj)
 						    json_string(burst_unit));
 		}
 
+		json_object_update(root, tmp);
+		json_decref(tmp);
+		break;
+	case NFT_OBJECT_SYNPROXY:
+		flags = json_array();
+		tmp = json_pack("{s:i, s:i}",
+				"mss", obj->synproxy.mss,
+				"wscale", obj->synproxy.wscale);
+		if (obj->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP)
+			json_array_append_new(flags, json_string("timestamp"));
+		if (obj->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM)
+			json_array_append_new(flags, json_string("sack-perm"));
+
+		if (json_array_size(flags) > 0)
+			json_object_set_new(tmp, "flags", flags);
+		else
+			json_decref(flags);
+
 		json_object_update(root, tmp);
 		json_decref(tmp);
 		break;
diff --git a/src/mnl.c b/src/mnl.c
index 8031bd6..57ff89f 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1036,6 +1036,14 @@  int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd,
 		nftnl_obj_set_str(nlo, NFTNL_OBJ_SECMARK_CTX,
 				  obj->secmark.ctx);
 		break;
+	case NFT_OBJECT_SYNPROXY:
+		nftnl_obj_set_u16(nlo, NFTNL_OBJ_SYNPROXY_MSS,
+				  obj->synproxy.mss);
+		nftnl_obj_set_u8(nlo, NFTNL_OBJ_SYNPROXY_WSCALE,
+				 obj->synproxy.wscale);
+		nftnl_obj_set_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS,
+				  obj->synproxy.flags);
+		break;
 	default:
 		BUG("Unknown type %d\n", obj->type);
 		break;
diff --git a/src/netlink.c b/src/netlink.c
index f8e1120..1e669e5 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1030,6 +1030,14 @@  struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
 		obj->ct_expect.size =
 			nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_EXPECT_SIZE);
 		break;
+	case NFT_OBJECT_SYNPROXY:
+		obj->synproxy.mss =
+			nftnl_obj_get_u16(nlo, NFTNL_OBJ_SYNPROXY_MSS);
+		obj->synproxy.wscale =
+			nftnl_obj_get_u8(nlo, NFTNL_OBJ_SYNPROXY_WSCALE);
+		obj->synproxy.flags =
+			nftnl_obj_get_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS);
+		break;
 	}
 	obj->type = type;
 
diff --git a/src/parser_bison.y b/src/parser_bison.y
index b7db1a2..3fccea6 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -151,6 +151,7 @@  int nft_lex(void *, void *, void *);
 	struct counter		*counter;
 	struct quota		*quota;
 	struct secmark		*secmark;
+	struct synproxy		*synproxy;
 	struct ct		*ct;
 	struct limit		*limit;
 	const struct datatype	*datatype;
@@ -461,6 +462,7 @@  int nft_lex(void *, void *, void *);
 %token COUNTERS			"counters"
 %token QUOTAS			"quotas"
 %token LIMITS			"limits"
+%token SYNPROXYS		"synproxys"
 %token HELPERS			"helpers"
 
 %token LOG			"log"
@@ -592,7 +594,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 ct_timeout_block ct_expect_block limit_block secmark_block
+%type <obj>			obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block ct_expect_block limit_block secmark_block synproxy_block
 %destructor { obj_free($$); }	obj_block_alloc
 
 %type <list>			stmt_list
@@ -700,8 +702,8 @@  int nft_lex(void *, void *, void *);
 %type <expr>			and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
 %destructor { expr_free($$); }	and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
 
-%type <obj>			counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj
-%destructor { obj_free($$); }	counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj
+%type <obj>			counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
+%destructor { obj_free($$); }	counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
 
 %type <expr>			relational_expr
 %destructor { expr_free($$); }	relational_expr
@@ -787,6 +789,9 @@  int nft_lex(void *, void *, void *);
 %destructor { xfree($$); }	limit_config
 %type <secmark>			secmark_config
 %destructor { xfree($$); }	secmark_config
+%type <synproxy>		synproxy_config
+%destructor { xfree($$); }	synproxy_config
+%type <val>			synproxy_ts	synproxy_sack
 
 %type <expr>			tcp_hdr_expr
 %destructor { expr_free($$); }	tcp_hdr_expr
@@ -1012,6 +1017,10 @@  add_cmd			:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
 			}
+			|	SYNPROXY	obj_spec	synproxy_obj
+			{
+				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
+			}
 			;
 
 replace_cmd		:	RULE		ruleid_spec	rule
@@ -1105,6 +1114,10 @@  create_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3);
 			}
+			|	SYNPROXY	obj_spec	synproxy_obj
+			{
+				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
+			}
 			;
 
 insert_cmd		:	RULE		rule_position	rule
@@ -1189,6 +1202,14 @@  delete_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
 			}
+			|	SYNPROXY	obj_spec
+			{
+				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+			}
+			|	SYNPROXY	objid_spec
+			{
+				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+			}
 			;
 
 get_cmd			:	ELEMENT		set_spec	set_block_expr
@@ -1273,6 +1294,18 @@  list_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &$2, &@$, NULL);
 			}
+			|	SYNPROXYS	ruleset_spec
+			{
+				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$2, &@$, NULL);
+			}
+			|	SYNPROXYS	TABLE	table_spec
+			{
+				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL);
+			}
+			|	SYNPROXY	obj_spec
+			{
+				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+			}
 			|	RULESET		ruleset_spec
 			{
 				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -1592,6 +1625,17 @@  table_block		:	/* empty */	{ $$ = $<table>-1; }
 				list_add_tail(&$4->list, &$1->objs);
 				$$ = $1;
 			}
+			|	table_block	SYNPROXY	obj_identifier
+					obj_block_alloc '{'	synproxy_block	'}'
+					stmt_separator
+			{
+				$4->location = @3;
+				$4->type = NFT_OBJECT_SYNPROXY;
+				handle_merge(&$4->handle, &$3);
+				handle_free(&$3);
+				list_add_tail(&$4->list, &$1->objs);
+				$$ = $1;
+			}
 			;
 
 chain_block_alloc	:	/* empty */
@@ -1928,6 +1972,16 @@  secmark_block		:	/* empty */	{ $$ = $<obj>-1; }
 			}
 			;
 
+synproxy_block		:	/* empty */	{ $$ = $<obj>-1; }
+			|	synproxy_block	common_block
+			|	synproxy_block	stmt_separator
+			|	synproxy_block	synproxy_config
+			{
+				$1->synproxy = *$2;
+				$$ = $1;
+			}
+			;
+
 type_identifier		:	STRING	{ $$ = $1; }
 			|	MARK	{ $$ = xstrdup("mark"); }
 			|	DSCP	{ $$ = xstrdup("dscp"); }
@@ -2788,6 +2842,12 @@  synproxy_stmt_alloc	:	SYNPROXY
 			{
 				$$ = synproxy_stmt_alloc(&@$);
 			}
+			|	SYNPROXY	NAME	stmt_expr
+			{
+				$$ = objref_stmt_alloc(&@$);
+				$$->objref.type = NFT_OBJECT_SYNPROXY;
+				$$->objref.expr = $3;
+			}
 			;
 
 synproxy_args		:	synproxy_arg
@@ -2817,6 +2877,64 @@  synproxy_arg		:	MSS	NUM
 			}
 			;
 
+synproxy_config		:	MSS	NUM	WSCALE	NUM	synproxy_ts	synproxy_sack
+			{
+				struct synproxy *synproxy;
+				uint32_t flags = 0;
+
+				synproxy = xzalloc(sizeof(*synproxy));
+				synproxy->mss = $2;
+				flags |= NF_SYNPROXY_OPT_MSS;
+				synproxy->wscale = $4;
+				flags |= NF_SYNPROXY_OPT_WSCALE;
+				if ($5)
+					flags |= $5;
+				if ($6)
+					flags |= $6;
+				synproxy->flags = flags;
+				$$ = synproxy;
+			}
+			|	MSS	NUM	stmt_separator	WSCALE	NUM	stmt_separator	synproxy_ts	synproxy_sack
+			{
+				struct synproxy *synproxy;
+				uint32_t flags = 0;
+
+				synproxy = xzalloc(sizeof(*synproxy));
+				synproxy->mss = $2;
+				flags |= NF_SYNPROXY_OPT_MSS;
+				synproxy->wscale = $5;
+				flags |= NF_SYNPROXY_OPT_WSCALE;
+				if ($7)
+					flags |= $7;
+				if ($8)
+					flags |= $8;
+				synproxy->flags = flags;
+				$$ = synproxy;
+			}
+			;
+
+synproxy_obj		:	synproxy_config
+			{
+				$$ = obj_alloc(&@$);
+				$$->type = NFT_OBJECT_SYNPROXY;
+				$$->synproxy = *$1;
+			}
+			;
+
+synproxy_ts		:	/* empty */	{ $$ = 0; }
+			|	TIMESTAMP
+			{
+				$$ = NF_SYNPROXY_OPT_TIMESTAMP;
+			}
+			;
+
+synproxy_sack		:	/* empty */	{ $$ = 0; }
+			|	SACKPERM
+			{
+				$$ = NF_SYNPROXY_OPT_SACK_PERM;
+			}
+			;
+
 primary_stmt_expr	:	symbol_expr		{ $$ = $1; }
 			|	integer_expr		{ $$ = $1; }
 			|	boolean_expr		{ $$ = $1; }
diff --git a/src/parser_json.c b/src/parser_json.c
index 183d9c9..bcac5e1 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3019,8 +3019,9 @@  static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
 	const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes";
 	uint32_t l3proto = NFPROTO_UNSPEC;
 	struct handle h = { 0 };
+	int inv = 0, flags = 0;
 	struct obj *obj;
-	int inv = 0;
+	json_t *jflags;
 
 	if (json_unpack_err(ctx, root, "{s:s, s:s}",
 			    "family", &family,
@@ -3196,6 +3197,25 @@  static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
 		obj->limit.unit = seconds_from_unit(tmp);
 		obj->limit.flags = inv ? NFT_LIMIT_F_INV : 0;
 		break;
+	case CMD_OBJ_SYNPROXY:
+		obj->type = NFT_OBJECT_SYNPROXY;
+		if (json_unpack_err(ctx, root, "{s:i, s:i}",
+				    "mss", &obj->synproxy.mss,
+				    "wscale", &obj->synproxy.wscale)) {
+			obj_free(obj);
+			return NULL;
+		}
+		obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
+		obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
+		if (!json_unpack(root, "{s:o}", "flags", &jflags)) {
+			flags = json_parse_synproxy_flags(ctx, jflags);
+			if (flags < 0) {
+				obj_free(obj);
+				return NULL;
+			}
+			obj->synproxy.flags |= flags;
+		}
+		break;
 	default:
 		BUG("Invalid CMD '%d'", cmd_obj);
 	}
diff --git a/src/rule.c b/src/rule.c
index 1912513..5bb1c1d 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -32,6 +32,7 @@ 
 #include <linux/netfilter.h>
 #include <linux/netfilter_arp.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/netfilter/nf_synproxy.h>
 #include <net/if.h>
 #include <linux/netfilter_bridge.h>
 
@@ -1451,6 +1452,7 @@  void cmd_free(struct cmd *cmd)
 		case CMD_OBJ_CT_EXPECT:
 		case CMD_OBJ_LIMIT:
 		case CMD_OBJ_SECMARK:
+		case CMD_OBJ_SYNPROXY:
 			obj_free(cmd->object);
 			break;
 		case CMD_OBJ_FLOWTABLE:
@@ -1542,6 +1544,7 @@  static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
 	case CMD_OBJ_CT_EXPECT:
 	case CMD_OBJ_LIMIT:
 	case CMD_OBJ_SECMARK:
+	case CMD_OBJ_SYNPROXY:
 		return mnl_nft_obj_add(ctx, cmd, flags);
 	case CMD_OBJ_FLOWTABLE:
 		return mnl_nft_flowtable_add(ctx, cmd, flags);
@@ -1627,6 +1630,8 @@  static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
 		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_LIMIT);
 	case CMD_OBJ_SECMARK:
 		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SECMARK);
+	case CMD_OBJ_SYNPROXY:
+		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SYNPROXY);
 	case CMD_OBJ_FLOWTABLE:
 		return mnl_nft_flowtable_del(ctx, cmd);
 	default:
@@ -1778,6 +1783,22 @@  static void print_proto_timeout_policy(uint8_t l4, const uint32_t *timeout,
 	nft_print(octx, " }%s", opts->stmt_separator);
 }
 
+static const char *synproxy_sack_to_str(const uint32_t flags)
+{
+        if (flags & NF_SYNPROXY_OPT_SACK_PERM)
+                return "sack-perm";
+
+        return "";
+}
+
+static const char *synproxy_timestamp_to_str(const uint32_t flags)
+{
+        if (flags & NF_SYNPROXY_OPT_TIMESTAMP)
+                return "timestamp";
+
+        return "";
+}
+
 static void obj_print_data(const struct obj *obj,
 			   struct print_fmt_options *opts,
 			   struct output_ctx *octx)
@@ -1911,6 +1932,30 @@  static void obj_print_data(const struct obj *obj,
 		nft_print(octx, "%s", opts->nl);
 		}
 		break;
+	case NFT_OBJECT_SYNPROXY: {
+		uint32_t flags = obj->synproxy.flags;
+		const char *sack_str = synproxy_sack_to_str(flags);
+		const char *ts_str = synproxy_timestamp_to_str(flags);
+
+		nft_print(octx, " %s {", obj->handle.obj.name);
+		if (nft_output_handle(octx))
+			nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+		if (flags & NF_SYNPROXY_OPT_MSS) {
+			nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
+			nft_print(octx, "mss %u", obj->synproxy.mss);
+		}
+		if (flags & NF_SYNPROXY_OPT_WSCALE) {
+			nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
+			nft_print(octx, "wscale %u", obj->synproxy.wscale);
+		}
+		if (flags & (NF_SYNPROXY_OPT_TIMESTAMP | NF_SYNPROXY_OPT_SACK_PERM)) {
+			nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
+			nft_print(octx, "%s %s", ts_str, sack_str);
+		}
+		nft_print(octx, "%s", opts->stmt_separator);
+		}
+		break;
 	default:
 		nft_print(octx, " unknown {%s", opts->nl);
 		break;
@@ -1924,6 +1969,7 @@  static const char * const obj_type_name_array[] = {
 	[NFT_OBJECT_LIMIT]	= "limit",
 	[NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
 	[NFT_OBJECT_SECMARK]	= "secmark",
+	[NFT_OBJECT_SYNPROXY]	= "synproxy",
 	[NFT_OBJECT_CT_EXPECT]	= "ct expectation",
 };
 
@@ -1941,6 +1987,7 @@  static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {
 	[NFT_OBJECT_LIMIT]	= CMD_OBJ_LIMIT,
 	[NFT_OBJECT_CT_TIMEOUT] = CMD_OBJ_CT_TIMEOUT,
 	[NFT_OBJECT_SECMARK]	= CMD_OBJ_SECMARK,
+	[NFT_OBJECT_SYNPROXY]	= CMD_OBJ_SYNPROXY,
 	[NFT_OBJECT_CT_EXPECT]	= CMD_OBJ_CT_EXPECT,
 };
 
@@ -2308,6 +2355,9 @@  static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_SECMARK:
 	case CMD_OBJ_SECMARKS:
 		return do_list_obj(ctx, cmd, NFT_OBJECT_SECMARK);
+	case CMD_OBJ_SYNPROXY:
+	case CMD_OBJ_SYNPROXYS:
+		return do_list_obj(ctx, cmd, NFT_OBJECT_SYNPROXY);
 	case CMD_OBJ_FLOWTABLES:
 		return do_list_flowtables(ctx, cmd);
 	default:
diff --git a/src/scanner.l b/src/scanner.l
index fdf84ba..3de5a9e 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -330,6 +330,7 @@  addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "counters"		{ return COUNTERS; }
 "quotas"		{ return QUOTAS; }
 "limits"		{ return LIMITS; }
+"synproxys"		{ return SYNPROXYS; }
 
 "log"			{ return LOG; }
 "prefix"		{ return PREFIX; }
diff --git a/src/statement.c b/src/statement.c
index 12689ee..5aa5b1e 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -209,6 +209,7 @@  static const char *objref_type[NFT_OBJECT_MAX + 1] = {
 	[NFT_OBJECT_LIMIT]	= "limit",
 	[NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
 	[NFT_OBJECT_SECMARK]	= "secmark",
+	[NFT_OBJECT_SYNPROXY]	= "synproxy",
 	[NFT_OBJECT_CT_EXPECT]	= "ct expectation",
 };
 
diff --git a/tests/py/ip/objects.t b/tests/py/ip/objects.t
index 35d0110..4f4f909 100644
--- a/tests/py/ip/objects.t
+++ b/tests/py/ip/objects.t
@@ -50,3 +50,7 @@  ct timeout set "cttime1";ok
 %ctexpect5 type ct expectation { protocol udp; dport 9876; timeout 2m; size 12; l3proto ip; };ok
 
 ct expectation set "ctexpect1";ok
+
+# synproxy
+%synproxy1 type synproxy mss 1460 wscale 7;ok
+%synproxy2 type synproxy mss 1460 wscale 7 timestamp sack-perm;ok