]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
ct: support for NFT_CT_{SRC,DST}_{IP,IP6}
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 21 Jun 2019 08:28:37 +0000 (10:28 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 21 Jun 2019 16:49:07 +0000 (18:49 +0200)
These keys are available since kernel >= 4.17.

You can still use NFT_CT_{SRC,DST}, however, you need to specify 'meta
protocol' in first place to provide layer 3 context.

Note that NFT_CT_{SRC,DST} are broken with set, maps and concatenations.
This patch is implicitly fixing these cases.

If your kernel is < 4.17, you can still use address matching via
explicit meta nfproto:

meta nfproto ipv4 ct original saddr 1.2.3.4

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
13 files changed:
include/ct.h
src/ct.c
src/evaluate.c
src/json.c
src/netlink_delinearize.c
src/parser_bison.y
src/parser_json.c
tests/py/any/ct.t
tests/py/inet/ct.t.json
tests/py/inet/ct.t.json.output
tests/py/inet/ct.t.payload
tests/py/ip/ct.t.json
tests/py/ip/ct.t.payload

index 4c5bd804dabfc2e47ccc0050f11b0b697c947bf2..063f8cdf4aa4618c657842727813c1bbc0f5d7ee 100644 (file)
@@ -26,8 +26,7 @@ extern const struct ct_template ct_templates[__NFT_CT_MAX];
 }
 
 extern struct expr *ct_expr_alloc(const struct location *loc,
-                                 enum nft_ct_keys key, int8_t direction,
-                                 uint8_t nfproto);
+                                 enum nft_ct_keys key, int8_t direction);
 extern void ct_expr_update_type(struct proto_ctx *ctx, struct expr *expr);
 
 extern struct stmt *notrack_stmt_alloc(const struct location *loc);
index 72346cd54338cd5fe055ab26b76b1b7eec35decb..4f7807deea0f6f90cf205a4e92cba8426700d42d 100644 (file)
--- a/src/ct.c
+++ b/src/ct.c
@@ -291,6 +291,14 @@ const struct ct_template ct_templates[__NFT_CT_MAX] = {
                                              BYTEORDER_HOST_ENDIAN, 16),
        [NFT_CT_EVENTMASK]      = CT_TEMPLATE("event", &ct_event_type,
                                              BYTEORDER_HOST_ENDIAN, 32),
+       [NFT_CT_SRC_IP]         = CT_TEMPLATE("ip saddr", &ipaddr_type,
+                                             BYTEORDER_BIG_ENDIAN, 0),
+       [NFT_CT_DST_IP]         = CT_TEMPLATE("ip daddr", &ipaddr_type,
+                                             BYTEORDER_BIG_ENDIAN, 0),
+       [NFT_CT_SRC_IP6]        = CT_TEMPLATE("ip6 saddr", &ip6addr_type,
+                                             BYTEORDER_BIG_ENDIAN, 0),
+       [NFT_CT_DST_IP6]        = CT_TEMPLATE("ip6 daddr", &ip6addr_type,
+                                             BYTEORDER_BIG_ENDIAN, 0),
 };
 
 static void ct_print(enum nft_ct_keys key, int8_t dir, uint8_t nfproto,
@@ -368,7 +376,7 @@ const struct expr_ops ct_expr_ops = {
 };
 
 struct expr *ct_expr_alloc(const struct location *loc, enum nft_ct_keys key,
-                          int8_t direction, uint8_t nfproto)
+                          int8_t direction)
 {
        const struct ct_template *tmpl = &ct_templates[key];
        struct expr *expr;
@@ -377,7 +385,6 @@ struct expr *ct_expr_alloc(const struct location *loc, enum nft_ct_keys key,
                          tmpl->byteorder, tmpl->len);
        expr->ct.key = key;
        expr->ct.direction = direction;
-       expr->ct.nfproto = nfproto;
 
        switch (key) {
        case NFT_CT_SRC:
@@ -428,6 +435,16 @@ void ct_expr_update_type(struct proto_ctx *ctx, struct expr *expr)
                        break;
                datatype_set(expr, &inet_service_type);
                break;
+       case NFT_CT_SRC_IP:
+       case NFT_CT_DST_IP:
+               expr->dtype = &ipaddr_type;
+               expr->len = expr->dtype->size;
+               break;
+       case NFT_CT_SRC_IP6:
+       case NFT_CT_DST_IP6:
+               expr->dtype = &ip6addr_type;
+               expr->len = expr->dtype->size;
+               break;
        default:
                break;
        }
index dfdd3c242530fdb7b06aaf81cc68b3492f89d06b..19c2d4c6356a133bebb6d7c5c1480c822f922c65 100644 (file)
@@ -782,7 +782,7 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
                return 0;
        }
 
