#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)
/**
uint32_t flags;
};
+struct synproxy {
+ uint16_t mss;
+ uint8_t wscale;
+ uint32_t flags;
+};
+
struct secmark {
char ctx[NFT_SECMARK_CTX_MAXLEN];
};
struct ct_timeout ct_timeout;
struct secmark secmark;
struct ct_expect ct_expect;
+ struct synproxy synproxy;
};
};
* @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,
CMD_OBJ_SECMARK,
CMD_OBJ_SECMARKS,
CMD_OBJ_CT_EXPECT,
+ CMD_OBJ_SYNPROXY,
+ CMD_OBJ_SYNPROXYS,
};
struct markup {
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);
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);
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:
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)
{
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),
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;
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;
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;
struct counter *counter;
struct quota *quota;
struct secmark *secmark;
+ struct synproxy *synproxy;
struct ct *ct;
struct limit *limit;
const struct datatype *datatype;
%token COUNTERS "counters"
%token QUOTAS "quotas"
%token LIMITS "limits"
+%token SYNPROXYS "synproxys"
%token HELPERS "helpers"
%token LOG "log"
%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
%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
%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
{
$$ = 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
{
$$ = 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
{
$$ = 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
{
$$ = 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);
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 */
}
;
+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"); }
{
$$ = synproxy_stmt_alloc(&@$);
}
+ | SYNPROXY NAME stmt_expr
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_SYNPROXY;
+ $$->objref.expr = $3;
+ }
;
synproxy_args : synproxy_arg
}
;
+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; }
static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
- struct stmt *stmt;
+ struct stmt *stmt = NULL;
json_t *jflags;
int tmp, flags;
- stmt = synproxy_stmt_alloc(int_loc);
+ if (json_typeof(value) == JSON_NULL) {
+ stmt = synproxy_stmt_alloc(int_loc);
+ return stmt;
+ }
if (!json_unpack(value, "{s:i}", "mss", &tmp)) {
+ if (!stmt)
+ stmt = synproxy_stmt_alloc(int_loc);
if (tmp < 0) {
json_error(ctx, "Invalid synproxy mss value '%d'", tmp);
stmt_free(stmt);
stmt->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
}
if (!json_unpack(value, "{s:i}", "wscale", &tmp)) {
+ if (!stmt)
+ stmt = synproxy_stmt_alloc(int_loc);
if (tmp < 0) {
json_error(ctx, "Invalid synproxy wscale value '%d'", tmp);
stmt_free(stmt);
stmt->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
}
if (!json_unpack(value, "{s:o}", "flags", &jflags)) {
+ if (!stmt)
+ stmt = synproxy_stmt_alloc(int_loc);
flags = json_parse_synproxy_flags(ctx, jflags);
if (flags < 0) {
}
stmt->synproxy.flags |= flags;
}
+
+ if (!stmt) {
+ stmt = objref_stmt_alloc(int_loc);
+ stmt->objref.type = NFT_OBJECT_SYNPROXY;
+ stmt->objref.expr = json_parse_stmt_expr(ctx, value);
+ if (!stmt->objref.expr) {
+ json_error(ctx, "Invalid synproxy reference");
+ stmt_free(stmt);
+ return NULL;
+ }
+ }
return stmt;
}
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,
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);
}
#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>
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:
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);
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:
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)
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;
[NFT_OBJECT_LIMIT] = "limit",
[NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
[NFT_OBJECT_SECMARK] = "secmark",
+ [NFT_OBJECT_SYNPROXY] = "synproxy",
[NFT_OBJECT_CT_EXPECT] = "ct expectation",
};
[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,
};
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:
"counters" { return COUNTERS; }
"quotas" { return QUOTAS; }
"limits" { return LIMITS; }
+"synproxys" { return SYNPROXYS; }
"log" { return LOG; }
"prefix" { return PREFIX; }
[NFT_OBJECT_LIMIT] = "limit",
[NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
[NFT_OBJECT_SECMARK] = "secmark",
+ [NFT_OBJECT_SYNPROXY] = "synproxy",
[NFT_OBJECT_CT_EXPECT] = "ct expectation",
};
%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
+
+synproxy name tcp dport map {443 : "synproxy1", 80 : "synproxy2"};ok
}
]
+# synproxy name tcp dport map {443 : "synproxy1", 80 : "synproxy2"}
+[
+ {
+ "synproxy": {
+ "map": {
+ "key": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 80,
+ "synproxy2"
+ ],
+ [
+ 443,
+ "synproxy1"
+ ]
+ ]
+ }
+ }
+ }
+ }
+]
# ct expectation set "ctexpect1"
ip test-ip4 output
[ objref type 9 name ctexpect1 ]
+
+# synproxy name tcp dport map {443 : "synproxy1", 80 : "synproxy2"}
+__objmap%d test-ip4 43 size 2
+__objmap%d test-ip4 0
+ element 0000bb01 : 0 [end] element 00005000 : 0 [end]
+ip test-ip4 output
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ objref sreg 1 set __objmap%d ]