]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
json: add inner payload support
authorPablo Neira Ayuso <pablo@netfilter.org>
Tue, 20 Jun 2023 10:57:56 +0000 (12:57 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 20 Jun 2023 16:10:56 +0000 (18:10 +0200)
Add support for vxlan, geneve, gre and gretap.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/json.c
src/parser_json.c
tests/py/inet/geneve.t.json [new file with mode: 0644]
tests/py/inet/gre.t.json [new file with mode: 0644]
tests/py/inet/gretap.t.json [new file with mode: 0644]
tests/py/inet/vxlan.t.json [new file with mode: 0644]

index 305eb6e397febc3433cdb1bead8b2126cc1c4975..21199ca467d606f35b4329072bc5949cb3a63978 100644 (file)
@@ -573,15 +573,23 @@ json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx)
 {
        json_t *root;
 
-       if (payload_is_known(expr))
-               root = json_pack("{s:s, s:s}",
-                                "protocol", expr->payload.desc->name,
-                                "field", expr->payload.tmpl->token);
-       else
+       if (payload_is_known(expr)) {
+               if (expr->payload.inner_desc) {
+                       root = json_pack("{s:s, s:s, s:s}",
+                                        "tunnel", expr->payload.inner_desc->name,
+                                        "protocol", expr->payload.desc->name,
+                                        "field", expr->payload.tmpl->token);
+               } else {
+                       root = json_pack("{s:s, s:s}",
+                                        "protocol", expr->payload.desc->name,
+                                        "field", expr->payload.tmpl->token);
+               }
+       } else {
                root = json_pack("{s:s, s:i, s:i}",
                                 "base", proto_base_tokens[expr->payload.base],
                                 "offset", expr->payload.offset,
                                 "len", expr->len);
+       }
 
        return json_pack("{s:o}", "payload", root);
 }
index 605dcc49f71585d97799c0eca05fca2d741d05dd..91b37b58f2bfeb685f3924299621575bb4293ef0 100644 (file)
@@ -541,6 +541,27 @@ static const struct proto_desc *proto_lookup_byname(const char *name)
                &proto_dccp,
                &proto_sctp,
                &proto_th,
