Wireshark-dev: Re: [Wireshark-dev] Undefined reference to proto_tree_model_new
Date: Wed, 20 Jun 2012 08:36:01 -0400
As far as I know, you learn it mostly by doing.  The documents such as they are
give you a general idea, then you pick an existing dissector that's similar to the
one you want to write, and do a lot of copy/paste/modify/delete until it looks like
your protocol.  

Then you come back here for help figuring out why it doesn't work.  ;)

Challenge #1 is getting your development environment set up so that you can build
Wireshark.  Challenge #2 is figuring out which functions you have to implement and
which ones you have to call, simply to get your plugin to work in the first place.

I've attached a simple text file that lists the basic functions you need to know in 
order to write a dissector plugin.  Although it too may be out of date now, as I 
probably wrote it for 1.2....  :P

                                Writing a Wireshark plugin
                                ==========================


Required functions for protocol "foo":  
--------------------------------------

You must create these functions.  The names are arbitrary, however it is strongly recommended that you follow these naming conventions so the code will be readable.

   void proto_register_foo(void)

   void proto_reg_handoff_foo(void)

   void dissect_foo(tvbuff_t*, packet_info*, proto_tree*)



Function calls necessary for Wireshark to use your plugin:
----------------------------------------------------------

   dissector_handle_t   find_dissector(const char *name)
   dissector_handle_t   create_dissector_handle(dissector_t dissector, int proto)

   void   dissector_add(const char *name, guint32 pattern, dissector_handle_t handle)
   int    proto_register_protocol(const char *name, const char *short_name, const char *filt_name)

   void   proto_register_field_array(int parent, hf_register_info *hf, int num_records)
   void   proto_register_subtree_array(gint *const *indices, int num_indices)



Functions you may need when dissecting packet contents:
-------------------------------------------------------

  Display columns:
  ----------------
   gint check_col (column_info *cinfo, gint el)
   void col_clear (column_info *cinfo, gint el)
   void col_set_str (column_info *cinfo, gint el, const gchar *str)
   void col_add_fstr (column_info *cinfo, gint el, const gchar *format,...)

  Processing tvbuff_t:
  --------------------
   guint     tvb_length (tvbuff_t *tvb)
   guint8    tvb_get_guint8 (tvbuff_t *tvb, gint offset)
   guint     tvb_strsize (tvbuff_t *tvb, gint offset)
   guint16   tvb_get_ntohs (tvbuff_t *tvb, gint offset)
   tvbuff_t* tvb_new_subset(tvbuff_t* backing, gint backing_offset, gint backing_length, 
                                                                    gint reported_length)

  Generating display tree:
  ------------------------
   proto_item*  proto_tree_add_item (proto_tree *tree, int hfindex, tvbuff_t *tvb, 
                                       gint start, gint length, gboolean little_endian)
   proto_item*  proto_tree_add_uint(proto_tree *tree, int hfindex, tvbuff_t *tvb, 
                                       gint start, gint length, gint value);
   proto_tree*  proto_item_add_subtree (proto_item *pi, gint idx)
   void         proto_item_append_text (proto_item *pi, const char *format,...)
   proto_item*  proto_item_get_parent(proto_item *ti)
   proto_tree*  proto_item_get_subtree(proto_item *ti)

The length of some items cannot be determined until the item has been
dissected; to add such an item, add it with a length of -1, and, when the
dissection is complete, set the length with 'proto_item_set_len()':

   void         proto_item_set_len(proto_item *ti, gint length);

The "ti" argument is the value returned by the call that added the item
to the tree, and the "length" argument is the length of the item.


