forked from osmocom/wireshark
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.pespin/osmux-wip
parent
c0170dad42
commit
4f3f507eee
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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).
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ typedef enum {
|
|||
STTYPE_FIELD,
|
||||
STTYPE_FVALUE,
|
||||
STTYPE_SLICE,
|
||||
STTYPE_LAYER,
|
||||
STTYPE_RANGE_NODE,
|
||||
STTYPE_FUNCTION,
|
||||
STTYPE_SET,
|
||||
|
|
Binary file not shown.
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue