@@ -1284,6 +1284,10 @@ enum nft_fib_flags {
NFTA_FIB_F_PRESENT = 1 << 5, /* check existence only */
};
+enum nft_socket_flags {
+ NFTA_SOCKET_TRANSPARENT = (1<<0),
+};
+
enum nft_ct_helper_attributes {
NFTA_CT_HELPER_UNSPEC,
NFTA_CT_HELPER_NAME,
@@ -32,6 +32,13 @@ struct counter_stmt {
extern struct stmt *counter_stmt_alloc(const struct location *loc);
+struct socket_stmt {
+ bool exists;
+ __u8 flags;
+};
+
+extern struct stmt *socket_stmt_alloc(const struct location *loc, bool exists, __u8 flags);
+
struct exthdr_stmt {
struct expr *expr;
struct expr *val;
@@ -248,6 +255,7 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc);
* @STMT_EXTHDR: extension header statement
* @STMT_FLOW_OFFLOAD: flow offload statement
* @STMT_MAP: map statement
+ * @STMT_SOCKET: socket statement
*/
enum stmt_types {
STMT_INVALID,
@@ -273,6 +281,7 @@ enum stmt_types {
STMT_EXTHDR,
STMT_FLOW_OFFLOAD,
STMT_MAP,
+ STMT_SOCKET,
};
/**
@@ -335,6 +344,7 @@ struct stmt {
struct objref_stmt objref;
struct flow_stmt flow;
struct map_stmt map;
+ struct socket_stmt socket;
};
};
@@ -14,7 +14,7 @@ void xt_stmt_release(const struct stmt *stmt);
void netlink_parse_target(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle);
-void netlink_parse_match(struct netlink_parse_ctx *ctx,
+void xt_netlink_parse_match(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle);
void stmt_xt_postprocess(struct rule_pp_ctx *rctx, struct stmt *stmt,
@@ -28,7 +28,7 @@ static inline void xt_stmt_release(const struct stmt *stmt) {}
static inline void netlink_parse_target(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle) {}
-static inline void netlink_parse_match(struct netlink_parse_ctx *ctx,
+static inline void xt_netlink_parse_match(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle) {}
static inline void stmt_xt_postprocess(struct rule_pp_ctx *rctx,
@@ -2686,6 +2686,15 @@ static int stmt_evaluate_objref(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_socket(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ const struct socket_stmt * const s = &stmt->socket;
+
+ if (!s->exists && s->flags)
+ return -1;
+ return 0;
+}
+
int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
{
if (ctx->debug_mask & NFT_DEBUG_EVALUATION) {
@@ -2737,6 +2746,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_SOCKET:
+ return stmt_evaluate_socket(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -27,6 +27,7 @@
#include <sys/socket.h>
#include <libnftnl/udata.h>
#include <xt.h>
+#include <linux/netfilter/xt_socket.h>
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx);
@@ -1278,6 +1279,24 @@ static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
+static void netlink_parse_match(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ if (!strcmp(nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME), "socket") &&
+ nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV) == 3) {
+ const struct xt_socket_mtinfo3 *info;
+ uint32_t len = 0;
+
+ info = nftnl_expr_get(nle, NFTNL_EXPR_MT_INFO, &len);
+ if(!info)
+ return;
+ ctx->stmt = socket_stmt_alloc(loc, true, info->flags); // true is placeholder
+ } else {
+ xt_netlink_parse_match(ctx, loc, nle);
+ }
+}
+
static const struct {
const char *name;
void (*parse)(struct netlink_parse_ctx *ctx,
@@ -19,6 +19,7 @@
#include <gmputil.h>
#include <utils.h>
#include <netinet/in.h>
+#include <linux/netfilter/xt_socket.h>
#include <linux/netfilter.h>
#include <libnftnl/udata.h>
@@ -1155,6 +1156,24 @@ static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx,
nftnl_rule_add_expr(ctx->nlr, nle);
}
+static void netlink_gen_socket_match_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct nftnl_expr *nle = alloc_nft_expr("match");
+ struct xt_socket_mtinfo3 *info;
+
+ nftnl_expr_set_str(nle, NFTNL_EXPR_MT_NAME, "socket");
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_MT_REV, 3);
+
+ info = xzalloc(sizeof(struct xt_socket_mtinfo3));
+ info->flags = stmt->socket.flags;
+
+ nftnl_expr_set(nle, NFTNL_EXPR_MT_INFO, info, sizeof(struct xt_socket_mtinfo3));
+
+ nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
+
static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
@@ -1283,6 +1302,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_objref_stmt(ctx, stmt);
case STMT_MAP:
return netlink_gen_map_stmt(ctx, stmt);
+ case STMT_SOCKET:
+ return netlink_gen_socket_match_stmt(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -189,6 +189,9 @@ int nft_lex(void *, void *, void *);
%token FIB "fib"
+%token SOCKET "socket"
+%token TRANSPARENT "transparent"
+
%token HOOK "hook"
%token DEVICE "device"
%token DEVICES "devices"
@@ -547,8 +550,11 @@ int nft_lex(void *, void *, void *);
%type <list> stmt_list
%destructor { stmt_list_free($$); xfree($$); } stmt_list
-%type <stmt> stmt match_stmt verdict_stmt
-%destructor { stmt_free($$); } stmt match_stmt verdict_stmt
+%type <stmt> stmt match_stmt verdict_stmt socket_stmt
+%destructor { stmt_free($$); } stmt match_stmt verdict_stmt socket_stmt
+
+%type <val> socket_stmt_flag socket_stmt_flags
+
%type <stmt> counter_stmt counter_stmt_alloc
%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc
%type <stmt> payload_stmt
@@ -2078,6 +2084,27 @@ stmt : verdict_stmt
| fwd_stmt
| set_stmt
| map_stmt
+ | socket_stmt
+ ;
+
+socket_stmt_flag : TRANSPARENT { $$ = NFTA_SOCKET_TRANSPARENT; }
+ ;
+
+socket_stmt_flags : socket_stmt_flags COMMA socket_stmt_flag
+ {
+ $$ = $1 | $3;
+ }
+ | socket_stmt_flag
+ ;
+
+socket_stmt : SOCKET EXISTS /* with the actual implementation we cannot match abscence */
+ {
+ $$ = socket_stmt_alloc(&@$, true, 0);
+ }
+ | SOCKET FLAGS socket_stmt_flags /* we suppose existance criterion in this case */
+ {
+ $$ = socket_stmt_alloc(&@$, true, $3);
+ }
;
verdict_stmt : verdict_expr
@@ -258,6 +258,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"ruleset" { return RULESET; }
"trace" { return TRACE; }
+"socket" { return SOCKET; }
+"transparent" { return TRANSPARENT;}
+
"accept" { return ACCEPT; }
"drop" { return DROP; }
"continue" { return CONTINUE; }
@@ -176,6 +176,40 @@ struct stmt *counter_stmt_alloc(const struct location *loc)
return stmt;
}
+static void socket_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ const struct socket_stmt *s = &stmt->socket;
+ const char *transp_str = "transparent",
+ *existance_str = (s->exists) ? "exists" : "missing";
+
+ nft_print(octx, "socket");
+ if (s->flags) {
+ __u8 f = s->flags;
+
+ nft_print(octx, " flags ");
+ if(f & NFTA_SOCKET_TRANSPARENT)
+ nft_print(octx, "%s", transp_str);
+ } else {
+ nft_print(octx, " %s", existance_str);
+ }
+ // (!s->exists && s->flags) is impossible, see stmt_evaluate_socket
+}
+
+static const struct stmt_ops socket_stmt_ops = {
+ .type = STMT_SOCKET,
+ .name = "socket",
+ .print = socket_stmt_print,
+};
+
+extern struct stmt *socket_stmt_alloc(const struct location *loc, bool exists, __u8 flags)
+{
+ struct stmt *stmt = stmt_alloc(loc, &socket_stmt_ops);
+
+ stmt->socket.exists = exists;
+ stmt->socket.flags = flags;
+ return stmt;
+}
+
static const char *objref_type[NFT_OBJECT_MAX + 1] = {
[NFT_OBJECT_COUNTER] = "counter",
[NFT_OBJECT_QUOTA] = "quota",
@@ -188,7 +188,7 @@ static struct xtables_match *xt_match_clone(struct xtables_match *m)
* Delinearization
*/
-void netlink_parse_match(struct netlink_parse_ctx *ctx,
+void xt_netlink_parse_match(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
Socket matching is achieved using the nft_compat interface. The list of known limitations of the current implementation are: * The absence of a corresponding socket cannot be matched (`socket missing`). * Only transparent socket flag can be matched, nowildcard is not a flag, it should be matched with a different expression if desired. Other options that can be set with `setsockopt` are unavailable. * Such a rule cannot be added to an `inet` table. In the long term native implementation might be worth it. Example: table ip stable { chain tchain { type filter hook prerouting priority -150; policy accept; socket flags transparent counter packets 12 bytes 608 mark set 0x00000001 accept socket exists counter packets 52 bytes 3316 } } table ip6 stable { chain tchain { type filter hook prerouting priority -150; policy accept; socket flags transparent counter packets 0 bytes 0 mark set 0x00000001 accept socket exists counter packets 0 bytes 0 } } Signed-off-by: Máté Eckl <ecklm94@gmail.com> --- include/linux/netfilter/nf_tables.h | 4 ++++ include/statement.h | 10 +++++++++ include/xt.h | 4 ++-- src/evaluate.c | 11 ++++++++++ src/netlink_delinearize.c | 19 ++++++++++++++++ src/netlink_linearize.c | 21 ++++++++++++++++++ src/parser_bison.y | 31 ++++++++++++++++++++++++-- src/scanner.l | 3 +++ src/statement.c | 34 +++++++++++++++++++++++++++++ src/xt.c | 2 +- 10 files changed, 134 insertions(+), 5 deletions(-)