]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
json: Accept more than two operands in binary expressions
authorPhil Sutter <phil@nwl.cc>
Wed, 20 Mar 2024 14:54:54 +0000 (15:54 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 23 Jan 2025 00:35:35 +0000 (01:35 +0100)
commit 0ac39384fd9e48ff6bcc5605df2cbeb33af64b9e upstream.

The most common use case is ORing flags like

| syn | ack | rst

but nft seems to be fine with less intuitive stuff like

| meta mark set ip dscp << 2 << 3

so support all of them.

Signed-off-by: Phil Sutter <phil@nwl.cc>
doc/libnftables-json.adoc
src/json.c
src/parser_json.c
tests/py/inet/tcp.t.json
tests/py/inet/tcp.t.json.output
tests/shell/testcases/nft-f/dumps/0012different_defines_0.json-nft [new file with mode: 0644]
tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft [new file with mode: 0644]

index 6f86d149a8eb5ca7fbcacfb9194e7ae06bdce7fe..1cce0e69301bac97c1aa1d48209398b091eb755e 100644 (file)
@@ -1337,15 +1337,17 @@ Perform kernel Forwarding Information Base lookups.
 
 === BINARY OPERATION
 [verse]
-*{ "|": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ "^": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ "&": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ "+<<+": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ ">>": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-
-All binary operations expect an array of exactly two expressions, of which the
+*{ "|": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ "^": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ "&": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ "+<<+": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ ">>": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+'EXPRESSIONS' := 'EXPRESSION' | 'EXPRESSION'*,* 'EXPRESSIONS'
+
+All binary operations expect an array of at least two expressions, of which the
 first element denotes the left hand side and the second one the right hand
-side.
+side. Extra elements are accepted in the given array and appended to the term
+accordingly.
 
 === VERDICT
 [verse]
index 7e2bf433f39dddef098a226fa573eb3b10a1113d..f26250469fa04164ee67081d51d21968271427e9 100644 (file)
@@ -530,11 +530,24 @@ json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx)
                         "right", expr_print_json(expr->flagcmp.value, octx));
 }
 
+static json_t *
+__binop_expr_json(int op, const struct expr *expr, struct output_ctx *octx)
+{
+       json_t *a = json_array();
+
+       if (expr->etype == EXPR_BINOP && expr->op == op) {
+               json_array_extend(a, __binop_expr_json(op, expr->left, octx));
+               json_array_extend(a, __binop_expr_json(op, expr->right, octx));
+       } else {
+               json_array_append_new(a, expr_print_json(expr, octx));
+       }
+       return a;
+}
+
 json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx)
 {
-       return json_pack("{s:[o, o]}", expr_op_symbols[expr->op],
-                        expr_print_json(expr->left, octx),
-                        expr_print_json(expr->right, octx));
+       return json_pack("{s:o}", expr_op_symbols[expr->op],
+                        __binop_expr_json(expr->op, expr, octx));
 }
 
 json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx)
index b59918c0da0d193e98c3faf44961b21b868f6601..97e1172c362e0f09cbc0fb97dcfa2dc66d419226 100644 (file)
@@ -1133,6 +1133,18 @@ static struct expr *json_parse_binop_expr(struct json_ctx *ctx,
                return NULL;
        }
 
+       if (json_array_size(root) > 2) {
+               left = json_parse_primary_expr(ctx, json_array_get(root, 0));
+               right = json_parse_primary_expr(ctx, json_array_get(root, 1));
+               left = binop_expr_alloc(int_loc, thisop, left, right);
+               for (i = 2; i < json_array_size(root); i++) {
+                       jright = json_array_get(root, i);
+                       right = json_parse_primary_expr(ctx, jright);
+                       left = binop_expr_alloc(int_loc, thisop, left, right);
+               }
+               return left;
+       }
+
        if (json_unpack_err(ctx, root, "[o, o!]", &jleft, &jright))
                return NULL;
 
index d3a846cf9a4006f81e0d598bfc7cd9c091f6c428..bd589cf0091fea4d7f386fc0980b678cf5125ea5 100644 (file)
                         }
                     },
                     {
-                        "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ]
+                        "|": [ "fin", "syn", "rst", "psh", "ack", "urg", "ecn", "cwr" ]
                     }
                 ]
             },
             "op": "==",
-            "right": { "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ] }
+            "right": { "|": [ "fin", "syn", "rst", "psh", "ack", "urg", "ecn", "cwr" ] }
         }
     }
 ]
                             "protocol": "tcp"
                         }
                     },
-                    {
-                        "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            {
-                                                "|": [
-                                                    {
-                                                        "|": [
-                                                            "fin",
-                                                            "syn"
-                                                        ]
-                                                    },
-                                                    "rst"
-                                                ]
-                                            },
-                                            "psh"
-                                        ]
-                                    },
-                                    "ack"
-                                ]
-                            },
-                            "urg"
-                        ]
-                    }
+                    { "|": [ "fin", "syn", "rst", "psh", "ack", "urg" ] }
                 ]
             },
             "op": "==",
             "right": {
                 "set": [
-                    {
-                        "|": [
-                            {
-                                "|": [
-                                    "fin",
-                                    "psh"
-                                ]
-                            },
-                            "ack"
-                        ]
-                    },
+                    { "|": [ "fin", "psh", "ack" ] },
                     "fin",
-                    {
-                        "|": [
-                            "psh",
-                            "ack"
-                        ]
-                    },
+                    { "|": [ "psh", "ack" ] },
                     "ack"
                 ]
             }
                             "protocol": "tcp"
                         }
                     },
-                    {
-                        "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            "fin",
-                                            "syn"
-                                        ]
-                                    },
-                                    "rst"
-                                ]
-                            },
-                            "ack"
-                        ]
-                    }
+                    { "|": [ "fin", "syn", "rst", "ack" ] }
                 ]
             },
             "op": "!=",
index e186e127fd6711c4caf43b65b8fa760e21179c6e..3f03c0ddd1586dc48d304b8610b08f7539222c26 100644 (file)
                     },
                     {
                         "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            {
-                                                "|": [
-                                                    {
-                                                        "|": [
-                                                            "fin",
-                                                            "syn"
-                                                        ]
-                                                    },
-                                                    "rst"
-                                                ]
-                                            },
-                                            "psh"
-                                        ]
-                                    },
-                                    "ack"
-                                ]
-                            },
+                            "fin",
+                            "syn",
+                            "rst",
+                            "psh",
+                            "ack",
                             "urg"
                         ]
                     }
                     "fin",
                     {
                         "|": [
-                            {
-                                "|": [
-                                    "fin",
-                                    "psh"
-                                ]
-                            },
+                            "fin",
+                            "psh",
                             "ack"
                         ]
                     },
                     },
                     {
                         "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            "fin",
-                                            "syn"
-                                        ]
-                                    },
-                                    "rst"
-                                ]
-                            },
+                            "fin",
+                            "syn",
+                            "rst",
                             "ack"
                         ]
                     }
                     },
                     {
                         "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            "fin",
-                                            "syn"
-                                        ]
-                                    },
-                                    "rst"
-                                ]
-                            },
+                            "fin",
+                            "syn",
+                            "rst",
                             "ack"
                         ]
                     }
                     },
                     {
                         "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            "fin",
-                                            "syn"
-                                        ]
-                                    },
-                                    "rst"
-                                ]
-                            },
+                            "fin",
+                            "syn",
+                            "rst",
                             "ack"
                         ]
                     }
                     },
                     {
                         "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            "fin",
-                                            "syn"
-                                        ]
-                                    },
-                                    "rst"
-                                ]
-                            },
+                            "fin",
+                            "syn",
+                            "rst",
                             "ack"
                         ]
                     }
                     },
                     {
                         "|": [
-                            {
-                                "|": [
-                                    {
-                                        "|": [
-                                            "fin",
-                                            "syn"
-                                        ]
-                                    },
-                                    "rst"
-                                ]
-                            },
+                            "fin",
+                            "syn",
+                            "rst",
                             "ack"
                         ]
                     }
diff --git a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.json-nft b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.json-nft
new file mode 100644 (file)
index 0000000..1b2e342
--- /dev/null
@@ -0,0 +1,778 @@
+{
+  "nftables": [
+    {
+      "metainfo": {
+        "version": "VERSION",
+        "release_name": "RELEASE_NAME",
+        "json_schema_version": 1
+      }
+    },
+    {
+      "table": {
+        "family": "inet",
+        "name": "t",
+        "handle": 0
+      }
+    },
+    {
+      "chain": {
+        "family": "inet",
+        "table": "t",
+        "name": "c",
+        "handle": 0
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "iifname"
+                }
+              },
+              "right": "whatever"
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "oifname"
+                }
+              },
+              "right": "whatever"
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "iif"
+                }
+              },
+              "right": "lo"
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "oif"
+                }
+              },
+              "right": "lo"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "iifname"
+                }
+              },
+              "right": {
+                "set": [
+                  "whatever"
+                ]
+              }
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "iif"
+                }
+              },
+              "right": {
+                "set": [
+                  "lo"
+                ]
+              }
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "mark"
+                }
+              },
+              "right": 123
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "in",
+              "left": {
+                "ct": {
+                  "key": "state"
+                }
+              },
+              "right": [
+                "established",
+                "related",
+                "new"
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "!=",
+              "left": {
+                "ct": {
+                  "key": "state"
+                }
+              },
+              "right": {
+                "|": [
+                  "established",
+                  "related",
+                  "new"
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "ip",
+                  "field": "saddr"
+                }
+              },
+              "right": "10.0.0.0"
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "ip",
+                  "field": "daddr"
+                }
+              },
+              "right": "10.0.0.2"
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "ip",
+                  "field": "saddr"
+                }
+              },
+              "right": "10.0.0.0"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "ip6",
+                  "field": "daddr"
+                }
+              },
+              "right": "fe0::1"
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "ip6",
+                  "field": "saddr"
+                }
+              },
+              "right": "fe0::2"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "vmap": {
+              "key": {
+                "payload": {
+                  "protocol": "ip",
+                  "field": "saddr"
+                }
+              },
+              "data": {
+                "set": [
+                  [
+                    "10.0.0.0",
+                    {
+                      "drop": null
+                    }
+                  ],
+                  [
+                    "10.0.0.2",
+                    {
+                      "accept": null
+                    }
+                  ]
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "vmap": {
+              "key": {
+                "payload": {
+                  "protocol": "ip6",
+                  "field": "daddr"
+                }
+              },
+              "data": {
+                "set": [
+                  [
+                    "fe0::1",
+                    {
+                      "drop": null
+                    }
+                  ],
+                  [
+                    "fe0::2",
+                    {
+                      "accept": null
+                    }
+                  ]
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "concat": [
+                  {
+                    "payload": {
+                      "protocol": "ip6",
+                      "field": "saddr"
+                    }
+                  },
+                  {
+                    "payload": {
+                      "protocol": "ip6",
+                      "field": "nexthdr"
+                    }
+                  }
+                ]
+              },
+              "right": {
+                "set": [
+                  {
+                    "concat": [
+                      "fe0::2",
+                      "tcp"
+                    ]
+                  },
+                  {
+                    "concat": [
+                      "fe0::1",
+                      "udp"
+                    ]
+                  }
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "vmap": {
+              "key": {
+                "concat": [
+                  {
+                    "payload": {
+                      "protocol": "ip",
+                      "field": "daddr"
+                    }
+                  },
+                  {
+                    "meta": {
+                      "key": "iif"
+                    }
+                  }
+                ]
+              },
+              "data": {
+                "set": [
+                  [
+                    {
+                      "concat": [
+                        "10.0.0.0",
+                        "lo"
+                      ]
+                    },
+                    {
+                      "accept": null
+                    }
+                  ]
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "dport"
+                }
+              },
+              "right": {
+                "range": [
+                  100,
+                  222
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "vmap": {
+              "key": {
+                "payload": {
+                  "protocol": "udp",
+                  "field": "dport"
+                }
+              },
+              "data": {
+                "set": [
+                  [
+                    {
+                      "range": [
+                        100,
+                        222
+                      ]
+                    },
+                    {
+                      "accept": null
+                    }
+                  ]
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "sport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "dport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "oifname"
+                }
+              },
+              "right": "foobar"
+            }
+          },
+          {
+            "queue": {
+              "num": 0,
+              "flags": "bypass"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "sport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "dport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "oifname"
+                }
+              },
+              "right": "foobar"
+            }
+          },
+          {
+            "queue": {
+              "num": {
+                "range": [
+                  1,
+                  42
+                ]
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "sport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "dport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "oifname"
+                }
+              },
+              "right": "foobar"
+            }
+          },
+          {
+            "queue": {
+              "num": {
+                "range": [
+                  1,
+                  42
+                ]
+              },
+              "flags": [
+                "bypass",
+                "fanout"
+              ]
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "sport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "dport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "oifname"
+                }
+              },
+              "right": "foobar"
+            }
+          },
+          {
+            "queue": {
+              "num": {
+                "symhash": {
+                  "mod": 2
+                }
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "t",
+        "chain": "c",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "sport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "dport"
+                }
+              },
+              "right": 1
+            }
+          },
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "oifname"
+                }
+              },
+              "right": "foobar"
+            }
+          },
+          {
+            "queue": {
+              "num": {
+                "jhash": {
+                  "mod": 4,
+                  "expr": {
+                    "concat": [
+                      {
+                        "payload": {
+                          "protocol": "tcp",
+                          "field": "dport"
+                        }
+                      },
+                      {
+                        "payload": {
+                          "protocol": "tcp",
+                          "field": "sport"
+                        }
+                      }
+                    ]
+                  }
+                }
+              },
+              "flags": "bypass"
+            }
+          }
+        ]
+      }
+    }
+  ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft b/tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft
new file mode 100644 (file)
index 0000000..6a35115
--- /dev/null
@@ -0,0 +1,138 @@
+{
+  "nftables": [
+    {
+      "metainfo": {
+        "version": "VERSION",
+        "release_name": "RELEASE_NAME",
+        "json_schema_version": 1
+      }
+    },
+    {
+      "table": {
+        "family": "ip",
+        "name": "test",
+        "handle": 0
+      }
+    },
+    {
+      "set": {
+        "family": "ip",
+        "name": "tcp_good_flags",
+        "table": "test",
+        "type": "tcp_flag",
+        "handle": 0,
+        "flags": [
+          "constant"
+        ],
+        "elem": [
+          {
+            "|": [
+              "fin",
+              "psh",
+              "ack",
+              "urg"
+            ]
+          },
+          {
+            "|": [
+              "fin",
+              "psh",
+              "ack"
+            ]
+          },
+          {
+            "|": [
+              "fin",
+              "ack",
+              "urg"
+            ]
+          },
+          {
+            "|": [
+              "fin",
+              "ack"
+            ]
+          },
+          {
+            "|": [
+              "syn",
+              "psh",
+              "ack",
+              "urg"
+            ]
+          },
+          {
+            "|": [
+              "syn",
+              "psh",
+              "ack"
+            ]
+          },
+          {
+            "|": [
+              "syn",
+              "ack",
+              "urg"
+            ]
+          },
+          {
+            "|": [
+              "syn",
+              "ack"
+            ]
+          },
+          "syn",
+          {
+            "|": [
+              "rst",
+              "psh",
+              "ack",
+              "urg"
+            ]
+          },
+          {
+            "|": [
+              "rst",
+              "psh",
+              "ack"
+            ]
+          },
+          {
+            "|": [
+              "rst",
+              "ack",
+              "urg"
+            ]
+          },
+          {
+            "|": [
+              "rst",
+              "ack"
+            ]
+          },
+          "rst",
+          {
+            "|": [
+              "psh",
+              "ack",
+              "urg"
+            ]
+          },
+          {
+            "|": [
+              "psh",
+              "ack"
+            ]
+          },
+          {
+            "|": [
+              "ack",
+              "urg"
+            ]
+          },
+          "ack"
+        ]
+      }
+    }
+  ]
+}