-       left = ct_expr_alloc(&ct->location, NFT_CT_L3PROTOCOL, ct->ct.direction, ct->ct.nfproto);
+       left = ct_expr_alloc(&ct->location, NFT_CT_L3PROTOCOL, ct->ct.direction);
 
        right = constant_expr_alloc(&ct->location, left->dtype,
                                    left->dtype->byteorder, left->len,
@@ -803,13 +803,30 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
  */
 static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr)
 {
+       const struct proto_desc *base, *error;
        struct expr *ct = *expr;
 
+       base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+
        switch (ct->ct.key) {
        case NFT_CT_SRC:
        case NFT_CT_DST:
                ct_gen_nh_dependency(ctx, ct);
                break;
+       case NFT_CT_SRC_IP:
+       case NFT_CT_DST_IP:
+               if (base == &proto_ip6) {
+                       error = &proto_ip;
+                       goto err_conflict;
+               }
+               break;
+       case NFT_CT_SRC_IP6:
+       case NFT_CT_DST_IP6:
+               if (base == &proto_ip) {
+                       error = &proto_ip6;
+                       goto err_conflict;
+               }
+               break;
        default:
                break;
        }
@@ -817,6 +834,12 @@ static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr)
        ct_expr_update_type(&ctx->pctx, ct);
 
        return expr_evaluate_primary(ctx, expr);
+
+err_conflict:
+       return stmt_binary_error(ctx, ct,
+                                &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+                                "conflicting protocols specified: %s vs. %s",
+                                base->name, error->name);
 }
 
 /*
index e0127c5741a0627b12cf2d906232ea2d60db99e4..4e6468420163874f56aa3592cf4bbcf8b73586c4 100644 (file)
@@ -485,7 +485,6 @@ json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx)
 {
        const char *dirstr = ct_dir2str(expr->ct.direction);
        enum nft_ct_keys key = expr->ct.key;
-       const struct proto_desc *desc;
        json_t *root;
 
        root = json_pack("{s:s}", "key", ct_templates[key].token);
@@ -495,18 +494,6 @@ json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx)
 
        if (dirstr)
                json_object_set_new(root, "dir", json_string(dirstr));
-
-       switch (key) {
-       case NFT_CT_SRC:
-       case NFT_CT_DST:
-               desc = proto_find_upper(&proto_inet, expr->ct.nfproto);
-               if (desc)
-                       json_object_set_new(root, "family",
-                                           json_string(desc->name));
-               break;
-       default:
-               break;
-       }
 out:
        return json_pack("{s:o}", "ct", root);
 }
index a4044f0c7329dd2b10f4372a04d9ac46148fc47c..1dd3ffd107b2b67d3f9d527bef2e8deda2f9f5d7 100644 (file)
@@ -788,7 +788,7 @@ static void netlink_parse_ct_expr(struct netlink_parse_ctx *ctx,
                dir = nftnl_expr_get_u8(nle, NFTNL_EXPR_CT_DIR);
 
        key  = nftnl_expr_get_u32(nle, NFTNL_EXPR_CT_KEY);
-       expr = ct_expr_alloc(loc, key, dir, NFPROTO_UNSPEC);
+       expr = ct_expr_alloc(loc, key, dir);
 
        dreg = netlink_parse_register(nle, NFTNL_EXPR_CT_DREG);
        netlink_set_register(ctx, dreg, expr);
index 1c0b60cf40fd9ba42c850da38e69b618676cf84b..670e91f544c7584ab4ed0493def1a2d468b55ca8 100644 (file)
@@ -4060,15 +4060,15 @@ rt_key                  :       CLASSID         { $$ = NFT_RT_CLASSID; }
 
 ct_expr                        :       CT      ct_key
                        {
-                               $$ = ct_expr_alloc(&@$, $2, -1, NFPROTO_UNSPEC);
+                               $$ = ct_expr_alloc(&@$, $2, -1);
                        }
                        |       CT      ct_dir  ct_key_dir
                        {
-                               $$ = ct_expr_alloc(&@$, $3, $2, NFPROTO_UNSPEC);
+                               $$ = ct_expr_alloc(&@$, $3, $2);
                        }
-                       |       CT      ct_dir  nf_key_proto ct_key_proto_field
+                       |       CT      ct_dir  ct_key_proto_field
                        {
-                               $$ = ct_expr_alloc(&@$, $4, $2, $3);
+                               $$ = ct_expr_alloc(&@$, $3, $2);
                        }
                        ;
 
@@ -4102,8 +4102,10 @@ ct_key_dir               :       SADDR           { $$ = NFT_CT_SRC; }
                        |       ct_key_dir_optional
                        ;
 
-ct_key_proto_field     :       SADDR           { $$ = NFT_CT_SRC; }
-                       |       DADDR           { $$ = NFT_CT_DST; }
+ct_key_proto_field     :       IP      SADDR   { $$ = NFT_CT_SRC_IP; }
+                       |       IP      DADDR   { $$ = NFT_CT_DST_IP; }
+                       |       IP6     SADDR   { $$ = NFT_CT_SRC_IP6; }
+                       |       IP6     DADDR   { $$ = NFT_CT_DST_IP6; }
                        ;
 
 ct_key_dir_optional    :       BYTES           { $$ = NFT_CT_BYTES; }
index af7701fcc240852b7224d3b48760907a19918ad2..30b171736a8fb358815444d9c744c6bb5422a8a3 100644 (file)
@@ -714,6 +714,10 @@ static bool ct_key_is_dir(enum nft_ct_keys key)
                NFT_CT_BYTES,
                NFT_CT_AVGPKT,
                NFT_CT_ZONE,
+               NFT_CT_SRC_IP,
+               NFT_CT_DST_IP,
+               NFT_CT_SRC_IP6,
+               NFT_CT_DST_IP6,
        };
        unsigned int i;
 
@@ -727,9 +731,9 @@ static bool ct_key_is_dir(enum nft_ct_keys key)
 static struct expr *json_parse_ct_expr(struct json_ctx *ctx,
                                       const char *type, json_t *root)
 {
+       int dirval = -1, keyval = -1;
        const char *key, *dir;
        unsigned int i;
-       int dirval = -1, familyval, keyval = -1;
 
        if (json_unpack_err(ctx, root, "{s:s}", "key", &key))
                return NULL;
@@ -746,10 +750,6 @@ static struct expr *json_parse_ct_expr(struct json_ctx *ctx,
                return NULL;
        }
 
-       familyval = json_parse_family(ctx, root);
-       if (familyval < 0)
-               return NULL;
-
        if (!json_unpack(root, "{s:s}", "dir", &dir)) {
                if (!strcmp(dir, "original")) {
                        dirval = IP_CT_DIR_ORIGINAL;
@@ -766,7 +766,7 @@ static struct expr *json_parse_ct_expr(struct json_ctx *ctx,
                }
        }
 
-       return ct_expr_alloc(int_loc, keyval, dirval, familyval);
+       return ct_expr_alloc(int_loc, keyval, dirval);
 }
 
 static struct expr *json_parse_numgen_expr(struct json_ctx *ctx,
index 81d937d97ac9c27b16129cc27c96e29169a330f2..267eca1a60ead4c3802d737de4f3b1d8d4f183a4 100644 (file)
@@ -97,10 +97,10 @@ ct both bytes gt 1;fail
 ct bytes original reply;fail
 
 # missing direction
-ct saddr 1.2.3.4;fail
+ct ip saddr 1.2.3.4;fail
 
 # wrong base (ip6 but ipv4 address given)
-meta nfproto ipv6 ct original saddr 1.2.3.4;fail
+meta nfproto ipv6 ct original ip saddr 1.2.3.4;fail
 
 # direction, but must be used without
 ct original mark 42;fail
index 02bb2d271577d9fe7b7be38a619f1e9dbaf73b2b..d0c26aef933306489a9deffa7b60aae77986a176 100644 (file)
@@ -30,8 +30,7 @@
             "left": {
                 "ct": {
                     "dir": "original",
-                    "family": "ip6",
-                    "key": "saddr"
+                    "key": "ip6 saddr"
                 }
             },
            "op": "==",
index 8b71519e9be73ab773e7267b5f966de121404ba1..74c436a3a79e6c93031512ee8ba686cfd2e5418d 100644 (file)
@@ -5,7 +5,6 @@
             "left": {
                 "ct": {
                     "dir": "original",
-                    "family": "ip",
                     "key": "saddr"
                 }
             },
index 97128eccf540fc0a1c0f3049443de89a97d9bb91..83146869e56ccb1701357b46bb1b9b37ab094cff 100644 (file)
@@ -7,7 +7,6 @@ ip test-ip4 output
 
 # ct original ip6 saddr ::1
 inet test-inet input
-  [ ct load l3protocol => reg 1 , dir original ]
-  [ cmp eq reg 1 0x0000000a ]
-  [ ct load src => reg 1 , dir original ]
+  [ ct load src_ip6 => reg 1 , dir original ]
   [ cmp eq reg 1 0x00000000 0x00000000 0x00000000 0x01000000 ]
+
index cc3ab69270cb2458a74cffbb4d81d27ac1f07c99..881cd4c942c1b66ba688e22db4626750d18a93f0 100644 (file)
@@ -5,8 +5,7 @@
             "left": {
                 "ct": {
                     "dir": "original",
-                    "family": "ip",
-                    "key": "saddr"
+                    "key": "ip saddr"
                 }
             },
            "op": "==",
@@ -22,8 +21,7 @@
             "left": {
                 "ct": {
                     "dir": "reply",
-                    "family": "ip",
-                    "key": "saddr"
+                    "key": "ip saddr"
                 }
             },
            "op": "==",
@@ -39,8 +37,7 @@
             "left": {
                 "ct": {
                     "dir": "original",
-                    "family": "ip",
-                    "key": "daddr"
+                    "key": "ip daddr"
                 }
             },
            "op": "==",
@@ -56,8 +53,7 @@
             "left": {
                 "ct": {
                     "dir": "reply",
-                    "family": "ip",
-                    "key": "daddr"
+                    "key": "ip daddr"
                 }
             },
            "op": "==",
@@ -73,8 +69,7 @@
             "left": {
                 "ct": {
                     "dir": "original",
-                    "family": "ip",
-                    "key": "saddr"
+                    "key": "ip saddr"
                 }
             },
            "op": "==",
@@ -95,8 +90,7 @@
             "left": {
                 "ct": {
                     "dir": "reply",
-                    "family": "ip",
-                    "key": "saddr"
+                    "key": "ip saddr"
                 }
             },
            "op": "==",
             "left": {
                 "ct": {
                     "dir": "original",
-                    "family": "ip",
-                    "key": "daddr"
+                    "key": "ip daddr"
                 }
             },
            "op": "==",
             "left": {
                 "ct": {
                     "dir": "reply",
-                    "family": "ip",
-                    "key": "daddr"
+                    "key": "ip daddr"
                 }
             },
            "op": "==",
index b7cd130dbea2534108281af190add2d0218ca63b..d5faed4c667c526592b16c33177b6b9e2c63cb04 100644 (file)
@@ -1,44 +1,44 @@
 # ct original ip saddr 192.168.0.1
 ip test-ip4 output
-  [ ct load src => reg 1 , dir original ]
+  [ ct load src_ip => reg 1 , dir original ]
   [ cmp eq reg 1 0x0100a8c0 ]
 
 # ct reply ip saddr 192.168.0.1
 ip test-ip4 output
-  [ ct load src => reg 1 , dir reply ]
+  [ ct load src_ip => reg 1 , dir reply ]
   [ cmp eq reg 1 0x0100a8c0 ]
 
 # ct original ip daddr 192.168.0.1
 ip test-ip4 output
-  [ ct load dst => reg 1 , dir original ]
+  [ ct load dst_ip => reg 1 , dir original ]
   [ cmp eq reg 1 0x0100a8c0 ]
 
 # ct reply ip daddr 192.168.0.1
 ip test-ip4 output
-  [ ct load dst => reg 1 , dir reply ]
+  [ ct load dst_ip => reg 1 , dir reply ]
   [ cmp eq reg 1 0x0100a8c0 ]
 
 # ct original ip saddr 192.168.1.0/24
 ip test-ip4 output
-  [ ct load src => reg 1 , dir original ]
+  [ ct load src_ip => reg 1 , dir original ]
   [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x0001a8c0 ]
 
 # ct reply ip saddr 192.168.1.0/24
 ip test-ip4 output
-  [ ct load src => reg 1 , dir reply ]
+  [ ct load src_ip => reg 1 , dir reply ]
   [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x0001a8c0 ]
 
 # ct original ip daddr 192.168.1.0/24
 ip test-ip4 output
-  [ ct load dst => reg 1 , dir original ]
+  [ ct load dst_ip => reg 1 , dir original ]
   [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x0001a8c0 ]
 
 # ct reply ip daddr 192.168.1.0/24
 ip test-ip4 output
-  [ ct load dst => reg 1 , dir reply ]
+  [ ct load dst_ip => reg 1 , dir reply ]
   [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x0001a8c0 ]