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@xxxxxxxxx> --- 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(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 3395faf..31fd6f4 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -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, diff --git a/include/statement.h b/include/statement.h index de26549..84a8f3f 100644 --- a/include/statement.h +++ b/include/statement.h @@ -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; }; }; diff --git a/include/xt.h b/include/xt.h index 753511e..5b29522 100644 --- a/include/xt.h +++ b/include/xt.h @@ -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, diff --git a/src/evaluate.c b/src/evaluate.c index 4eb36e2..5222f4e 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -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); } diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 8f4035a..19c753a 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -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, diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 2ab8acc..5e9345a 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -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); } diff --git a/src/parser_bison.y b/src/parser_bison.y index 0e3ee84..67a5b6f 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -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 diff --git a/src/scanner.l b/src/scanner.l index 6a861cf..416bd27 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -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; } diff --git a/src/statement.c b/src/statement.c index d291001..ff6a98a 100644 --- a/src/statement.c +++ b/src/statement.c @@ -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", diff --git a/src/xt.c b/src/xt.c index 95d0c5f..4f7c235 100644 --- a/src/xt.c +++ b/src/xt.c @@ -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) { -- ecklm -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html