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 <eckl...@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(-)

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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to