Ethereal-dev: Re: [Ethereal-dev] tapping commentary

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Jason House <jhouse@xxxxxxxxx>
Date: Wed, 23 Oct 2002 11:07:56 -0400
Guy Harris wrote:

> > OK.   Can actual field values like the value of "ip.addr" be extracted from
> > edt->tree?
>
> Yes.  Currently, it may be a bit of work, as the routine would have to
> walk the tree itself, but it can be done.

It definitely can be done, below is code of mine to do the physical traversal.  I
tried to make any simple changes from C++ to C.  I left the STL in there, but I
know that with some effort, the STL data structures can be replaced with glib
structures and functions instead.  Hopefully this will save a bit of work for
whoever implements the tap extension to handle filterable fields.

Protocols like OSPF cause trouble in that they can have multiple of the same set
of fields.  The traversal code below attempts to control that by "forgetting" what
was found in a sub tree of a protocol (ie. handle each LSA separately).  I still
have to submit my patch for OSPF that provides filter names as well as
restructuring one small part of the tree output (there is a comment like "should
we put this in a tree?"... for this code the answer is yes).

This allows generation of complete tuples of results for a variety of proto_tree
structures.  There are multiple find_result functions in order to merge the lowest
level of the tree for all protocols together...  allowing things such as ip.len
and udp.srcport to be searched for together, without having "ip.len" be forgotten
before reaching "udp.srcport".  It also allows things such as ip.len" and
"ospf.lsa.type" to be searched for together, and simply return multiple tuples for
a single packet.  The code would work ok with ip.len and ip.flags.df, but not
ip.flags.df and ospf.lsa.type because ip.flags.df would be forgotten before
finding ospf.lsa.type.  I'm sure there is room for plenty of discussion on how to
fix that kind of problem.  If no forgetting is done, then protocols like OSPF will
create trouble (something I really need to work for my applications).

code for finding out which integers to look for to identify a particular field
*****************************************************************
set<int> find_translation(char *field_abbrev){
  set<int> field_id;
  n_symbols = proto_registrar_n();
  for (id = 0; id<n_symbols; id++){
      if (strcmp(field_abbrev, proto_registrar_get_abbrev(id))==0)
        field_id.insert(id);
  }
  return field_id;
}

Actual tree traversal
***********************************************************
typedef map<set<int>, field_info *> tree_scan_result;
struct tree_scan_results{
  tree_scan_result cur_result;
  list<tree_scan_result> *all_results;
};

update_tables(proto_tree *protocol_tree, table_def &tbl, list<set<int> > key_ids,
list<set<int> > value_ids){
  tree_scan_results status;
  status.all_results = new list<tree_scan_result>();
  g_node_children_foreach((GNode*)protocol_tree,G_TRAVERSE_ALL,
find_results_no_forget,(gpointer)&status);
  save_results(*status.all_results,tbl,key_ids,value_ids);
}

/* not forgetting for the list of protocols in the packet */
void find_results_no_forget(GNode *node, gpointer data){
  tree_scan_results &status = *(tree_scan_results*)data; /* copy results */
  if (update_result(status.cur_result,(field_info*)node->data))
    status.all_results->push_back(status.cur_result);
  g_node_children_foreach(node,G_TRAVERSE_ALL,find_results_no_forget2,
(gpointer)&status);
}

/* not fogetting for the list of fields within a protocol */
void find_results_no_forget2(GNode *node, gpointer data){
  tree_scan_results &status = *(tree_scan_results*)data; /* copy results */
  if (update_result(status.cur_result,(field_info*)node->data))
    status.all_results->push_back(status.cur_result);
  g_node_children_foreach(node,G_TRAVERSE_ALL,find_results,  (gpointer)&status);
}

/* allow forgetting for subtrees of a protocol... allow complex items like OSPF to
work */
void find_results(GNode *node, gpointer data){
  tree_scan_results &status = *(tree_scan_results*)data;
  tree_scan_results copied_status;
  if (update_result(status.cur_result,(field_info*)node->data))
    status.all_results->push_back(status.cur_result);
  /* make copy to allow "forgetting" everyting inside of this subtree*/
  copied_status.cur_result = status.cur_result;
  copied_status.all_results = status.all_results;
  g_node_children_foreach(node,G_TRAVERSE_ALL,find_results,
(gpointer)&copied_results);
}

bool update_result(tree_scan_result &x, field_info *fi){
  tree_scan_result::iterator itr;
  bool found_empty = false;
  bool updated_result = false;
  if (fi==NULL)
    return false;
  for (itr = x.begin(); itr != x.end(); itr++){
    //This currently does not work with multiple fields with the same name
    if ((*itr).first.count(fi->hfinfo->id) > 0){
      (*itr).second = fi;
      updated_result = true;
    }
    /* ordering is important!... what we just updated could have been empty
     * ie, if this line was before the if, it would detect an empty that
     * really isn't any more. */
    found_empty |= ((*itr).second==NULL);
  }
  return updated_result && !found_empty;
}