summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFernando Fernandez Mancera <ffmancera@riseup.net>2019-06-22 19:12:08 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2019-07-17 10:22:39 +0200
commit1188a69604c3df2a63daca9e735fdb535e8f6b63 (patch)
tree21887d78f5b30023546f40da96aaa4cf256a59e5
parent82cc676289803cfd1e69dfeaa4658d57a1547a86 (diff)
src: introduce SYNPROXY matching
Add support for "synproxy" statement. For example (for TCP port 8888): table ip x { chain y { type filter hook prerouting priority raw; policy accept; tcp dport 8888 tcp flags syn notrack } chain z { type filter hook input priority filter; policy accept; tcp dport 8888 ct state invalid,untracked synproxy mss 1460 wscale 7 timestamp sack-perm ct state invalid drop } } Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--doc/statements.txt87
-rw-r--r--include/json.h1
-rw-r--r--include/linux/netfilter/nf_synproxy.h23
-rw-r--r--include/linux/netfilter/nf_tables.h17
-rw-r--r--include/statement.h11
-rw-r--r--src/evaluate.c15
-rw-r--r--src/json.c29
-rw-r--r--src/netlink_delinearize.c17
-rw-r--r--src/netlink_linearize.c17
-rw-r--r--src/parser_bison.y47
-rw-r--r--src/parser_json.c94
-rw-r--r--src/scanner.l5
-rw-r--r--src/statement.c51
-rw-r--r--tests/py/inet/synproxy.t13
-rw-r--r--tests/py/inet/synproxy.t.json71
-rw-r--r--tests/py/inet/synproxy.t.payload72
16 files changed, 570 insertions, 0 deletions
diff --git a/doc/statements.txt b/doc/statements.txt
index bc2f9449..e17068a8 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -483,6 +483,93 @@ table inet x {
}
-------------------------------------
+SYNPROXY STATEMENT
+~~~~~~~~~~~~~~~~~~
+This statement will process TCP three-way-handshake parallel in netfilter
+context to protect either local or backend system. This statement requires
+connection tracking because sequence numbers need to be translated.
+
+[verse]
+*synproxy* [*mss* 'mss_value'] [*wscale* 'wscale_value'] ['SYNPROXY_FLAGS']
+
+.synproxy statement attributes
+[options="header"]
+|=================
+| Name | Description
+| mss | Maximum segment size announced to clients. This must match the backend.
+| wscale | Window scale announced to clients. This must match the backend.
+|=================
+
+.synproxy statement flags
+[options="header"]
+|=================
+| Flag | Description
+| sack-perm |
+Pass client selective acknowledgement option to backend (will be disabled if
+not present).
+| timestamp |
+Pass client timestamp option to backend (will be disabled if not present, also
+needed for selective acknowledgement and window scaling).
+|=================
+
+.Example ruleset for synproxy statement
+---------------------------------------
+Determine tcp options used by backend, from an external system
+
+ tcpdump -pni eth0 -c 1 'tcp[tcpflags] == (tcp-syn|tcp-ack)'
+ port 80 &
+ telnet 192.0.2.42 80
+ 18:57:24.693307 IP 192.0.2.42.80 > 192.0.2.43.48757:
+ Flags [S.], seq 360414582, ack 788841994, win 14480,
+ options [mss 1460,sackOK,
+ TS val 1409056151 ecr 9690221,
+ nop,wscale 9],
+ length 0
+
+Switch tcp_loose mode off, so conntrack will mark out-of-flow packets as state INVALID.
+
+ echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose
+
+Make SYN packets untracked.
+
+ table ip x {
+ chain y {
+ type filter hook prerouting priority raw; policy accept;
+ tcp flags syn notrack
+ }
+ }
+
+Catch UNTRACKED (SYN packets) and INVALID (3WHS ACK packets) states and send
+them to SYNPROXY. This rule will respond to SYN packets with SYN+ACK
+syncookies, create ESTABLISHED for valid client response (3WHS ACK packets) and
+drop incorrect cookies. Flags combinations not expected during 3WHS will not
+match and continue (e.g. SYN+FIN, SYN+ACK). Finally, drop invalid packets, this
+will be out-of-flow packets that were not matched by SYNPROXY.
+
+ table ip foo {
+ chain z {
+ type filter hook input priority filter; policy accept;
+ ct state { invalid, untracked } synproxy mss 1460 wscale 9 timestamp sack-perm
+ ct state invalid drop
+ }
+ }
+
+The outcome ruleset of the steps above should be similar to the one below.
+
+ table ip x {
+ chain y {
+ type filter hook prerouting priority raw; policy accept;
+ tcp flags syn notrack
+ }
+
+ chain z {
+ type filter hook input priority filter; policy accept;
+ ct state { invalid, untracked } synproxy mss 1460 wscale 9 timestamp sack-perm
+ ct state invalid drop
+ }
+ }
+---------------------------------------
+
FLOW STATEMENT
~~~~~~~~~~~~~~
A flow statement allows us to select what flows you want to accelerate
diff --git a/include/json.h b/include/json.h
index c724c299..ce57c9f7 100644
--- a/include/json.h
+++ b/include/json.h
@@ -83,6 +83,7 @@ json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *connlimit_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd);
diff --git a/include/linux/netfilter/nf_synproxy.h b/include/linux/netfilter/nf_synproxy.h
new file mode 100644
index 00000000..0e7c3919
--- /dev/null
+++ b/include/linux/netfilter/nf_synproxy.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _NF_SYNPROXY_H
+#define _NF_SYNPROXY_H
+
+#include <linux/types.h>
+
+#define NF_SYNPROXY_OPT_MSS 0x01
+#define NF_SYNPROXY_OPT_WSCALE 0x02
+#define NF_SYNPROXY_OPT_SACK_PERM 0x04
+#define NF_SYNPROXY_OPT_TIMESTAMP 0x08
+#define NF_SYNPROXY_OPT_ECN 0x10
+#define NF_SYNPROXY_FLAGMASK (NF_SYNPROXY_OPT_MSS | \
+ NF_SYNPROXY_OPT_WSCALE | \
+ NF_SYNPROXY_OPT_SACK_PERM | \
+ NF_SYNPROXY_OPT_TIMESTAMP)
+
+struct nf_synproxy_info {
+ __u8 options;
+ __u8 wscale;
+ __u16 mss;
+};
+
+#endif /* _NF_SYNPROXY_H */
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 709fbc8d..adc08935 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1544,6 +1544,23 @@ enum nft_osf_attributes {
#define NFTA_OSF_MAX (__NFTA_OSF_MAX - 1)
/**
+ * enum nft_synproxy_attributes - nftables synproxy expression
+ * netlink attributes
+ *
+ * @NFTA_SYNPROXY_MSS: mss value sent to the backend (NLA_U16)
+ * @NFTA_SYNPROXY_WSCALE: wscale value sent to the backend (NLA_U8)
+ * @NFTA_SYNPROXY_FLAGS: flags (NLA_U32)
+ */
+enum nft_synproxy_attributes {
+ NFTA_SYNPROXY_UNSPEC,
+ NFTA_SYNPROXY_MSS,
+ NFTA_SYNPROXY_WSCALE,
+ NFTA_SYNPROXY_FLAGS,
+ __NFTA_SYNPROXY_MAX,
+};
+#define NFTA_SYNPROXY_MAX (__NFTA_SYNPROXY_MAX - 1)
+
+/**
* enum nft_device_attributes - nf_tables device netlink attributes
*
* @NFTA_DEVICE_NAME: name of this device (NLA_STRING)
diff --git a/include/statement.h b/include/statement.h
index 6fb5cf15..585908de 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -204,6 +204,14 @@ struct map_stmt {
extern struct stmt *map_stmt_alloc(const struct location *loc);
+struct synproxy_stmt {
+ uint16_t mss;
+ uint8_t wscale;
+ uint32_t flags;
+};
+
+extern struct stmt *synproxy_stmt_alloc(const struct location *loc);
+
struct meter_stmt {
struct expr *set;
struct expr *key;
@@ -271,6 +279,7 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc);
* @STMT_FLOW_OFFLOAD: flow offload statement
* @STMT_CONNLIMIT: connection limit statement
* @STMT_MAP: map statement
+ * @STMT_SYNPROXY: synproxy statement
*/
enum stmt_types {
STMT_INVALID,
@@ -298,6 +307,7 @@ enum stmt_types {
STMT_FLOW_OFFLOAD,
STMT_CONNLIMIT,
STMT_MAP,
+ STMT_SYNPROXY,
};
/**
@@ -362,6 +372,7 @@ struct stmt {
struct objref_stmt objref;
struct flow_stmt flow;
struct map_stmt map;
+ struct synproxy_stmt synproxy;
};
};
diff --git a/src/evaluate.c b/src/evaluate.c
index 864d3daf..55cd9d00 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -17,6 +17,7 @@
#include <linux/netfilter.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_synproxy.h>
#include <linux/netfilter_ipv4.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
@@ -2746,6 +2747,18 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_synproxy(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ if (stmt->synproxy.flags != 0 &&
+ !(stmt->synproxy.flags & (NF_SYNPROXY_OPT_MSS |
+ NF_SYNPROXY_OPT_WSCALE |
+ NF_SYNPROXY_OPT_TIMESTAMP |
+ NF_SYNPROXY_OPT_SACK_PERM)))
+ return stmt_error(ctx, stmt, "This flags are not supported for SYNPROXY");
+
+ return 0;
+}
+
static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
{
int err;
@@ -3090,6 +3103,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_objref(ctx, stmt);
case STMT_MAP:
return stmt_evaluate_map(ctx, stmt);
+ case STMT_SYNPROXY:
+ return stmt_evaluate_synproxy(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
diff --git a/src/json.c b/src/json.c
index 47543768..96ba557a 100644
--- a/src/json.c
+++ b/src/json.c
@@ -16,6 +16,7 @@
#include <linux/netfilter/nf_log.h>
#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_synproxy.h>
#include <linux/xfrm.h>
#include <pwd.h>
#include <grp.h>
@@ -1466,6 +1467,34 @@ json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return json_pack("{s:o}", "tproxy", root);
}
+json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ json_t *root = json_object(), *flags = json_array();
+
+ if (stmt->synproxy.flags & NF_SYNPROXY_OPT_MSS)
+ json_object_set_new(root, "mss",
+ json_integer(stmt->synproxy.mss));
+ if (stmt->synproxy.flags & NF_SYNPROXY_OPT_WSCALE)
+ json_object_set_new(root, "wscale",
+ json_integer(stmt->synproxy.wscale));
+ if (stmt->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP)
+ json_array_append_new(flags, json_string("timestamp"));
+ if (stmt->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(root, "flags", flags);
+ else
+ json_decref(flags);
+
+ if (!json_object_size(root)) {
+ json_decref(root);
+ root = json_null();
+ }
+
+ return json_pack("{s:o}", "synproxy", root);
+}
+
static json_t *table_print_json_full(struct netlink_ctx *ctx,
struct table *table)
{
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 1dd3ffd1..fc2574b1 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1010,6 +1010,22 @@ out_err:
xfree(stmt);
}
+static void netlink_parse_synproxy(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ struct stmt *stmt;
+
+ stmt = synproxy_stmt_alloc(loc);
+ stmt->synproxy.mss = nftnl_expr_get_u16(nle, NFTNL_EXPR_SYNPROXY_MSS);
+ stmt->synproxy.wscale = nftnl_expr_get_u8(nle,
+ NFTNL_EXPR_SYNPROXY_WSCALE);
+ stmt->synproxy.flags = nftnl_expr_get_u32(nle,
+ NFTNL_EXPR_SYNPROXY_FLAGS);
+
+ ctx->stmt = stmt;
+}
+
static void netlink_parse_tproxy(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -1476,6 +1492,7 @@ static const struct {
{ .name = "tcpopt", .parse = netlink_parse_exthdr },
{ .name = "flow_offload", .parse = netlink_parse_flow_offload },
{ .name = "xfrm", .parse = netlink_parse_xfrm },
+ { .name = "synproxy", .parse = netlink_parse_synproxy },
};
static int netlink_parse_expr(const struct nftnl_expr *nle,
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 2c6aa64d..498326d0 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1141,6 +1141,21 @@ static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx,
nftnl_rule_add_expr(ctx->nlr, nle);
}
+static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("synproxy");
+ nftnl_expr_set_u16(nle, NFTNL_EXPR_SYNPROXY_MSS, stmt->synproxy.mss);
+ nftnl_expr_set_u8(nle, NFTNL_EXPR_SYNPROXY_WSCALE,
+ stmt->synproxy.wscale);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_SYNPROXY_FLAGS,
+ stmt->synproxy.flags);
+
+ nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
@@ -1382,6 +1397,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_nat_stmt(ctx, stmt);
case STMT_TPROXY:
return netlink_gen_tproxy_stmt(ctx, stmt);
+ case STMT_SYNPROXY:
+ return netlink_gen_synproxy_stmt(ctx, stmt);
case STMT_DUP:
return netlink_gen_dup_stmt(ctx, stmt);
case STMT_QUEUE:
diff --git a/src/parser_bison.y b/src/parser_bison.y
index c7591bc2..53e66952 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -23,6 +23,7 @@
#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter/nf_log.h>
#include <linux/netfilter/nfnetlink_osf.h>
+#include <linux/netfilter/nf_synproxy.h>
#include <linux/xfrm.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
@@ -200,6 +201,11 @@ int nft_lex(void *, void *, void *);
%token OSF "osf"
+%token SYNPROXY "synproxy"
+%token MSS "mss"
+%token WSCALE "wscale"
+%token SACKPERM "sack-perm"
+
%token HOOK "hook"
%token DEVICE "device"
%token DEVICES "devices"
@@ -611,6 +617,9 @@ int nft_lex(void *, void *, void *);
%type <val> nf_nat_flags nf_nat_flag offset_opt
%type <stmt> tproxy_stmt
%destructor { stmt_free($$); } tproxy_stmt
+%type <stmt> synproxy_stmt synproxy_stmt_alloc
+%destructor { stmt_free($$); } synproxy_stmt synproxy_stmt_alloc
+
%type <stmt> queue_stmt queue_stmt_alloc
%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc
@@ -2289,6 +2298,7 @@ stmt : verdict_stmt
| fwd_stmt
| set_stmt
| map_stmt
+ | synproxy_stmt
;
verdict_stmt : verdict_expr
@@ -2719,6 +2729,43 @@ tproxy_stmt : TPROXY TO stmt_expr
}
;
+synproxy_stmt : synproxy_stmt_alloc
+ | synproxy_stmt_alloc synproxy_args
+ ;
+
+synproxy_stmt_alloc : SYNPROXY
+ {
+ $$ = synproxy_stmt_alloc(&@$);
+ }
+ ;
+
+synproxy_args : synproxy_arg
+ {
+ $<stmt>$ = $<stmt>0;
+ }
+ | synproxy_args synproxy_arg
+ ;
+
+synproxy_arg : MSS NUM
+ {
+ $<stmt>0->synproxy.mss = $2;
+ $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
+ }
+ | WSCALE NUM
+ {
+ $<stmt>0->synproxy.wscale = $2;
+ $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
+ }
+ | TIMESTAMP
+ {
+ $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_TIMESTAMP;
+ }
+ | SACKPERM
+ {
+ $<stmt>0->synproxy.flags |= 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 79f5865c..42893c2f 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -23,6 +23,7 @@
#include <linux/netfilter/nf_conntrack_tuple_common.h>
#include <linux/netfilter/nf_log.h>
#include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_synproxy.h>
#include <linux/netfilter/nf_tables.h>
#include <jansson.h>
@@ -2180,6 +2181,98 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
return stmt;
}
+static int json_parse_synproxy_flag(struct json_ctx *ctx,
+ json_t *root, int *flags)
+{
+ const struct {
+ const char *flag;
+ int val;
+ } flag_tbl[] = {
+ { "timestamp", NF_SYNPROXY_OPT_TIMESTAMP },
+ { "sack-perm", NF_SYNPROXY_OPT_SACK_PERM },
+ };
+ const char *flag;
+ unsigned int i;
+
+ assert(flags);
+
+ if (!json_is_string(root)) {
+ json_error(ctx, "Invalid log flag type %s, expected string.",
+ json_typename(root));
+ return 1;
+ }
+ flag = json_string_value(root);
+ for (i = 0; i < array_size(flag_tbl); i++) {
+ if (!strcmp(flag, flag_tbl[i].flag)) {
+ *flags |= flag_tbl[i].val;
+ return 0;
+ }
+ }
+ json_error(ctx, "Unknown log flag '%s'.", flag);
+ return 1;
+}
+
+static int json_parse_synproxy_flags(struct json_ctx *ctx, json_t *root)
+{
+ int flags = 0;
+ json_t *value;
+ size_t index;
+
+ if (json_is_string(root)) {
+ json_parse_synproxy_flag(ctx, root, &flags);
+ return flags;
+ } else if (!json_is_array(root)) {
+ json_error(ctx, "Invalid log flags type %s.",
+ json_typename(root));
+ return -1;
+ }
+ json_array_foreach(root, index, value) {
+ if (json_parse_synproxy_flag(ctx, value, &flags))
+ json_error(ctx, "Parsing log flag at index %zu failed.",
+ index);
+ }
+ return flags;
+}
+
+static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+ json_t *jflags;
+ int tmp, flags;
+
+ stmt = synproxy_stmt_alloc(int_loc);
+
+ if (!json_unpack(value, "{s:i}", "mss", &tmp)) {
+ if (tmp < 0) {
+ json_error(ctx, "Invalid synproxy mss value '%d'", tmp);
+ stmt_free(stmt);
+ return NULL;
+ }
+ stmt->synproxy.mss = tmp;
+ stmt->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
+ }
+ if (!json_unpack(value, "{s:i}", "wscale", &tmp)) {
+ if (tmp < 0) {
+ json_error(ctx, "Invalid synproxy wscale value '%d'", tmp);
+ stmt_free(stmt);
+ return NULL;
+ }
+ stmt->synproxy.wscale = tmp;
+ stmt->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
+ }
+ if (!json_unpack(value, "{s:o}", "flags", &jflags)) {
+ flags = json_parse_synproxy_flags(ctx, jflags);
+
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
+ }
+ stmt->synproxy.flags |= flags;
+ }
+ return stmt;
+}
+
static struct stmt *json_parse_cthelper_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
@@ -2375,6 +2468,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "queue", json_parse_queue_stmt },
{ "ct count", json_parse_connlimit_stmt },
{ "tproxy", json_parse_tproxy_stmt },
+ { "synproxy", json_parse_synproxy_stmt },
};
const char *type;
unsigned int i;
diff --git a/src/scanner.l b/src/scanner.l
index 51bf64f4..4ed5f924 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -553,6 +553,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"osf" { return OSF; }
+"synproxy" { return SYNPROXY; }
+"mss" { return MSS; }
+"wscale" { return WSCALE; }
+"sack-perm" { return SACKPERM; }
+
"notrack" { return NOTRACK; }
"options" { return OPTIONS; }
diff --git a/src/statement.c b/src/statement.c
index a82c1b39..a9e72de3 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -29,6 +29,7 @@
#include <netinet/in.h>
#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter/nf_log.h>
+#include <linux/netfilter/nf_synproxy.h>
struct stmt *stmt_alloc(const struct location *loc,
const struct stmt_ops *ops)
@@ -883,3 +884,53 @@ struct stmt *xt_stmt_alloc(const struct location *loc)
{
return stmt_alloc(loc, &xt_stmt_ops);
}
+
+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 synproxy_stmt_print(const struct stmt *stmt,
+ struct output_ctx *octx)
+{
+ uint32_t flags = stmt->synproxy.flags;
+ const char *ts_str = synproxy_timestamp_to_str(flags);
+ const char *sack_str = synproxy_sack_to_str(flags);
+
+ if (flags & (NF_SYNPROXY_OPT_MSS | NF_SYNPROXY_OPT_WSCALE))
+ nft_print(octx, "synproxy mss %u wscale %u%s%s",
+ stmt->synproxy.mss, stmt->synproxy.wscale,
+ ts_str, sack_str);
+ else if (flags & NF_SYNPROXY_OPT_MSS)
+ nft_print(octx, "synproxy mss %u%s%s", stmt->synproxy.mss,
+ ts_str, sack_str);
+ else if (flags & NF_SYNPROXY_OPT_WSCALE)
+ nft_print(octx, "synproxy wscale %u%s%s", stmt->synproxy.wscale,
+ ts_str, sack_str);
+ else
+ nft_print(octx, "synproxy%s%s", ts_str, sack_str);
+
+}
+
+static const struct stmt_ops synproxy_stmt_ops = {
+ .type = STMT_SYNPROXY,
+ .name = "synproxy",
+ .print = synproxy_stmt_print,
+ .json = synproxy_stmt_json,
+};
+
+struct stmt *synproxy_stmt_alloc(const struct location *loc)
+{
+ return stmt_alloc(loc, &synproxy_stmt_ops);
+}
diff --git a/tests/py/inet/synproxy.t b/tests/py/inet/synproxy.t
new file mode 100644
index 00000000..55a05e1e
--- /dev/null
+++ b/tests/py/inet/synproxy.t
@@ -0,0 +1,13 @@
+:synproxychain;type filter hook input priority 0
+
+*ip;synproxyip;synproxychain
+*ip6;synproxyip6;synproxychain
+*inet;synproxyinet;synproxychain
+
+synproxy;ok
+synproxy mss 1460 wscale 7;ok
+synproxy mss 1460 wscale 5 timestamp sack-perm;ok
+synproxy timestamp sack-perm;ok
+synproxy timestamp;ok
+synproxy sack-perm;ok
+
diff --git a/tests/py/inet/synproxy.t.json b/tests/py/inet/synproxy.t.json
new file mode 100644
index 00000000..313fa9fe
--- /dev/null
+++ b/tests/py/inet/synproxy.t.json
@@ -0,0 +1,71 @@
+# synproxy
+[
+ {
+ "synproxy":null
+ }
+]
+
+# synproxy mss 1460
+[
+ {
+ "synproxy": {
+ "mss": 1460
+ }
+ }
+]
+
+# synproxy wscale 7
+[
+ {
+ "synproxy": {
+ "wscale": 7
+ }
+ }
+]
+
+# synproxy mss 1460 wscale 7
+[
+ {
+ "synproxy": {
+ "mss": 1460,
+ "wscale": 7
+ }
+ }
+]
+
+# synproxy timestamp
+[
+ {
+ "synproxy": {
+ "flags": [
+ "timestamp"
+ ]
+ }
+ }
+]
+
+# synproxy timestamp sack-perm
+[
+ {
+ "synproxy": {
+ "flags": [
+ "timestamp",
+ "sack-perm"
+ ]
+ }
+ }
+]
+
+# synproxy mss 1460 wscale 7 timestamp sack-perm
+[
+ {
+ "synproxy": {
+ "mss": 1460,
+ "wscale": 7,
+ "flags": [
+ "timestamp",
+ "sack-perm"
+ ]
+ }
+ }
+]
diff --git a/tests/py/inet/synproxy.t.payload b/tests/py/inet/synproxy.t.payload
new file mode 100644
index 00000000..2e6feaaf
--- /dev/null
+++ b/tests/py/inet/synproxy.t.payload
@@ -0,0 +1,72 @@
+# synproxy
+ip synproxyip synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy
+ip6 synproxyip6 synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy
+inet synproxyinet synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy mss 1460 wscale 7
+ip synproxyip synproxychain
+ [ synproxy mss 1460 wscale 7 ]
+
+# synproxy mss 1460 wscale 7
+ip6 synproxyip6 synproxychain
+ [ synproxy mss 1460 wscale 7 ]
+
+# synproxy mss 1460 wscale 7
+inet synproxyinet synproxychain
+ [ synproxy mss 1460 wscale 7 ]
+
+# synproxy mss 1460 wscale 5 timestamp sack-perm
+ip synproxyip synproxychain
+ [ synproxy mss 1460 wscale 5 ]
+
+# synproxy mss 1460 wscale 5 timestamp sack-perm
+ip6 synproxyip6 synproxychain
+ [ synproxy mss 1460 wscale 5 ]
+
+# synproxy mss 1460 wscale 5 timestamp sack-perm
+inet synproxyinet synproxychain
+ [ synproxy mss 1460 wscale 5 ]
+
+# synproxy timestamp sack-perm
+ip synproxyip synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy timestamp sack-perm
+ip6 synproxyip6 synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy timestamp sack-perm
+inet synproxyinet synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy timestamp
+ip synproxyip synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy timestamp
+ip6 synproxyip6 synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy timestamp
+inet synproxyinet synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy sack-perm
+ip synproxyip synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy sack-perm
+ip6 synproxyip6 synproxychain
+ [ synproxy mss 0 wscale 0 ]
+
+# synproxy sack-perm
+inet synproxyinet synproxychain
+ [ synproxy mss 0 wscale 0 ]
+