dfilter: Add syntax to match specific layers in the protocol stack

Add support to display filters for matching a specific layer within a frame.
Layers are counted sequentially up the protocol stack. Each protocol
(dissector) that appears in the stack is one layer.

LINK-LAYER#1 <-> IP#1 <-> TCP#1 <-> IP#2 <-> TCP#2 <-> etc.

The syntax allows for negative indexes and ranges with the usual semantics
for slices (but note that counting starts at one):

    tcp.port#[2-4] == 1024

Matches layers 2 to 4 inclusive.

Fixes #3791.
This commit is contained in:
João Valverde 2022-04-09 23:03:40 +01:00 committed by A Wireshark GitLab Utility
parent c0170dad42
commit 4f3f507eee
15 changed files with 459 additions and 67 deletions

View File

@ -322,6 +322,24 @@ Slices can be combined. You can concatenate them using the comma operator:
This concatenates offset 1, offsets 3-5, and offset 9 to the end of the ftp
data.
=== The layer operator
A field can be restricted to a certain layer in the protocol stack using the
layer operator (#), followed by a decimal number:
ip.addr#2 == 192.168.30.40
matches only the inner (second) layer in the packet. Layers use simple stacking
semantics and protocol layers are counted sequentially, so tcp#2 is the layer
in the stack somewhere above tcp#1.
For more complicates ranges the same syntax used with slices is valid:
tcp.port#[2-4]
means layers number 2, 3 or 4 inclusive. The hash symbol is required to
distinguish a layer range from a slice.
=== The membership operator
A field may be checked for matches against a set of values simply with the

View File

@ -70,6 +70,8 @@ They previously shipped with Npcap 1.55.
** Adds new display filter functions max(), min() and abs().
** Functions can accept expressions as arguments, including other functions. Previously only protocol
fields and slices were syntactically valid function arguments.
** New syntax to match a specific layer in the protocol stack. For example ip.addr#2 == 1.1.1.1
matches only the inner layer in an IP-over-IP packet.
* text2pcap and "Import from Hex Dump":
** text2pcap supports writing the output file in all the capture file formats

View File

@ -24,9 +24,11 @@ dfvm_opcode_tostr(dfvm_opcode_t code)
case IF_TRUE_GOTO: return "IF_TRUE_GOTO";
case IF_FALSE_GOTO: return "IF_FALSE_GOTO";
case CHECK_EXISTS: return "CHECK_EXISTS";
case CHECK_EXISTS_R: return "CHECK_EXISTS_R";
case NOT: return "NOT";
case RETURN: return "RETURN";
case READ_TREE: return "READ_TREE";
case READ_TREE_R: return "READ_TREE_R";
case READ_REFERENCE: return "READ_REFERENCE";
case PUT_FVALUE: return "PUT_FVALUE";
case ALL_EQ: return "ALL_EQ";
@ -293,11 +295,21 @@ dfvm_dump_str(wmem_allocator_t *alloc, dfilter_t *df, gboolean print_references)
id, arg1_str);
break;
case CHECK_EXISTS_R:
wmem_strbuf_append_printf(buf, "%05d CHECK_EXISTS\t%s#[%s]\n",
id, arg1_str, arg2_str);
break;
case READ_TREE:
wmem_strbuf_append_printf(buf, "%05d READ_TREE\t\t%s -> %s\n",
id, arg1_str, arg2_str);
break;
case READ_TREE_R:
wmem_strbuf_append_printf(buf, "%05d READ_TREE\t\t%s#[%s] -> %s\n",
id, arg1_str, arg3_str, arg2_str);
break;
case READ_REFERENCE:
wmem_strbuf_append_printf(buf, "%05d READ_REFERENCE\t${%s} -> %s\n",
id, arg1_str, arg2_str);
@ -498,21 +510,98 @@ dfvm_dump(FILE *f, dfilter_t *df)
wmem_free(NULL, str);
}
static int
compare_finfo_layer(gconstpointer _a, gconstpointer _b)
{
const field_info *a = *(const field_info **)_a;
const field_info *b = *(const field_info **)_b;
return a->proto_layer_num - b->proto_layer_num;
}
static gboolean
drange_contains_layer(drange_t *dr, int num, int length)
{
drange_node *rn;
GSList *list = dr->range_list;
int lower, upper;
while (list) {
rn = list->data;
lower = rn->start_offset;
if (lower < 0) {
lower += length + 1;
}
if (rn->ending == DRANGE_NODE_END_T_LENGTH) {
upper = lower + rn->length - 1;
}
else if (rn->ending == DRANGE_NODE_END_T_OFFSET) {
upper = rn->end_offset;
}
else if (rn->ending == DRANGE_NODE_END_T_TO_THE_END) {
upper = INT_MAX;
}
if (num >= lower && num <= upper) { /* inclusive */
return TRUE;
}
list = g_slist_next(list);
}
return FALSE;
}
GSList *
filter_finfo_fvalues(GSList *fvalues, GPtrArray *finfos, drange_t *range)
{
int length; /* maximum proto layer number. The numbers are sequential. */
field_info *last_finfo, *finfo;
int cookie = -1;
gboolean cookie_matches;
int layer;
g_ptr_array_sort(finfos, compare_finfo_layer);
last_finfo = finfos->pdata[finfos->len - 1];
length = last_finfo->proto_layer_num;
for (guint i = 0; i < finfos->len; i++) {
finfo = finfos->pdata[i];
layer = finfo->proto_layer_num;
if (cookie == layer) {
if (cookie_matches) {
fvalues = g_slist_prepend(fvalues, &finfo->value);
}
}
else {
cookie = layer;
cookie_matches = drange_contains_layer(range, layer, length);
if (cookie_matches) {
fvalues = g_slist_prepend(fvalues, &finfo->value);
}
}
}
return fvalues;
}
/* Reads a field from the proto_tree and loads the fvalues into a register,
* if that field has not already been read. */
static gboolean
read_tree(dfilter_t *df, proto_tree *tree,
dfvm_value_t *arg1, dfvm_value_t *arg2)
dfvm_value_t *arg1, dfvm_value_t *arg2,
dfvm_value_t *arg3)
{
GPtrArray *finfos;
field_info *finfo;
int i, len;
GSList *fvalues = NULL;
gboolean found_something = FALSE;
drange_t *range = NULL;
header_field_info *hfinfo = arg1->value.hfinfo;
int reg = arg2->value.numeric;
if (arg3) {
range = arg3->value.drange;
}
/* Already loaded in this run of the dfilter? */
if (df->attempted_load[reg]) {
if (df->registers[reg]) {
@ -531,20 +620,22 @@ read_tree(dfilter_t *df, proto_tree *tree,
hfinfo = hfinfo->same_name_next;
continue;
}
else {
found_something = TRUE;
}
len = finfos->len;
for (i = 0; i < len; i++) {
finfo = g_ptr_array_index(finfos, i);
fvalues = g_slist_prepend(fvalues, &finfo->value);
if (range) {
fvalues = filter_finfo_fvalues(fvalues, finfos, range);
}
else {
len = finfos->len;
for (i = 0; i < len; i++) {
finfo = g_ptr_array_index(finfos, i);
fvalues = g_slist_prepend(fvalues, &finfo->value);
}
}
hfinfo = hfinfo->same_name_next;
}
if (!found_something) {
if (fvalues == NULL) {
return FALSE;
}
@ -1038,6 +1129,42 @@ stack_pop(dfilter_t *df, dfvm_value_t *arg1)
}
}
static gboolean
check_exists(proto_tree *tree, dfvm_value_t *arg1, dfvm_value_t *arg2)
{
GPtrArray *finfos;
header_field_info *hfinfo;
drange_t *range = NULL;
gboolean exists;
GSList *fvalues;
hfinfo = arg1->value.hfinfo;
if (arg2)
range = arg2->value.drange;
while (hfinfo) {
finfos = proto_get_finfo_ptr_array(tree, hfinfo->id);
if ((finfos == NULL) || (g_ptr_array_len(finfos) == 0)) {
hfinfo = hfinfo->same_name_next;
continue;
}
if (range == NULL) {
return TRUE;
}
fvalues = filter_finfo_fvalues(NULL, finfos, range);
exists = (fvalues != NULL);
g_slist_free(fvalues);
if (exists) {
return TRUE;
}
hfinfo = hfinfo->same_name_next;
}
return FALSE;
}
gboolean
dfvm_apply(dfilter_t *df, proto_tree *tree)
{
@ -1047,7 +1174,6 @@ dfvm_apply(dfilter_t *df, proto_tree *tree)
dfvm_value_t *arg1;
dfvm_value_t *arg2;
dfvm_value_t *arg3 = NULL;
header_field_info *hfinfo;
ws_assert(tree);
@ -1065,21 +1191,19 @@ dfvm_apply(dfilter_t *df, proto_tree *tree)
switch (insn->op) {
case CHECK_EXISTS:
hfinfo = arg1->value.hfinfo;
while(hfinfo) {
accum = proto_check_for_protocol_or_field(tree,
hfinfo->id);
if (accum) {
break;
}
else {
hfinfo = hfinfo->same_name_next;
}
}
accum = check_exists(tree, arg1, NULL);
break;
case CHECK_EXISTS_R:
accum = check_exists(tree, arg1, arg2);
break;
case READ_TREE:
accum = read_tree(df, tree, arg1, arg2);
accum = read_tree(df, tree, arg1, arg2, NULL);
break;
case READ_TREE_R:
accum = read_tree(df, tree, arg1, arg2, arg3);
break;
case READ_REFERENCE:

View File

@ -50,9 +50,11 @@ typedef enum {
IF_TRUE_GOTO,
IF_FALSE_GOTO,
CHECK_EXISTS,
CHECK_EXISTS_R,
NOT,
RETURN,
READ_TREE,
READ_TREE_R,
READ_REFERENCE,
PUT_FVALUE,
ALL_EQ,

View File

@ -73,11 +73,12 @@ dfw_append_jump(dfwork_t *dfw)
/* returns register number */
static dfvm_value_t *
dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo)
dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo,
drange_t *range)
{
dfvm_insn_t *insn;
int reg = -1;
dfvm_value_t *reg_val, *val1;
dfvm_value_t *reg_val, *val1, *val3;
gboolean added_new_hfinfo = FALSE;
void *loaded_key;
@ -89,15 +90,21 @@ dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo)
/* Keep track of which registers
* were used for which hfinfo's so that we
* can re-use registers. */
/* Re-use only if we are not using a range (layer filter). */
loaded_key = g_hash_table_lookup(dfw->loaded_fields, hfinfo);
if (loaded_key != NULL) {
/*
* Reg's are stored in has as reg+1, so
* that the non-existence of a hfinfo in
* the hash, or 0, can be differentiated from
* a hfinfo being loaded into register #0.
*/
reg = GPOINTER_TO_INT(loaded_key) - 1;
if (range == NULL) {
/*
* Reg's are stored in has as reg+1, so
* that the non-existence of a hfinfo in
* the hash, or 0, can be differentiated from
* a hfinfo being loaded into register #0.
*/
reg = GPOINTER_TO_INT(loaded_key) - 1;
}
else {
reg = dfw->next_register++;
}
}
else {
reg = dfw->next_register++;
@ -107,11 +114,19 @@ dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo)
added_new_hfinfo = TRUE;
}
insn = dfvm_insn_new(READ_TREE);
val1 = dfvm_value_new_hfinfo(hfinfo);
insn->arg1 = dfvm_value_ref(val1);
reg_val = dfvm_value_new_register(reg);
if (range) {
val3 = dfvm_value_new_drange(range);
insn = dfvm_insn_new(READ_TREE_R);
}
else {
val3 = NULL;
insn = dfvm_insn_new(READ_TREE);
}
insn->arg1 = dfvm_value_ref(val1);
insn->arg2 = dfvm_value_ref(reg_val);
insn->arg3 = dfvm_value_ref(val3);
dfw_append_insn(dfw, insn);
if (added_new_hfinfo) {
@ -189,7 +204,7 @@ dfw_append_mk_slice(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
insn->arg1 = dfvm_value_ref(val1);
reg_val = dfvm_value_new_register(dfw->next_register++);
insn->arg2 = dfvm_value_ref(reg_val);
val3 = dfvm_value_new_drange(sttype_range_drange(node));
val3 = dfvm_value_new_drange(sttype_range_drange_steal(node));
insn->arg3 = dfvm_value_ref(val3);
sttype_range_remove_drange(node);
dfw_append_insn(dfw, insn);
@ -439,7 +454,7 @@ gen_entity(dfwork_t *dfw, stnode_t *st_arg, GSList **jumps_ptr)
if (e_type == STTYPE_FIELD) {
hfinfo = stnode_data(st_arg);
val = dfw_append_read_tree(dfw, hfinfo);
val = dfw_append_read_tree(dfw, hfinfo, NULL);
*jumps_ptr = g_slist_prepend(*jumps_ptr, dfw_append_jump(dfw));
}
else if (e_type == STTYPE_REFERENCE) {
@ -447,6 +462,12 @@ gen_entity(dfwork_t *dfw, stnode_t *st_arg, GSList **jumps_ptr)
val = dfw_append_read_reference(dfw, hfinfo);
*jumps_ptr = g_slist_prepend(*jumps_ptr, dfw_append_jump(dfw));
}
else if (e_type == STTYPE_LAYER) {
stnode_t *entity = sttype_range_entity(st_arg);
drange_t *range = sttype_range_drange_steal(st_arg);
val = dfw_append_read_tree(dfw, stnode_data(entity), range);
*jumps_ptr = g_slist_prepend(*jumps_ptr, dfw_append_jump(dfw));
}
else if (e_type == STTYPE_FVALUE) {
val = dfvm_value_new_fvalue(stnode_steal_data(st_arg));
}
@ -473,16 +494,40 @@ static void
gen_exists(dfwork_t *dfw, stnode_t *st_node)
{
dfvm_insn_t *insn;
dfvm_value_t *val1, *val2 = NULL;
header_field_info *hfinfo;
stnode_t *st_arg;
drange_t *range;
hfinfo = stnode_data(st_node);
if (stnode_type_id(st_node) == STTYPE_LAYER) {
st_arg = sttype_range_entity(st_node);
range = sttype_range_drange_steal(st_node);
}
else {
st_arg = st_node;
range = NULL;
}
hfinfo = stnode_data(st_arg);
/* Rewind to find the first field of this name. */
while (hfinfo->same_name_prev_id != -1) {
hfinfo = proto_registrar_get_nth(hfinfo->same_name_prev_id);
}
insn = dfvm_insn_new(CHECK_EXISTS);
insn->arg1 = dfvm_value_new_hfinfo(hfinfo);
val1 = dfvm_value_new_hfinfo(hfinfo);
if (range) {
val2 = dfvm_value_new_drange(range);
}
if (val2) {
insn = dfvm_insn_new(CHECK_EXISTS_R);
insn->arg1 = dfvm_value_ref(val1);
insn->arg2 = dfvm_value_ref(val2);
}
else {
insn = dfvm_insn_new(CHECK_EXISTS);
insn->arg1 = dfvm_value_ref(val1);
}
dfw_append_insn(dfw, insn);
/* Record the FIELD_ID in hash of interesting fields. */
@ -621,6 +666,7 @@ gencode(dfwork_t *dfw, stnode_t *st_node)
gen_test(dfw, st_node);
break;
case STTYPE_FIELD:
case STTYPE_LAYER:
gen_exists(dfw, st_node);
break;
case STTYPE_ARITHMETIC:

View File

@ -121,9 +121,28 @@ atom(A) ::= LITERAL(S). { A = S; }
atom(A) ::= FIELD(F). { A = F; }
atom(A) ::= REFERENCE(F). { A = F; }
layer(R) ::= atom(F) HASH LBRACKET range_node_list(L) RBRACKET.
{
R = stnode_new(STTYPE_LAYER, NULL, NULL, NULL);
sttype_range_set(R, F, L);
g_slist_free(L);
}
layer(R) ::= atom(F) HASH INTEGER(N).
{
R = stnode_new(STTYPE_LAYER, NULL, NULL, stnode_location(F));
char *err_msg = sttype_range_set_number(R, F, stnode_token(N));
if (err_msg != NULL) {
FAIL(dfw, N, "%s", err_msg);
g_free(err_msg);
}
stnode_free(N);
}
entity(E) ::= atom(A). { E = A; }
entity(E) ::= slice(R). { E = R; }
entity(E) ::= function(F). { E = F; }
entity(E) ::= layer(L). { E = L; }
arithmetic_expr(T) ::= entity(N).
{

View File

@ -136,6 +136,7 @@ dot-bytes {hex2}(\.{hex2})+
hyphen-bytes {hex2}(-{hex2})+
%x RANGE
%x LAYER
%x DQUOTE
%x SQUOTE
%x REFERENCE
@ -194,7 +195,24 @@ hyphen-bytes {hex2}(-{hex2})+
"&" return math(TOKEN_BITWISE_AND);
"bitwise_and" return math(TOKEN_BITWISE_AND);
"[" {
"#" {
BEGIN(LAYER);
return simple(TOKEN_HASH);
}
<LAYER>[[:digit:]]+ {
BEGIN(INITIAL);
update_location(yyextra, yytext);
return set_lval_simple(yyextra, TOKEN_INTEGER, yytext, STTYPE_UNINITIALIZED);
}
<LAYER>[^[:digit:][] {
update_location(yyextra, yytext);
FAIL("Expected digit or \"[\", not \"%s\"", yytext);
return SCAN_FAILED;
}
<INITIAL,LAYER>"[" {
BEGIN(RANGE);
return simple(TOKEN_LBRACKET);
}

View File

@ -19,6 +19,7 @@
#include "sttype-test.h"
#include "sttype-set.h"
#include "sttype-function.h"
#include "sttype-pointer.h"
#include <epan/exceptions.h>
#include <epan/packet.h>
@ -506,6 +507,7 @@ check_exists(dfwork_t *dfw, stnode_t *st_arg1)
switch (stnode_type_id(st_arg1)) {
case STTYPE_FIELD:
case STTYPE_ARITHMETIC:
case STTYPE_LAYER:
/* This is OK */
break;
case STTYPE_REFERENCE:
@ -544,6 +546,17 @@ check_exists(dfwork_t *dfw, stnode_t *st_arg1)
}
}
static void
check_exists_layer(dfwork_t *dfw, stnode_t *st_arg1)
{
stnode_t *entity;
entity = sttype_range_entity(st_arg1);
dfw_resolve_unparsed(dfw, entity);
check_exists(dfw, entity);
/* Nothing to do here? */
}
static void
check_slice_sanity(dfwork_t *dfw, stnode_t *st, ftenum_t lhs_ftype)
{
@ -581,6 +594,52 @@ check_slice_sanity(dfwork_t *dfw, stnode_t *st, ftenum_t lhs_ftype)
}
}
static ftenum_t
check_layer_sanity(dfwork_t *dfw, stnode_t *st)
{
stnode_t *entity1;
sttype_id_t e_type;
LOG_NODE(st);
entity1 = sttype_range_entity(st);
ws_assert(entity1);
dfw_resolve_unparsed(dfw, entity1);
e_type = stnode_type_id(entity1);
if (e_type == STTYPE_FIELD) {
return sttype_pointer_ftenum(entity1);
}
else if (e_type == STTYPE_REFERENCE) {
/* TODO: Implement layers with references. */
FAIL(dfw, entity1, "The # operator is not valid with a reference");
}
else {
FAIL(dfw, entity1, "%s is not a valid field value", stnode_todisplay(entity1));
}
}
#define IS_FIELD_ENTITY(ft) \
((ft) == STTYPE_FIELD || \
(ft) == STTYPE_REFERENCE || \
(ft) == STTYPE_LAYER)
static ftenum_t
field_ftenum(stnode_t *st)
{
sttype_id_t e_type;
e_type = stnode_type_id(st);
if (e_type == STTYPE_FIELD || e_type == STTYPE_REFERENCE)
return sttype_pointer_ftenum(st);
else if (e_type == STTYPE_LAYER) {
return sttype_pointer_ftenum(sttype_range_entity(st));
}
else {
ws_assert_not_reached();
}
}
static void
convert_to_bytes(stnode_t *arg)
{
@ -648,7 +707,7 @@ check_relation_LHS_FIELD(dfwork_t *dfw, test_op_t st_op,
stnode_t *st_arg1, stnode_t *st_arg2)
{
sttype_id_t type2;
header_field_info *hfinfo1, *hfinfo2;
header_field_info *hfinfo1;
ftenum_t ftype1, ftype2;
fvalue_t *fvalue;
@ -657,6 +716,8 @@ check_relation_LHS_FIELD(dfwork_t *dfw, test_op_t st_op,
again:
type2 = stnode_type_id(st_arg2);
ws_assert(stnode_type_id(st_arg1) == STTYPE_FIELD ||
stnode_type_id(st_arg1) == STTYPE_REFERENCE);
hfinfo1 = stnode_data(st_arg1);
ftype1 = hfinfo1->type;
@ -666,19 +727,18 @@ again:
stnode_todisplay(st_node));
}
if (type2 == STTYPE_FIELD || type2 == STTYPE_REFERENCE) {
hfinfo2 = stnode_data(st_arg2);
ftype2 = hfinfo2->type;
if (IS_FIELD_ENTITY(type2)) {
ftype2 = field_ftenum(st_arg2);
if (!compatible_ftypes(ftype1, ftype2)) {
FAIL(dfw, st_arg2, "%s and %s are not of compatible types.",
hfinfo1->abbrev, hfinfo2->abbrev);
hfinfo1->abbrev, stnode_todisplay(st_arg2));
}
/* Do this check even though you'd think that if
* they're compatible, then can_func() would pass. */
if (!can_func(ftype2)) {
FAIL(dfw, st_arg2, "%s (type=%s) cannot participate in specified comparison.",
hfinfo2->abbrev, ftype_pretty_name(ftype2));
stnode_todisplay(st_arg2), ftype_pretty_name(ftype2));
}
}
else if (type2 == STTYPE_STRING || type2 == STTYPE_LITERAL || type2 == STTYPE_UNPARSED) {
@ -757,6 +817,21 @@ again:
}
}
static void
check_relation_LHS_LAYER(dfwork_t *dfw, test_op_t st_op,
FtypeCanFunc can_func,
gboolean allow_partial_value,
stnode_t *st_node,
stnode_t *st_arg1, stnode_t *st_arg2)
{
stnode_t *entity;
check_layer_sanity(dfw, st_arg1);
entity = sttype_range_entity(st_arg1);
ws_assert(stnode_type_id(entity) == STTYPE_FIELD);
check_relation_LHS_FIELD(dfw, st_op, can_func, allow_partial_value, st_node, entity, st_arg2);
}
static void
check_relation_LHS_SLICE(dfwork_t *dfw, test_op_t st_op,
FtypeCanFunc can_func _U_,
@ -765,7 +840,6 @@ check_relation_LHS_SLICE(dfwork_t *dfw, test_op_t st_op,
stnode_t *st_arg1, stnode_t *st_arg2)
{
sttype_id_t type2;
header_field_info *hfinfo2;
ftenum_t ftype2;
fvalue_t *fvalue;
@ -776,14 +850,13 @@ check_relation_LHS_SLICE(dfwork_t *dfw, test_op_t st_op,
again:
type2 = stnode_type_id(st_arg2);
if (type2 == STTYPE_FIELD || type2 == STTYPE_REFERENCE) {
hfinfo2 = stnode_data(st_arg2);
ftype2 = hfinfo2->type;
if (IS_FIELD_ENTITY(type2)) {
ftype2 = field_ftenum(st_arg2);
if (!is_bytes_type(ftype2)) {
if (!ftype_can_slice(ftype2)) {
FAIL(dfw, st_arg2, "\"%s\" is a %s and cannot be converted into a sequence of bytes.",
hfinfo2->abbrev,
stnode_todisplay(st_arg2),
ftype_pretty_name(ftype2));
}
@ -858,7 +931,6 @@ check_relation_LHS_FUNCTION(dfwork_t *dfw, test_op_t st_op,
stnode_t *st_arg1, stnode_t *st_arg2)
{
sttype_id_t type2;
header_field_info *hfinfo2;
ftenum_t ftype1, ftype2;
fvalue_t *fvalue;
@ -875,19 +947,18 @@ check_relation_LHS_FUNCTION(dfwork_t *dfw, test_op_t st_op,
again:
type2 = stnode_type_id(st_arg2);
if (type2 == STTYPE_FIELD || type2 == STTYPE_REFERENCE) {
hfinfo2 = stnode_data(st_arg2);
ftype2 = hfinfo2->type;
if (IS_FIELD_ENTITY(type2)) {
ftype2 = field_ftenum(st_arg2);
if (!compatible_ftypes(ftype1, ftype2)) {
FAIL(dfw, st_arg2, "Function %s and %s are not of compatible types.",
sttype_function_name(st_arg2), hfinfo2->abbrev);
sttype_function_name(st_arg2), stnode_todisplay(st_arg2));
}
/* Do this check even though you'd think that if
* they're compatible, then can_func() would pass. */
if (!can_func(ftype2)) {
FAIL(dfw, st_arg2, "%s (type=%s) cannot participate in specified comparison.",
hfinfo2->abbrev, ftype_pretty_name(ftype2));
stnode_todisplay(st_arg2), ftype_pretty_name(ftype2));
}
}
else if (type2 == STTYPE_STRING) {
@ -974,7 +1045,7 @@ check_relation_LHS_ARITHMETIC(dfwork_t *dfw, test_op_t st_op _U_,
sttype_test_get(st_arg1, NULL, &entity, NULL);
entity_type = stnode_type_id(entity);
if (entity_type == STTYPE_FIELD || entity_type == STTYPE_REFERENCE) {
if (IS_FIELD_ENTITY(entity_type)) {
check_relation_LHS_FIELD(dfw, st_op, can_func, allow_partial_value, st_node, entity, st_arg2);
}
else if (entity_type == STTYPE_FUNCTION) {
@ -1007,6 +1078,10 @@ check_relation(dfwork_t *dfw, test_op_t st_op,
check_relation_LHS_FIELD(dfw, st_op, can_func,
allow_partial_value, st_node, st_arg1, st_arg2);
break;
case STTYPE_LAYER:
check_relation_LHS_LAYER(dfw, st_op, can_func,
allow_partial_value, st_node, st_arg1, st_arg2);
break;
case STTYPE_SLICE:
check_relation_LHS_SLICE(dfw, st_op, can_func,
allow_partial_value, st_node, st_arg1, st_arg2);
@ -1020,7 +1095,7 @@ check_relation(dfwork_t *dfw, test_op_t st_op,
allow_partial_value, st_node, st_arg1, st_arg2);
break;
default:
FAIL(dfw, st_arg1, "Left side of %s expression must be a field or function, not %s.",
FAIL(dfw, st_arg1, "Left side of \"%s\" expression must be a field or function, not %s.",
stnode_todisplay(st_node), stnode_todisplay(st_arg1));
}
}
@ -1344,6 +1419,9 @@ semcheck(dfwork_t *dfw, stnode_t *st_node)
case STTYPE_ARITHMETIC:
check_arithmetic_expr(dfw, st_node, FT_NONE);
break;
case STTYPE_LAYER:
check_exists_layer(dfw, st_node);
break;
default:
check_exists(dfw, st_node);
}

View File

@ -115,6 +115,7 @@ sttype_pointer_ftenum(stnode_t *node)
{
switch (node->type->id) {
case STTYPE_FIELD:
case STTYPE_REFERENCE:
return ((header_field_info *)node->data)->type;
case STTYPE_FVALUE:
return fvalue_type_ftenum(node->data);

View File

@ -73,7 +73,7 @@ range_free(gpointer value)
}
static char *
range_tostr(const void *data, gboolean pretty)
slice_tostr(const void *data, gboolean pretty)
{
const range_t *range = data;
ws_assert_magic(range, RANGE_MAGIC);
@ -89,6 +89,23 @@ range_tostr(const void *data, gboolean pretty)
return repr;
}
static char *
layer_tostr(const void *data, gboolean pretty)
{
const range_t *range = data;
ws_assert_magic(range, RANGE_MAGIC);
char *repr, *drange_str;
drange_str = drange_tostr(range->drange);
repr = ws_strdup_printf("%s#[%s]",
stnode_tostr(range->entity, pretty),
drange_str);
g_free(drange_str);
return repr;
}
void
sttype_range_remove_drange(stnode_t *node)
{
@ -100,6 +117,18 @@ sttype_range_remove_drange(stnode_t *node)
range->drange = NULL;
}
drange_t *
sttype_range_drange_steal(stnode_t *node)
{
range_t *range;
drange_t *dr;
range = stnode_data(node);
ws_assert_magic(range, RANGE_MAGIC);
dr = range->drange;
range->drange = NULL;
return dr;
}
/* Set a range */
void
@ -121,6 +150,17 @@ sttype_range_set1(stnode_t *node, stnode_t *entity, drange_node *rn)
sttype_range_set(node, entity, g_slist_append(NULL, rn));
}
char *
sttype_range_set_number(stnode_t *node, stnode_t *entity, const char *number_str)
{
char *err_msg = NULL;
drange_node *rn = drange_node_from_str(number_str, &err_msg);
if (err_msg != NULL)
return err_msg;
sttype_range_set1(node, entity, rn);
return NULL;
}
stnode_t *
sttype_range_entity(stnode_t *node)
{
@ -146,10 +186,19 @@ sttype_register_range(void)
range_new,
range_free,
range_dup,
range_tostr
slice_tostr
};
static sttype_t layer_type = {
STTYPE_LAYER,
"LAYER",
range_new,
range_free,
range_dup,
layer_tostr
};
sttype_register(&slice_type);
sttype_register(&layer_type);
}
/*

View File

@ -21,6 +21,9 @@ sttype_range_entity(stnode_t *node);
drange_t *
sttype_range_drange(stnode_t *node);
drange_t *
sttype_range_drange_steal(stnode_t *node);
/* Set a range */
void
sttype_range_set(stnode_t *node, stnode_t *field, GSList* drange_list);
@ -28,6 +31,9 @@ sttype_range_set(stnode_t *node, stnode_t *field, GSList* drange_list);
void
sttype_range_set1(stnode_t *node, stnode_t *field, drange_node *rn);
char *
sttype_range_set_number(stnode_t *node, stnode_t *entity, const char *number_str);
/* Clear the 'drange' variable to remove responsibility for
* freeing it. */
void

View File

@ -273,12 +273,7 @@ stnode_tostr(stnode_t *node, gboolean pretty)
if (pretty && node->repr_token != NULL) {
g_free(node->repr_display);
if (stnode_type_id(node) == STTYPE_CHARCONST) {
node->repr_display = g_strdup(node->repr_token);
}
else {
node->repr_display = ws_strdup_printf("\"%s\"", node->repr_token);
}
node->repr_display = g_strdup(node->repr_token);
return node->repr_display;
}

View File

@ -31,6 +31,7 @@ typedef enum {
STTYPE_FIELD,
STTYPE_FVALUE,
STTYPE_SLICE,
STTYPE_LAYER,
STTYPE_RANGE_NODE,
STTYPE_FUNCTION,
STTYPE_SET,

BIN
test/captures/ipoipoip.pcap Normal file

Binary file not shown.

View File

@ -279,3 +279,36 @@ class case_field_reference(unittest.TestCase):
dfilter = 'frame.number < ${frame.number}'
# select frame 3, expect 2 frames out of 4.
checkDFilterCountWithSelectedFrame(dfilter, 2, 3)
@fixtures.uses_fixtures
class case_field_reference(unittest.TestCase):
trace_file = "ipoipoip.pcap"
def test_layer_1(self, checkDFilterCount):
dfilter = 'ip.addr#2 == 4.4.4.4'
checkDFilterCount(dfilter, 1)
def test_layer_2(self, checkDFilterCount):
dfilter = 'ip.addr#5'
checkDFilterCount(dfilter, 1)
def test_layer_3(self, checkDFilterCount):
dfilter = 'ip.addr#6'
checkDFilterCount(dfilter, 0)
def test_layer_4(self, checkDFilterCount):
dfilter = 'ip.dst#[2-4] == 8.8.8.8'
checkDFilterCount(dfilter, 1)
def test_layer_5(self, checkDFilterCount):
dfilter = 'ip.dst#[-1] == 8.8.8.8'
checkDFilterCount(dfilter, 0)
def test_layer_6(self, checkDFilterCount):
dfilter = 'ip.dst#[-1] == 9.9.9.9'
checkDFilterCount(dfilter, 1)
def test_layer_7(self, checkDFilterCount):
dfilter = 'ip.dst#[-5] == 2.2.2.2'
checkDFilterCount(dfilter, 1)