Associating a dissector with a parent protocol:
----------------------------------------------
(excerpt from http://www.wireshark.org/lists/wireshark-dev/200608/msg00036.html)

  "If you have a dissector for a protocol that's associated with a particular numerical 
  value of a particular field in the parent protocol (e.g., a particular value of the 
  Ethernet type field), the parent dissector would call register_dissector_table() in 
  its registration routine to create the dissector table, the child dissector would call 
  dissector_add() on that table in its handoff registration routine, and the parent 
  dissector would call dissector_try_port() during the analysis stage.

  "There are similar routines for string values.

  "If you have a dissector for a protocol that can't be associated with particular 
  values in the calling protocol, so you would have to look at the contents of the 
  packet to try to guess what protocol it's for, that's a heuristic dissector. For 
  those, the parent dissector would call register_heur_dissector_list() in its 
  registration routine to create the heuristic dissector table, the child dissector 
  would call heur_dissector_add() on that table in its handoff registration routine, 
  and the parent dissector would call dissector_try_heuristic() during the analysis 
  stage."


  If protocol is identified by (int) parent field:
  ------------------------------------------------
   dissector_table_t register_dissector_table(const char *name, const char *ui_name, 
                                          ftenum_t type, int base)
   gboolean          dissector_try_port(dissector_table_t sub_dissectors, guint32 port, 
                                          tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)

   void              dissector_add(const char *abbrev, guint32 pattern, dissector_handle_t handle)

   Dissector function has prototype:
     void (*dissector_t)(tvbuff_t *, packet_info *, proto_tree *)


  If protocol is identified by heuristic:
  ---------------------------------------
   void     register_heur_dissector_list(const char *name, heur_dissector_list_t *list)
   gboolean dissector_try_heuristic(heur_dissector_list_t sub_dissectors, 
                                          tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)

   void     heur_dissector_add(const char *name, heur_dissector_t dissector, int proto)

   Heuristic dissector function has prototype:
     gboolean (*heur_dissector_t)(tvbuff_t *, packet_info *, proto_tree *)




Reassembling fragmented packets:
--------------------------------

*** 1. Create and initialize tables
        #include <epan/reassemble.h>

        static GHashTable *msg_fragment_table = NULL;
        static GHashTable *msg_reassembled_table = NULL;

        // Call this once? when protocol is registered 
        static void msg_init_protocol(void)
        {
          fragment_table_init(&msg_fragment_table);
          reassembled_table_init(&msg_reassembled_table);
        }

        Also: need to define additional hf globals for use by reassembly code

*** 2. Save fragmented state of packet (?)
        save_fragmented = pinfo->fragmented;

*** 3. Extract fragment info from packet (both for frag and for whole PDU)
        /* protocol-specific */

*** 4. Set fragmented state of packet (?)
        pinfo->fragmented = TRUE;

*** 5. Create vars to hold tvb & frag data
        tvbuff_t* new_tvb = NULL;
        fragment_data *frag_msg = NULL;

*** 6. Call fragment_add_check to add fragment
        fragment_data * fragment_add_check(
                tvbuff_t *tvb,                  // current tvb being dissected 
                int offset,                     // offset where frag starts
                packet_info *pinfo,             // current packet info
                guint32 id,                     // tag for whole message
                GHashTable *fragment_table,     // list of message fragments (must init first)
                GHashTable *reassembled_table,  // list of reassembled messages (must init first)
                guint32 frag_offset,
                guint32 frag_data_len,          // fragment length - to the end 
                gboolean more_frags);           // does this frag go at the end of the message? 

e.g.:   frag_msg = fragment_add_check(tvb, offset, pinfo, msg_seqid, 
                                      msg_fragment_table, msg_reassembled_table, 
                                      msg_ofst, tvb_length_remaining(tvb, offset), isLastFrag);

   If fragment_add_check returns NULL, then we're not done yet.
   If frag_msg != NULL, then reassembly is complete.

*** 7. Call process_reassembled_data to create new tvb (if done)
        tvbuff_t * process_reassembled_data(
                tvbuff_t *tvb,                  // current tvb being dissected 
                int offset,                     // offset where frag starts
                packet_info *pinfo,             // current packet info
                const char *name, 
                fragment_data *fd_head, 
                const fragment_items *fit,
                gboolean *update_col_infop, 
                proto_tree *tree);

e.g.:   new_tvb = process_reassembled_data(tvb, offset, pinfo,
                        "Reassembled Message", frag_msg, &msg_frag_items, NULL, msg_tree);


*** 8. Indicate status in col info
        if (frag_msg)    /* Reassembled */
        { 
          if (check_col(pinfo->cinfo, COL_INFO))
            col_append_str(pinfo->cinfo, COL_INFO, " (Message Reassembled)");
        } 
        else             /* Not last packet of reassembled Message */
        { 
          if (check_col(pinfo->cinfo, COL_INFO))
            col_append_fstr(pinfo->cinfo, COL_INFO, " (Msg frag %x)", msg_ofst);
        }


*** 9. If message is complete, process it now
        if (new_tvb)     
        { 
          /* process_reassembled_data returned full packet, process it now */
          process_packet(new_tvb,...)
        } 
        else             
        { 
          /* make a new subset (?) */
          next_tvb = tvb_new_subset(tvb, offset, -1, -1);
        }

** 10. Restore packet's original fragmented state
        pinfo->fragmented = save_fragmented;






Troubleshooting:
================

1. If you get a Runtime Error from MSVC when you start Wireshark: 
     a. try building 'distclean' and then rebuilding Wireshark.
     b. make sure your protocol's parent creates the table/list you pass to dissector_add.
     c. make sure each element in your hf array matches the corresponding types.