From b602911b31dc98a2b2c835f116396a7beb5fabbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Valverde?= Date: Wed, 20 Apr 2022 00:04:05 +0100 Subject: [PATCH] dfilter: Add support for universal quantifiers Adds the keywords "any" and "all" to implement the quantification to any existing relational operator. Filter: all tcp.port in {100, 2000..3000} Syntax tree: 0 ALL TEST_IN: 1 FIELD(tcp.port) 1 SET(#2): 2 FVALUE(100 ) 2 FVALUE(2000 ) .. FVALUE(3000 ) Instructions: 00000 READ_TREE tcp.port -> reg#0 00001 IF_FALSE_GOTO 5 00002 ALL_EQ reg#0 === 100 00003 IF_TRUE_GOTO 5 00004 ALL_IN_RANGE reg#0 in { 2000 .. 3000 } 00005 RETURN --- doc/wireshark-filter.adoc | 10 +++ docbook/release-notes.adoc | 1 + epan/dfilter/dfvm.c | 139 +++++++++++++++++++++++------ epan/dfilter/dfvm.h | 12 ++- epan/dfilter/gencode.c | 93 +++++++++++++++---- epan/dfilter/grammar.lemon | 16 +++- epan/dfilter/scanner.l | 2 + epan/dfilter/sttype-test.c | 25 +++++- epan/dfilter/sttype-test.h | 6 ++ epan/dfilter/syntax-tree.h | 6 ++ epan/proto.c | 2 + test/suite_dfilter/group_syntax.py | 13 ++- 12 files changed, 278 insertions(+), 47 deletions(-) diff --git a/doc/wireshark-filter.adoc b/doc/wireshark-filter.adoc index 7a6b7339c0..02d9bbb07a 100644 --- a/doc/wireshark-filter.adoc +++ b/doc/wireshark-filter.adoc @@ -77,6 +77,16 @@ operators, their aliases and meaning: all_eq, === All fields must be equal any_ne, !== Any fields must be not equal +The operators "any" or "all" can be used with any comparison operator to make +the test match any or all fields: + + all tcp.port > 1024 + + any ip.addr != 1.1.1.1 + +The "any" and "all" modifiers take precedence over comparison operators such +as "===" and "any_eq". + === Search and match operators Additional operators exist expressed only in English, not C-like syntax: diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc index a3be623024..d81943d9cf 100644 --- a/docbook/release-notes.adoc +++ b/docbook/release-notes.adoc @@ -81,6 +81,7 @@ The following features are new (or have been significantly updated) since versio ** New display filter functions max(), min() and abs() have been added. ** Functions can accept expressions as arguments, including other functions. Previously only protocol fields and slices were syntactically valid function arguments. +** Adds the universal quantifiers "any" and "all" to any relational operator: all tcp.port > 1024. * The `text2pcap` command and the “Import from Hex Dump” feature have been updated and enhanced: ** `text2pcap` supports writing the output file in all the capture file formats that wiretap library supports, using the same `-F` option as `editcap`, `mergecap`, and `tshark`. diff --git a/epan/dfilter/dfvm.c b/epan/dfilter/dfvm.c index 10f340b4c4..123ea2bb5c 100644 --- a/epan/dfilter/dfvm.c +++ b/epan/dfilter/dfvm.c @@ -35,13 +35,19 @@ dfvm_opcode_tostr(dfvm_opcode_t code) case ANY_EQ: return "ANY_EQ"; case ALL_NE: return "ALL_NE"; case ANY_NE: return "ANY_NE"; + case ALL_GT: return "ALL_GT"; case ANY_GT: return "ANY_GT"; + case ALL_GE: return "ALL_GE"; case ANY_GE: return "ANY_GE"; + case ALL_LT: return "ALL_LT"; case ANY_LT: return "ANY_LT"; + case ALL_LE: return "ALL_LE"; case ANY_LE: return "ANY_LE"; - case ANY_ZERO: return "ANY_ZERO"; case ALL_ZERO: return "ALL_ZERO"; + case ANY_ZERO: return "ANY_ZERO"; + case ALL_CONTAINS: return "ALL_CONTAINS"; case ANY_CONTAINS: return "ANY_CONTAINS"; + case ALL_MATCHES: return "ALL_MATCHES"; case ANY_MATCHES: return "ANY_MATCHES"; case MK_SLICE: return "MK_SLICE"; case MK_BITWISE_AND: return "MK_BITWISE_AND"; @@ -54,6 +60,7 @@ dfvm_opcode_tostr(dfvm_opcode_t code) case CALL_FUNCTION: return "CALL_FUNCTION"; case STACK_PUSH: return "STACK_PUSH"; case STACK_POP: return "STACK_POP"; + case ALL_IN_RANGE: return "ALL_IN_RANGE"; case ANY_IN_RANGE: return "ANY_IN_RANGE"; } return "(fix-opcode-string)"; @@ -370,24 +377,28 @@ dfvm_dump_str(wmem_allocator_t *alloc, dfilter_t *df, gboolean print_references) id, arg1_str, arg2_str); break; + case ALL_GT: case ANY_GT: - wmem_strbuf_append_printf(buf, "%05d ANY_GT\t\t%s > %s\n", - id, arg1_str, arg2_str); + wmem_strbuf_append_printf(buf, "%05d %s\t\t%s > %s\n", + id, dfvm_opcode_tostr(insn->op), arg1_str, arg2_str); break; + case ALL_GE: case ANY_GE: - wmem_strbuf_append_printf(buf, "%05d ANY_GE\t\t%s >= %s\n", - id, arg1_str, arg2_str); + wmem_strbuf_append_printf(buf, "%05d %s\t\t%s >= %s\n", + id, dfvm_opcode_tostr(insn->op), arg1_str, arg2_str); break; + case ALL_LT: case ANY_LT: - wmem_strbuf_append_printf(buf, "%05d ANY_LT\t\t%s < %s\n", - id, arg1_str, arg2_str); + wmem_strbuf_append_printf(buf, "%05d %s\t\t%s < %s\n", + id, dfvm_opcode_tostr(insn->op), arg1_str, arg2_str); break; + case ALL_LE: case ANY_LE: - wmem_strbuf_append_printf(buf, "%05d ANY_LE\t\t%s <= %s\n", - id, arg1_str, arg2_str); + wmem_strbuf_append_printf(buf, "%05d %s\t\t%s <= %s\n", + id, dfvm_opcode_tostr(insn->op), arg1_str, arg2_str); break; case MK_BITWISE_AND: @@ -395,14 +406,10 @@ dfvm_dump_str(wmem_allocator_t *alloc, dfilter_t *df, gboolean print_references) id, arg1_str, arg2_str, arg3_str); break; - case ANY_ZERO: - wmem_strbuf_append_printf(buf, "%05d ANY_ZERO\t\t%s\n", - id, arg1_str); - break; - case ALL_ZERO: - wmem_strbuf_append_printf(buf, "%05d ALL_ZERO\t\t%s\n", - id, arg1_str); + case ANY_ZERO: + wmem_strbuf_append_printf(buf, "%05d %s\t\t%s\n", + id, dfvm_opcode_tostr(insn->op), arg1_str); break; case DFVM_ADD: @@ -430,19 +437,23 @@ dfvm_dump_str(wmem_allocator_t *alloc, dfilter_t *df, gboolean print_references) id, arg1_str, arg2_str, arg3_str); break; + case ALL_CONTAINS: case ANY_CONTAINS: - wmem_strbuf_append_printf(buf, "%05d ANY_CONTAINS\t%s contains %s\n", - id, arg1_str, arg2_str); + wmem_strbuf_append_printf(buf, "%05d %s\t%s contains %s\n", + id, dfvm_opcode_tostr(insn->op), arg1_str, arg2_str); break; + case ALL_MATCHES: case ANY_MATCHES: - wmem_strbuf_append_printf(buf, "%05d ANY_MATCHES\t%s matches %s\n", - id, arg1_str, arg2_str); + wmem_strbuf_append_printf(buf, "%05d %s\t%s matches %s\n", + id, dfvm_opcode_tostr(insn->op), arg1_str, arg2_str); break; + case ALL_IN_RANGE: case ANY_IN_RANGE: - wmem_strbuf_append_printf(buf, "%05d ANY_IN_RANGE\t%s in { %s .. %s }\n", - id, arg1_str, arg2_str, arg3_str); + wmem_strbuf_append_printf(buf, "%05d %s\t%s in { %s .. %s }\n", + id, dfvm_opcode_tostr(insn->op), + arg1_str, arg2_str, arg3_str); break; case MK_MINUS: @@ -812,6 +823,21 @@ any_matches(dfilter_t *df, dfvm_value_t *arg1, dfvm_value_t *arg2) return FALSE; } +static gboolean +all_matches(dfilter_t *df, dfvm_value_t *arg1, dfvm_value_t *arg2) +{ + GSList *list1 = df->registers[arg1->value.numeric]; + ws_regex_t *re = arg2->value.pcre; + + while (list1) { + if (!fvalue_matches(list1->data, re)) { + return FALSE; + } + list1 = g_slist_next(list1); + } + return TRUE; +} + static gboolean any_in_range_internal(GSList *list1, fvalue_t *low, fvalue_t *high) { @@ -826,7 +852,20 @@ any_in_range_internal(GSList *list1, fvalue_t *low, fvalue_t *high) } static gboolean -any_in_range(dfilter_t *df, dfvm_value_t *arg1, +all_in_range_internal(GSList *list1, fvalue_t *low, fvalue_t *high) +{ + while (list1) { + if (!fvalue_ge(list1->data, low) || + !fvalue_le(list1->data, high)) { + return FALSE; + } + list1 = g_slist_next(list1); + } + return TRUE; +} + +static gboolean +match_in_range(dfilter_t *df, enum match_how how, dfvm_value_t *arg1, dfvm_value_t *arg_low, dfvm_value_t *arg_high) { GSList *list1 = df->registers[arg1->value.numeric]; @@ -855,7 +894,27 @@ any_in_range(dfilter_t *df, dfvm_value_t *arg1, else { ws_assert_not_reached(); } - return any_in_range_internal(list1, low, high); + + if (how == MATCH_ALL) + return all_in_range_internal(list1, low, high); + else if (how == MATCH_ANY) + return any_in_range_internal(list1, low, high); + else + ws_assert_not_reached(); +} + +static gboolean +any_in_range(dfilter_t *df, dfvm_value_t *arg1, + dfvm_value_t *arg_low, dfvm_value_t *arg_high) +{ + return match_in_range(df, MATCH_ANY, arg1, arg_low, arg_high); +} + +static gboolean +all_in_range(dfilter_t *df, dfvm_value_t *arg1, + dfvm_value_t *arg_low, dfvm_value_t *arg_high) +{ + return match_in_range(df, MATCH_ALL, arg1, arg_low, arg_high); } /* Clear registers that were populated during evaluation. @@ -1246,18 +1305,34 @@ dfvm_apply(dfilter_t *df, proto_tree *tree) accum = any_test(df, fvalue_ne, arg1, arg2); break; + case ALL_GT: + accum = all_test(df, fvalue_gt, arg1, arg2); + break; + case ANY_GT: accum = any_test(df, fvalue_gt, arg1, arg2); break; + case ALL_GE: + accum = all_test(df, fvalue_ge, arg1, arg2); + break; + case ANY_GE: accum = any_test(df, fvalue_ge, arg1, arg2); break; + case ALL_LT: + accum = all_test(df, fvalue_lt, arg1, arg2); + break; + case ANY_LT: accum = any_test(df, fvalue_lt, arg1, arg2); break; + case ALL_LE: + accum = all_test(df, fvalue_le, arg1, arg2); + break; + case ANY_LE: accum = any_test(df, fvalue_le, arg1, arg2); break; @@ -1286,22 +1361,34 @@ dfvm_apply(dfilter_t *df, proto_tree *tree) mk_binary(df, fvalue_modulo, arg1, arg2, arg3); break; + case ALL_ZERO: + accum = all_test_unary(df, fvalue_is_zero, arg1); + break; + case ANY_ZERO: accum = any_test_unary(df, fvalue_is_zero, arg1); break; - case ALL_ZERO: - accum = all_test_unary(df, fvalue_is_zero, arg1); + case ALL_CONTAINS: + accum = all_test(df, fvalue_contains, arg1, arg2); break; case ANY_CONTAINS: accum = any_test(df, fvalue_contains, arg1, arg2); break; + case ALL_MATCHES: + accum = all_matches(df, arg1, arg2); + break; + case ANY_MATCHES: accum = any_matches(df, arg1, arg2); break; + case ALL_IN_RANGE: + accum = all_in_range(df, arg1, arg2, arg3); + break; + case ANY_IN_RANGE: accum = any_in_range(df, arg1, arg2, arg3); break; diff --git a/epan/dfilter/dfvm.h b/epan/dfilter/dfvm.h index 1381fecbdd..51dbf3588a 100644 --- a/epan/dfilter/dfvm.h +++ b/epan/dfilter/dfvm.h @@ -61,13 +61,19 @@ typedef enum { ANY_EQ, ALL_NE, ANY_NE, + ALL_GT, ANY_GT, + ALL_GE, ANY_GE, + ALL_LT, ANY_LT, + ALL_LE, ANY_LE, - ANY_ZERO, ALL_ZERO, + ANY_ZERO, + ALL_CONTAINS, ANY_CONTAINS, + ALL_MATCHES, ANY_MATCHES, MK_SLICE, MK_BITWISE_AND, @@ -80,8 +86,8 @@ typedef enum { CALL_FUNCTION, STACK_PUSH, STACK_POP, - ANY_IN_RANGE - + ALL_IN_RANGE, + ANY_IN_RANGE, } dfvm_opcode_t; const char * diff --git a/epan/dfilter/gencode.c b/epan/dfilter/gencode.c index df8602ab6b..5686c71fcb 100644 --- a/epan/dfilter/gencode.c +++ b/epan/dfilter/gencode.c @@ -28,6 +28,61 @@ gencode(dfwork_t *dfw, stnode_t *st_node); static dfvm_value_t * gen_entity(dfwork_t *dfw, stnode_t *st_arg, GSList **jumps_ptr); +static dfvm_opcode_t +select_opcode(dfvm_opcode_t op, test_match_t how) +{ + if (how == ST_MATCH_DEF) + return op; + + switch (op) { + case ALL_EQ: + case ALL_NE: + case ALL_GT: + case ALL_GE: + case ALL_LT: + case ALL_LE: + case ALL_ZERO: + case ALL_CONTAINS: + case ALL_MATCHES: + case ALL_IN_RANGE: + return how == ST_MATCH_ALL ? op : op + 1; + case ANY_EQ: + case ANY_NE: + case ANY_GT: + case ANY_GE: + case ANY_LT: + case ANY_LE: + case ANY_ZERO: + case ANY_CONTAINS: + case ANY_MATCHES: + case ANY_IN_RANGE: + return how == ST_MATCH_ANY ? op : op - 1; + case IF_TRUE_GOTO: + case IF_FALSE_GOTO: + case CHECK_EXISTS: + case CHECK_EXISTS_R: + case NOT: + case RETURN: + case READ_TREE: + case READ_TREE_R: + case READ_REFERENCE: + case PUT_FVALUE: + case MK_SLICE: + case MK_BITWISE_AND: + case MK_MINUS: + case DFVM_ADD: + case DFVM_SUBTRACT: + case DFVM_MULTIPLY: + case DFVM_DIVIDE: + case DFVM_MODULO: + case CALL_FUNCTION: + case STACK_PUSH: + case STACK_POP: + break; + } + ws_assert_not_reached(); +} + static void dfw_append_insn(dfwork_t *dfw, dfvm_insn_t *insn) { @@ -298,7 +353,8 @@ gen_relation_insn(dfwork_t *dfw, dfvm_opcode_t op, } static void -gen_relation(dfwork_t *dfw, dfvm_opcode_t op, stnode_t *st_arg1, stnode_t *st_arg2) +gen_relation(dfwork_t *dfw, dfvm_opcode_t op, test_match_t how, + stnode_t *st_arg1, stnode_t *st_arg2) { GSList *jumps = NULL; dfvm_value_t *val1, *val2; @@ -308,6 +364,7 @@ gen_relation(dfwork_t *dfw, dfvm_opcode_t op, stnode_t *st_arg1, stnode_t *st_ar val2 = gen_entity(dfw, st_arg2, &jumps); /* Then combine them in a DFVM insruction */ + op = select_opcode(op, how); gen_relation_insn(dfw, op, val1, val2, NULL, NULL); /* If either of the relation arguments need an "exit" instruction @@ -331,7 +388,8 @@ fixup_jumps(gpointer data, gpointer user_data) /* Generate the code for the in operator. It behaves much like an OR-ed * series of == tests, but without the redundant existence checks. */ static void -gen_relation_in(dfwork_t *dfw, stnode_t *st_arg1, stnode_t *st_arg2) +gen_relation_in(dfwork_t *dfw, test_match_t how, + stnode_t *st_arg1, stnode_t *st_arg2) { dfvm_insn_t *insn; dfvm_value_t *jmp; @@ -339,6 +397,7 @@ gen_relation_in(dfwork_t *dfw, stnode_t *st_arg1, stnode_t *st_arg2) GSList *node_jumps = NULL; dfvm_value_t *val1, *val2, *val3; stnode_t *node1, *node2; + dfvm_opcode_t op; GSList *nodelist_head, *nodelist; /* Create code for the LHS of the relation */ @@ -358,13 +417,15 @@ gen_relation_in(dfwork_t *dfw, stnode_t *st_arg1, stnode_t *st_arg2) val3 = gen_entity(dfw, node2, &node_jumps); /* Add test to see if the item is in range. */ - gen_relation_insn(dfw, ANY_IN_RANGE, val1, val2, val3, NULL); + op = select_opcode(ANY_IN_RANGE, how); + gen_relation_insn(dfw, op, val1, val2, val3, NULL); } else { /* Normal element: add equality test. */ val2 = gen_entity(dfw, node1, &node_jumps); /* Add test to see if the item matches */ - gen_relation_insn(dfw, ANY_EQ, val1, val2, NULL, NULL); + op = select_opcode(ANY_EQ, how); + gen_relation_insn(dfw, op, val1, val2, NULL, NULL); } /* Exit as soon as we find a match */ @@ -560,12 +621,14 @@ static void gen_test(dfwork_t *dfw, stnode_t *st_node) { test_op_t st_op; + test_match_t st_how; stnode_t *st_arg1, *st_arg2; dfvm_insn_t *insn; dfvm_value_t *jmp; sttype_test_get(st_node, &st_op, &st_arg1, &st_arg2); + st_how = sttype_test_get_match(st_node); switch (st_op) { case TEST_OP_UNINITIALIZED: @@ -603,47 +666,47 @@ gen_test(dfwork_t *dfw, stnode_t *st_node) break; case TEST_OP_ALL_EQ: - gen_relation(dfw, ALL_EQ, st_arg1, st_arg2); + gen_relation(dfw, ALL_EQ, st_how, st_arg1, st_arg2); break; case TEST_OP_ANY_EQ: - gen_relation(dfw, ANY_EQ, st_arg1, st_arg2); + gen_relation(dfw, ANY_EQ, st_how, st_arg1, st_arg2); break; case TEST_OP_ALL_NE: - gen_relation(dfw, ALL_NE, st_arg1, st_arg2); + gen_relation(dfw, ALL_NE, st_how, st_arg1, st_arg2); break; case TEST_OP_ANY_NE: - gen_relation(dfw, ANY_NE, st_arg1, st_arg2); + gen_relation(dfw, ANY_NE, st_how, st_arg1, st_arg2); break; case TEST_OP_GT: - gen_relation(dfw, ANY_GT, st_arg1, st_arg2); + gen_relation(dfw, ANY_GT, st_how, st_arg1, st_arg2); break; case TEST_OP_GE: - gen_relation(dfw, ANY_GE, st_arg1, st_arg2); + gen_relation(dfw, ANY_GE, st_how, st_arg1, st_arg2); break; case TEST_OP_LT: - gen_relation(dfw, ANY_LT, st_arg1, st_arg2); + gen_relation(dfw, ANY_LT, st_how, st_arg1, st_arg2); break; case TEST_OP_LE: - gen_relation(dfw, ANY_LE, st_arg1, st_arg2); + gen_relation(dfw, ANY_LE, st_how, st_arg1, st_arg2); break; case TEST_OP_CONTAINS: - gen_relation(dfw, ANY_CONTAINS, st_arg1, st_arg2); + gen_relation(dfw, ANY_CONTAINS, st_how, st_arg1, st_arg2); break; case TEST_OP_MATCHES: - gen_relation(dfw, ANY_MATCHES, st_arg1, st_arg2); + gen_relation(dfw, ANY_MATCHES, st_how, st_arg1, st_arg2); break; case TEST_OP_IN: - gen_relation_in(dfw, st_arg1, st_arg2); + gen_relation_in(dfw, st_how, st_arg1, st_arg2); break; case OP_BITWISE_AND: diff --git a/epan/dfilter/grammar.lemon b/epan/dfilter/grammar.lemon index 29f10b544e..0188bc094c 100644 --- a/epan/dfilter/grammar.lemon +++ b/epan/dfilter/grammar.lemon @@ -88,7 +88,7 @@ shifting 3 more symbols. */ sentence ::= expr(X). { dfw->st_root = X; } sentence ::= . { dfw->st_root = NULL; } -expr(X) ::= relation_test(R). { X = R; } +expr(X) ::= relation(R). { X = R; } expr(X) ::= arithmetic_expr(E). { X = E; } /* Logical tests */ @@ -265,6 +265,20 @@ relation_test(T) ::= entity(E) TEST_NOT(P) TEST_IN(O) set(S). sttype_test_set1(T, TEST_OP_NOT, O); } +relation(R) ::= relation_test(T). { R = T; } + +relation(R) ::= ANY relation_test(T). +{ + R = T; + sttype_test_set_match(R, ST_MATCH_ANY); +} + +relation(R) ::= ALL relation_test(T). +{ + R = T; + sttype_test_set_match(R, ST_MATCH_ALL); +} + set(S) ::= LBRACE set_list(L) RBRACE. { S = stnode_new(STTYPE_SET, L, NULL, NULL); diff --git a/epan/dfilter/scanner.l b/epan/dfilter/scanner.l index 0b80c3d17d..fd9fe19174 100644 --- a/epan/dfilter/scanner.l +++ b/epan/dfilter/scanner.l @@ -153,6 +153,8 @@ hyphen-bytes {hex2}(-{hex2})+ "{" return simple(TOKEN_LBRACE); ".." return simple(TOKEN_DOTDOT); "}" return simple(TOKEN_RBRACE); +"any" return simple(TOKEN_ANY); +"all" return simple(TOKEN_ALL); "==" return test(TOKEN_TEST_ANY_EQ); "eq" return test(TOKEN_TEST_ANY_EQ); diff --git a/epan/dfilter/sttype-test.c b/epan/dfilter/sttype-test.c index faef9f9a43..946951b255 100644 --- a/epan/dfilter/sttype-test.c +++ b/epan/dfilter/sttype-test.c @@ -13,6 +13,7 @@ typedef struct { guint32 magic; test_op_t op; + test_match_t how; stnode_t *val1; stnode_t *val2; } test_t; @@ -30,6 +31,7 @@ test_new(gpointer junk) test->magic = TEST_MAGIC; test->op = TEST_OP_UNINITIALIZED; + test->how = ST_MATCH_DEF; test->val1 = NULL; test->val2 = NULL; @@ -43,7 +45,8 @@ test_dup(gconstpointer data) test_t *test; test = test_new(NULL); - test->op = org->op; + test->op = org->op; + test->how = org->how; test->val1 = stnode_dup(org->val1); test->val2 = stnode_dup(org->val1); @@ -212,6 +215,10 @@ test_todebug(const test_t *test) break; } + if (test->how == ST_MATCH_ALL) + return g_strdup_printf("ALL %s", s); + if (test->how == ST_MATCH_ANY) + return g_strdup_printf("ANY %s", s); return g_strdup(s); } @@ -341,6 +348,22 @@ sttype_test_get(stnode_t *node, test_op_t *p_op, stnode_t **p_val1, stnode_t **p *p_val2 = test->val2; } +void +sttype_test_set_match(stnode_t *node, test_match_t how) +{ + test_t *test = stnode_data(node); + ws_assert_magic(test, TEST_MAGIC); + test->how = how; +} + +test_match_t +sttype_test_get_match(stnode_t *node) +{ + test_t *test = stnode_data(node); + ws_assert_magic(test, TEST_MAGIC); + return test->how; +} + void sttype_register_test(void) { diff --git a/epan/dfilter/sttype-test.h b/epan/dfilter/sttype-test.h index 384e9965bc..5c9121089a 100644 --- a/epan/dfilter/sttype-test.h +++ b/epan/dfilter/sttype-test.h @@ -32,4 +32,10 @@ sttype_test_get_op(stnode_t *node); void sttype_test_get(stnode_t *node, test_op_t *p_op, stnode_t **p_val1, stnode_t **p_val2); +void +sttype_test_set_match(stnode_t *node, test_match_t how); + +test_match_t +sttype_test_get_match(stnode_t *node); + #endif diff --git a/epan/dfilter/syntax-tree.h b/epan/dfilter/syntax-tree.h index 1c5087d933..4f62e08947 100644 --- a/epan/dfilter/syntax-tree.h +++ b/epan/dfilter/syntax-tree.h @@ -65,6 +65,12 @@ typedef enum { TEST_OP_IN } test_op_t; +typedef enum { + ST_MATCH_DEF, + ST_MATCH_ANY, + ST_MATCH_ALL, +} test_match_t; + typedef gpointer (*STTypeNewFunc)(gpointer); typedef gpointer (*STTypeDupFunc)(gconstpointer); typedef void (*STTypeFreeFunc)(gpointer); diff --git a/epan/proto.c b/epan/proto.c index 5f6dfab82d..6cc65e7beb 100644 --- a/epan/proto.c +++ b/epan/proto.c @@ -478,6 +478,8 @@ static const char *reserved_filter_names[] = { "and", "or", "in", + "any", + "all", NULL }; diff --git a/test/suite_dfilter/group_syntax.py b/test/suite_dfilter/group_syntax.py index 9faa5d2120..515b20a1b2 100644 --- a/test/suite_dfilter/group_syntax.py +++ b/test/suite_dfilter/group_syntax.py @@ -311,4 +311,15 @@ class case_field_reference(unittest.TestCase): def test_layer_7(self, checkDFilterCount): dfilter = 'ip.dst#[-5] == 2.2.2.2' checkDFilterCount(dfilter, 1) - + +@fixtures.uses_fixtures +class case_quantifiers(unittest.TestCase): + trace_file = "ipoipoip.pcap" + + def test_any_1(self, checkDFilterCount): + dfilter = 'any ip.addr > 1.1.1.1' + checkDFilterCount(dfilter, 2) + + def test_all_1(self, checkDFilterCount): + dfilter = 'all ip.addr > 1.1.1.1' + checkDFilterCount(dfilter, 1)