+               &proto_vxlan,
+               &proto_gre,
+               &proto_gretap,
+               &proto_geneve,
+       };
+       unsigned int i;
+
+       for (i = 0; i < array_size(proto_tbl); i++) {
+               if (!strcmp(proto_tbl[i]->name, name))
+                       return proto_tbl[i];
+       }
+       return NULL;
+}
+
+static const struct proto_desc *inner_proto_lookup_byname(const char *name)
+{
+       const struct proto_desc *proto_tbl[] = {
+               &proto_geneve,
+               &proto_gre,
+               &proto_gretap,
+               &proto_vxlan,
        };
        unsigned int i;
 
@@ -554,7 +575,7 @@ static const struct proto_desc *proto_lookup_byname(const char *name)
 static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
                                            const char *type, json_t *root)
 {
-       const char *protocol, *field, *base;
+       const char *tunnel, *protocol, *field, *base;
        int offset, len, val;
        struct expr *expr;
 
@@ -576,6 +597,33 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
                payload_init_raw(expr, val, offset, len);
                expr->byteorder         = BYTEORDER_BIG_ENDIAN;
                expr->payload.is_raw    = true;
+               return expr;
+       } else if (!json_unpack(root, "{s:s, s:s, s:s}",
+                               "tunnel", &tunnel, "protocol", &protocol, "field", &field)) {
+               const struct proto_desc *proto = proto_lookup_byname(protocol);
+               const struct proto_desc *inner_proto = inner_proto_lookup_byname(tunnel);
+
+               if (!inner_proto) {
+                       json_error(ctx, "Unknown payload tunnel protocol '%s'.",
+                                  tunnel);
+                       return NULL;
+               }
+               if (!proto) {
+                       json_error(ctx, "Unknown payload protocol '%s'.",
+                                  protocol);
+                       return NULL;
+               }
+               if (json_parse_payload_field(proto, field, &val)) {
+                       json_error(ctx, "Unknown %s field '%s'.",
+                                  protocol, field);
+                       return NULL;
+               }
+               expr = payload_expr_alloc(int_loc, proto, val);
+               expr->payload.inner_desc = inner_proto;
+
+               if (proto == &proto_th)
+                       expr->payload.is_raw = true;
+
                return expr;
        } else if (!json_unpack(root, "{s:s, s:s}",
                                "protocol", &protocol, "field", &field)) {
diff --git a/tests/py/inet/geneve.t.json b/tests/py/inet/geneve.t.json
new file mode 100644 (file)
index 0000000..a299fcd
--- /dev/null
@@ -0,0 +1,344 @@
+# udp dport 6081 geneve vni 10
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "vni",
+                    "protocol": "geneve",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": 10
+        }
+    }
+]
+
+# udp dport 6081 geneve ip saddr 10.141.11.2
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": "10.141.11.2"
+        }
+    }
+]
+
+# udp dport 6081 geneve ip saddr 10.141.11.0/24
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": {
+                "prefix": {
+                    "addr": "10.141.11.0",
+                    "len": 24
+                }
+            }
+        }
+    }
+]
+
+# udp dport 6081 geneve ip protocol 1
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "protocol",
+                    "protocol": "ip",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": 1
+        }
+    }
+]
+
+# udp dport 6081 geneve udp sport 8888
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "sport",
+                    "protocol": "udp",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": 8888
+        }
+    }
+]
+
+# udp dport 6081 geneve icmp type echo-reply
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "type",
+                    "protocol": "icmp",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": "echo-reply"
+        }
+    }
+]
+
+# udp dport 6081 geneve ether saddr 62:87:4d:d6:19:05
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ether",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": "62:87:4d:d6:19:05"
+        }
+    }
+]
+
+# udp dport 6081 geneve vlan id 10
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "id",
+                    "protocol": "vlan",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": 10
+        }
+    }
+]
+
+# udp dport 6081 geneve ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# udp dport 6081 geneve ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "geneve"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 6081
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "concat": [
+                    {
+                        "payload": {
+                            "field": "saddr",
+                            "protocol": "ip",
+                            "tunnel": "geneve"
+                        }
+                    },
+                    {
+                        "payload": {
+                            "field": "daddr",
+                            "protocol": "ip",
+                            "tunnel": "geneve"
+                        }
+                    }
+                ]
+            },
+            "op": "==",
+            "right": {
+                "set": [
+                    {
+                        "concat": [
+                            "1.2.3.4",
+                            "4.3.2.1"
+                        ]
+                    }
+                ]
+            }
+        }
+    }
+]
+
diff --git a/tests/py/inet/gre.t.json b/tests/py/inet/gre.t.json
new file mode 100644 (file)
index 0000000..c443176
--- /dev/null
@@ -0,0 +1,177 @@
+# gre version 0
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "version",
+                    "protocol": "gre"
+                }
+            },
+            "op": "==",
+            "right": 0
+        }
+    }
+]
+
+# gre ip saddr 10.141.11.2
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "gre"
+                }
+            },
+            "op": "==",
+            "right": "10.141.11.2"
+        }
+    }
+]
+
+# gre ip saddr 10.141.11.0/24
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "gre"
+                }
+            },
+            "op": "==",
+            "right": {
+                "prefix": {
+                    "addr": "10.141.11.0",
+                    "len": 24
+                }
+            }
+        }
+    }
+]
+
+# gre ip protocol 1
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "protocol",
+                    "protocol": "ip",
+                    "tunnel": "gre"
+                }
+            },
+            "op": "==",
+            "right": 1
+        }
+    }
+]
+
+# gre udp sport 8888
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "sport",
+                    "protocol": "udp",
+                    "tunnel": "gre"
+                }
+            },
+            "op": "==",
+            "right": 8888
+        }
+    }
+]
+
+# gre icmp type echo-reply
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "type",
+                    "protocol": "icmp",
+                    "tunnel": "gre"
+                }
+            },
+            "op": "==",
+            "right": "echo-reply"
+        }
+    }
+]
+
+# gre ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "gre"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# gre ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "gre"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+    {
+        "match": {
+            "left": {
+                "concat": [
+                    {
+                        "payload": {
+                            "field": "saddr",
+                            "protocol": "ip",
+                            "tunnel": "gre"
+                        }
+                    },
+                    {
+                        "payload": {
+                            "field": "daddr",
+                            "protocol": "ip",
+                            "tunnel": "gre"
+                        }
+                    }
+                ]
+            },
+            "op": "==",
+            "right": {
+                "set": [
+                    {
+                        "concat": [
+                            "1.2.3.4",
+                            "4.3.2.1"
+                        ]
+                    }
+                ]
+            }
+        }
+    }
+]
+
diff --git a/tests/py/inet/gretap.t.json b/tests/py/inet/gretap.t.json
new file mode 100644 (file)
index 0000000..36fa978
--- /dev/null
@@ -0,0 +1,195 @@
+# gretap ip saddr 10.141.11.2
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": "10.141.11.2"
+        }
+    }
+]
+
+# gretap ip saddr 10.141.11.0/24
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": {
+                "prefix": {
+                    "addr": "10.141.11.0",
+                    "len": 24
+                }
+            }
+        }
+    }
+]
+
+# gretap ip protocol 1
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "protocol",
+                    "protocol": "ip",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": 1
+        }
+    }
+]
+
+# gretap udp sport 8888
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "sport",
+                    "protocol": "udp",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": 8888
+        }
+    }
+]
+
+# gretap icmp type echo-reply
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "type",
+                    "protocol": "icmp",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": "echo-reply"
+        }
+    }
+]
+
+# gretap ether saddr 62:87:4d:d6:19:05
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ether",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": "62:87:4d:d6:19:05"
+        }
+    }
+]
+
+# gretap vlan id 10
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "id",
+                    "protocol": "vlan",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": 10
+        }
+    }
+]
+
+# gretap ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# gretap ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "gretap"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+    {
+        "match": {
+            "left": {
+                "concat": [
+                    {
+                        "payload": {
+                            "field": "saddr",
+                            "protocol": "ip",
+                            "tunnel": "gretap"
+                        }
+                    },
+                    {
+                        "payload": {
+                            "field": "daddr",
+                            "protocol": "ip",
+                            "tunnel": "gretap"
+                        }
+                    }
+                ]
+            },
+            "op": "==",
+            "right": {
+                "set": [
+                    {
+                        "concat": [
+                            "1.2.3.4",
+                            "4.3.2.1"
+                        ]
+                    }
+                ]
+            }
+        }
+    }
+]
+
diff --git a/tests/py/inet/vxlan.t.json b/tests/py/inet/vxlan.t.json
new file mode 100644 (file)
index 0000000..91b3d29
--- /dev/null
@@ -0,0 +1,344 @@
+# udp dport 4789 vxlan vni 10
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "vni",
+                    "protocol": "vxlan",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": 10
+        }
+    }
+]
+
+# udp dport 4789 vxlan ip saddr 10.141.11.2
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": "10.141.11.2"
+        }
+    }
+]
+
+# udp dport 4789 vxlan ip saddr 10.141.11.0/24
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ip",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": {
+                "prefix": {
+                    "addr": "10.141.11.0",
+                    "len": 24
+                }
+            }
+        }
+    }
+]
+
+# udp dport 4789 vxlan ip protocol 1
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "protocol",
+                    "protocol": "ip",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": 1
+        }
+    }
+]
+
+# udp dport 4789 vxlan udp sport 8888
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "sport",
+                    "protocol": "udp",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": 8888
+        }
+    }
+]
+
+# udp dport 4789 vxlan icmp type echo-reply
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "type",
+                    "protocol": "icmp",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": "echo-reply"
+        }
+    }
+]
+
+# udp dport 4789 vxlan ether saddr 62:87:4d:d6:19:05
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "saddr",
+                    "protocol": "ether",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": "62:87:4d:d6:19:05"
+        }
+    }
+]
+
+# udp dport 4789 vxlan vlan id 10
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "id",
+                    "protocol": "vlan",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": 10
+        }
+    }
+]
+
+# udp dport 4789 vxlan ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# udp dport 4789 vxlan ip dscp 0x02
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dscp",
+                    "protocol": "ip",
+                    "tunnel": "vxlan"
+                }
+            },
+            "op": "==",
+            "right": 2
+        }
+    }
+]
+
+# udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "dport",
+                    "protocol": "udp"
+                }
+            },
+            "op": "==",
+            "right": 4789
+        }
+    },
+    {
+        "match": {
+            "left": {
+                "concat": [
+                    {
+                        "payload": {
+                            "field": "saddr",
+                            "protocol": "ip",
+                            "tunnel": "vxlan"
+                        }
+                    },
+                    {
+                        "payload": {
+                            "field": "daddr",
+                            "protocol": "ip",
+                            "tunnel": "vxlan"
+                        }
+                    }
+                ]
+            },
+            "op": "==",
+            "right": {
+                "set": [
+                    {
+                        "concat": [
+                            "1.2.3.4",
+                            "4.3.2.1"
+                        ]
+                    }
+                ]
+            }
+        }
+    }
+]
+