Huge thanks to our Platinum Members Endace and LiveAction,
and our Silver Member Veeam, for supporting the Wireshark Foundation and project.

Wireshark-dev: Re: [Wireshark-dev] roofnet v1

From: Nicola Arnoldi <nicola.arnoldi@xxxxxxxxxxxx>
Date: Sat, 13 Jan 2007 10:18:24 +0100
In order for you not to start from scratch, I send you the dissector I tried to
write, even if it does not work and is sub-optimal.

Nicola


Scrive Sebastien Tandel <sebastien@xxxxxxxxx>:

> Hi,
>
> I will try to do it this WE.
>
>
> Regards,
>
> Sebastien Tandel
>
> Nicola Arnoldi wrote:
> > Hi all.
> > I noticed that the version 1 of the roofnet header has got additional
> > fields, as one might understand from the srpacket.hh file i posted
> > some email ago.
> >
> > In particular, there are 6 and a half more lines in the header than in
> > version 2.
> >
> > Can you modify the dissector in order to make it able to decode both
> > v1 and v2 packets?
> >
> > Thanks
> >
> > Nicola
> >
> > Il giorno 10/gen/07, alle ore 16:48, Nicola Arnoldi ha scritto:
> >
> >> Was the pcap file enough?
> >>
> >> Bye
> >>
> >> Nicola
> >>
> >> Il giorno mar, 09/01/2007 alle 12.34 +0100, Sebastien Tandel ha scritto:
> >>> Hi,
> >>>
> >>>    Can you send a pcap file for testing purpose, please?
> >>>
> >>>
> >>> Regards,
> >>>
> >>> Sebastien Tandel
> >>>
> >>> Nicola Arnoldi wrote:
> >>>> Hi everybody.
> >>>> The great work Sebastien did about Roofnet dissectors was related to
> >>>> Version 2, which is still far from stable.
> >>>> It should be implemented the V 1 as well, which has some differences
> >>>> between V2.
> >>>> I attach the c++ header, hoping it would be sufficient for you to
> >>>> modify
> >>>> the dissector accordingly.
> >>>>
> >>>> Note that ETHTYPES, which in V2 all started with a 6 (6xx) now start
> >>>> with number 9. (9xx)
> >>>>
> >>>> The message I receive when I try to decode V1 datagrams with the old
> >>>> dissector is something like.
> >>>> Bogus IP header, more payload than told by roofnet...and others.
> >>>>
> >>>> Bye.
> >>>>
> >>>> Nicola
> >>>>
> >>>> ------------------------------------------------------------------------
> >>>>
> >>>>
> >>>> #ifndef CLICK_SRPAKCET_HH
> >>>> #define CLICK_SRPAKCET_HH
> >>>> #include <click/ipaddress.hh>
> >>>> #include <elements/wifi/path.hh>
> >>>> CLICK_DECLS
> >>>>
> >>>> #define max(a, b) ((a) > (b) ? (a) : (b))
> >>>> #define min(a, b) ((a) < (b) ? (a) : (b))
> >>>>
> >>>>
> >>>> enum SRCRPacketType { PT_QUERY = 0x01,
> >>>>               PT_REPLY = 0x02,
> >>>>                       PT_TOP5_RESULT = 0x03,
> >>>>               PT_DATA  = 0x04,
> >>>>                       PT_GATEWAY = 0x08
> >>>> };
> >>>>
> >>>>
> >>>>
> >>>> enum SRCRPacketFlags {
> >>>>   FLAG_ERROR = (1<<0),
> >>>>   FLAG_UPDATE = (1<<1),
> >>>>   FLAG_TOP5_REQUEST_RESULT = (1<<2),
> >>>>   FLAG_TOP5_BEST_ROUTE = (1<<3),
> >>>>   FLAG_SCHEDULE = (1<<4),
> >>>>   FLAG_SCHEDULE_TOKEN = (1<<5),
> >>>>   FLAG_SCHEDULE_FAKE = (1<<6),
> >>>>   FLAG_ECN = (1<<7)
> >>>> };
> >>>>
> >>>> static const uint8_t _sr_version = 0x0b;
> >>>>
> >>>>
> >>>> // Packet format.
> >>>> CLICK_SIZE_PACKED_STRUCTURE(
> >>>> struct srpacket {,
> >>>>   uint8_t _version; /* see _srcr_version */
> >>>>   uint8_t _type;  /* see enum SRCRPacketType */
> >>>>   uint8_t _nlinks;
> >>>>   uint8_t _next;   // Index of next node who should process this
> >>>> packet.
> >>>>
> >>>>
> >>>>   uint16_t _ttl;
> >>>>   uint16_t _cksum;
> >>>>   uint16_t _flags;
> >>>>   uint16_t _dlen;
> >>>>
> >>>>   /* PT_QUERY
> >>>>    * _qdst is used for the query destination in control packets
> >>>>    * and a extra 32 bit seq number in data packets
> >>>>    */
> >>>>   uint32_t _qdst;
> >>>>
> >>>>
> >>>>   uint32_t _seq;   // seq number
> >>>>   uint32_t _seq2;  // another seq number
> >>>>
> >>>>
> >>>>   /* uin32_t ip[_nlinks] */
> >>>>   /* uin32_t metrics[_nlinks] */
> >>>>
> >>>>
> >>>>   /* ip */
> >>>>   /* fwd */
> >>>>   /* rev */
> >>>>   /* seq */
> >>>>   /* ip */
> >>>>
> >>>>   uint32_t _random_from;
> >>>>   uint32_t _random_fwd_metric;
> >>>>   uint32_t _random_rev_metric;
> >>>>   uint32_t _random_seq;
> >>>>   uint16_t _random_age;
> >>>>   uint32_t _random_to;
> >>>>
> >>>>
> >>>>   void set_random_from(IPAddress ip) {
> >>>>     _random_from = ip;
> >>>>   }
> >>>>   void set_random_to(IPAddress ip) {
> >>>>     _random_to = ip;
> >>>>   }
> >>>>   void set_random_fwd_metric(uint32_t m) {
> >>>>     _random_fwd_metric = m;
> >>>>   }
> >>>>
> >>>>   void set_random_rev_metric(uint32_t m) {
> >>>>     _random_rev_metric = m;
> >>>>   }
> >>>>   void set_random_seq(uint32_t s) {
> >>>>     _random_seq = s;
> >>>>   }
> >>>>   void set_random_age(uint32_t s) {
> >>>>     _random_age = s;
> >>>>   }
> >>>>
> >>>>   IPAddress get_random_from() {
> >>>>     return _random_from;
> >>>>   }
> >>>>   IPAddress get_random_to() {
> >>>>     return _random_to;
> >>>>   }
> >>>>   uint32_t get_random_fwd_metric() {
> >>>>     return _random_fwd_metric;
> >>>>   }
> >>>>   uint32_t get_random_rev_metric() {
> >>>>     return _random_rev_metric;
> >>>>   }
> >>>>
> >>>>   uint32_t get_random_seq() {
> >>>>     return _random_seq;
> >>>>   }
> >>>>
> >>>>   uint32_t get_random_age() {
> >>>>     return _random_age;
> >>>>   }
> >>>>
> >>>>
> >>>>   void set_link(int link,
> >>>>         IPAddress a, IPAddress b,
> >>>>         uint32_t fwd, uint32_t rev,
> >>>>         uint32_t seq,
> >>>>         uint32_t age) {
> >>>>
> >>>>     uint32_t *ndx = (uint32_t *) (this+1);
> >>>>     ndx += link * 5;
> >>>>
> >>>>     ndx[0] = a;
> >>>>     ndx[1] = fwd;
> >>>>     ndx[2] = rev;
> >>>>     ndx[3] = seq;
> >>>>     ndx[4] = age;
> >>>>     ndx[5] = b;
> >>>>   }
> >>>>
> >>>>   uint32_t get_link_fwd(int link) {
> >>>>     uint32_t *ndx = (uint32_t *) (this+1);
> >>>>     ndx += link * 5;
> >>>>     return ndx[1];
> >>>>   }
> >>>>   uint32_t get_link_rev(int link) {
> >>>>     uint32_t *ndx = (uint32_t *) (this+1);
> >>>>     ndx += link * 5;
> >>>>     return ndx[2];
> >>>>   }
> >>>>
> >>>>   uint32_t get_link_seq(int link) {
> >>>>     uint32_t *ndx = (uint32_t *) (this+1);
> >>>>     ndx += link * 5;
> >>>>     return ndx[3];
> >>>>   }
> >>>>
> >>>>   uint32_t get_link_age(int link) {
> >>>>     uint32_t *ndx = (uint32_t *) (this+1);
> >>>>     ndx += link * 5;
> >>>>     return ndx[4];
> >>>>   }
> >>>>
> >>>>   IPAddress get_link_node(int link) {
> >>>>     uint32_t *ndx = (uint32_t *) (this+1);
> >>>>     ndx += link * 5;
> >>>>     return ndx[0];
> >>>>   }
> >>>>
> >>>>
> >>>>   void set_link_node(int link, IPAddress ip) {
> >>>>     uint32_t *ndx = (uint32_t *) (this+1);
> >>>>     ndx += link * 5;
> >>>>     ndx[0] = ip;
> >>>>   }
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>   // How long should the packet be?
> >>>>   size_t hlen_wo_data() const { return len_wo_data(_nlinks); }
> >>>>   size_t hlen_with_data() const { return len_with_data(_nlinks,
> >>>> ntohs(_dlen)); }
> >>>>
> >>>>   static size_t len_wo_data(int nlinks) {
> >>>>     return sizeof(struct srpacket) +
> >>>>       sizeof(uint32_t) +
> >>>>       (nlinks) * sizeof(uint32_t) * 5;
> >>>>
> >>>>   }
> >>>>   static size_t len_with_data(int nlinks, int dlen) {
> >>>>     return len_wo_data(nlinks) + dlen;
> >>>>   }
> >>>>
> >>>>   int num_links() {
> >>>>     return _nlinks;
> >>>>   }
> >>>>
> >>>>   int next() {
> >>>>     return _next;
> >>>>   }
> >>>>   Path get_path() {
> >>>>     Path p;
> >>>>     for (int x = 0; x <= num_links(); x++) {
> >>>>       p.push_back(get_link_node(x));
> >>>>     }
> >>>>     return p;
> >>>>   }
> >>>>   void set_data_seq(uint32_t n) {
> >>>>     _qdst = htonl(n);
> >>>>   }
> >>>>   uint32_t data_seq() {
> >>>>     return ntohl(_qdst);
> >>>>   }
> >>>>   void set_seq(uint32_t n) {
> >>>>     _seq = htonl(n);
> >>>>   }
> >>>>   uint32_t seq() {
> >>>>     return ntohl(_seq);
> >>>>   }
> >>>>
> >>>>   void set_seq2(uint32_t n) {
> >>>>     _seq2 = htonl(n);
> >>>>   }
> >>>>   uint32_t seq2() {
> >>>>     return ntohl(_seq2);
> >>>>   }
> >>>>   void set_next(uint8_t n) {
> >>>>     _next = n;
> >>>>   }
> >>>>
> >>>>   void set_num_links(uint8_t n) {
> >>>>     _nlinks = n;
> >>>>   }
> >>>>   void set_data_len(uint16_t len) {
> >>>>     _dlen = htons(len);
> >>>>   }
> >>>>   uint16_t data_len() {
> >>>>     return ntohs(_dlen);
> >>>>   }
> >>>>
> >>>>
> >>>>   void set_flag(uint16_t f) {
> >>>>     uint16_t flags = ntohs(_flags);
> >>>>     _flags = htons(flags | f);
> >>>>   }
> >>>>
> >>>>   bool flag(int f) {
> >>>>     int x = ntohs(_flags);
> >>>>     return x & f;
> >>>>   }
> >>>>   void unset_flag(uint16_t f) {
> >>>>     uint16_t flags = ntohs(_flags);
> >>>>     _flags = htons(flags & !f);
> >>>>   }
> >>>>
> >>>>
> >>>>   /* remember that if you call this you must have set the number of
> >>>> links in this packet! */
> >>>>   u_char *data() { return (((u_char *)this) +
> >>>> len_wo_data(num_links())); }
> >>>>
> >>>>
> >>>>   void set_checksum() {
> >>>>     unsigned int tlen = 0;
> >>>>     if (_type & PT_DATA) {
> >>>>       tlen = hlen_with_data();
> >>>>     } else {
> >>>>       tlen = hlen_wo_data();
> >>>>     }
> >>>>     _cksum = 0;
> >>>>     _cksum = click_in_cksum((unsigned char *) this, tlen);
> >>>>   }
> >>>> });
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> #ifndef sr_assert
> >>>> #define sr_assert(e) ((e) ? (void) 0 : sr_assert_(__FILE__,
> >>>> __LINE__, #e))
> >>>> #endif /* sr_assert */
> >>>>
> >>>>
> >>>> inline void
> >>>> sr_assert_(const char *file, int line, const char *expr)
> >>>> {
> >>>>   click_chatter("assertion \"%s\" FAILED: file %s, line %d",
> >>>>         expr, file, line);
> >>>>
> >>>> #ifdef CLICK_USERLEVEL
> >>>>   abort();
> >>>> #endif
> >>>>
> >>>> }
> >>>>
> >>>>
> >>>> CLICK_ENDDECLS
> >>>> #endif /* CLICK_SRPACKET_HH */
> >>>>
> >>>> ------------------------------------------------------------------------
> >>>>
> >>>>
> >>>> _______________________________________________
> >>>> Wireshark-dev mailing list
> >>>> Wireshark-dev@xxxxxxxxxxxxx
> >>>> http://www.wireshark.org/mailman/listinfo/wireshark-dev
> >>>>
> >>>
> >>> _______________________________________________
> >>> Wireshark-dev mailing list
> >>> Wireshark-dev@xxxxxxxxxxxxx
> >>> http://www.wireshark.org/mailman/listinfo/wireshark-dev
> >>
> >> _______________________________________________
> >> Wireshark-dev mailing list
> >> Wireshark-dev@xxxxxxxxxxxxx
> >> http://www.wireshark.org/mailman/listinfo/wireshark-dev
> >
> > ------------------------------------------------------------------------
> >
> > _______________________________________________
> > Wireshark-dev mailing list
> > Wireshark-dev@xxxxxxxxxxxxx
> > http://www.wireshark.org/mailman/listinfo/wireshark-dev
> >
>
> _______________________________________________
> Wireshark-dev mailing list
> Wireshark-dev@xxxxxxxxxxxxx
> http://www.wireshark.org/mailman/listinfo/wireshark-dev
>


----------------------------------------
Nicola Arnoldi - Undergraduate student
Jr.Researcher
DIT - University of Trento
Via Sommarive 14, I-38100 Trento (Italy)
----------------------------------------
Email:	nicola.arnoldi@xxxxxxxxxxxx
Web: http://dit.unitn.it/~arnoldi/
----------------------------------------
/* ==================================================================
* packet-roofnet.c
* Routines for roofnet dissection
*
* Copyright 2006, Sebastien Tandel (sebastien@xxxxxxxxx)
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@xxxxxxxxxxxxx>
* Copyright 1998 Gerald Combs*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330,
* Boston,  MA 02111-1307  USA
================================================================== */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <glib.h>

#include <epan/packet.h>
#include <epan/addr_resolv.h>
#include <epan/value_string.h>
#include <epan/expert.h>
#include <epan/ptvcursor.h>
#include <epan/tvbuff.h>


/* roofnet packet type constants */
#define ROOFNET1_PT_QUERY 0x01
#define ROOFNET1_PT_REPLY 0x02
#define ROOFNET1_PT_DATA 0x04
#define ROOFNET1_PT_GATEWAY 0x08
static const value_string roofnet1_pt_vals[] = {
  { ROOFNET1_PT_QUERY, "Query" },
  { ROOFNET1_PT_REPLY, "Reply" },
  { ROOFNET1_PT_DATA, "Data" },
  { ROOFNET1_PT_GATEWAY, "Gateway" },
  { 0, NULL }
};

/* roofnet flag bit masks */
#define ROOFNET1_FLAG_ERROR 0x01
#define ROOFNET1_FLAG_UPDATE 0x02
static const value_string roofnet1_flags_vals[] = {
  { ROOFNET1_FLAG_ERROR, "Error" },
  { ROOFNET1_FLAG_UPDATE, "Update" },
  { 0, NULL }
};

/* header length */
#define ROOFNET1_HEADER_LENGTH 166
/* roofnet max length */
/* may change with time */
#define ROOFNET1_MAX_LENGTH 400
/* Roofnet Link Description Length 
 * which is 6 fields of 4 bytes */
#define ROOFNET1_LINK_DESCRIPTION_LENGTH 6*4

/* offset constants */
#define ROOFNET1_OFFSET_TYPE 1
#define ROOFNET1_OFFSET_NLINKS 2
#define ROOFNET1_OFFSET_DATA_LENGTH 10

/* offset relative to a link section of roofnet */
#define ROOFNET1_LINK_OFFSET_SRC 0
#define ROOFNET1_LINK_OFFSET_DST 20
/* roofnet link fields length */
#define ROOFNET1_LINK_LEN 24

/* forward reference */
void proto_reg_handoff_roofnet1();

static dissector_handle_t ip_handle;
static int proto_roofnet1 = -1;

/* hf fields for the header of roofnet */
static int hf_roofnet1_version = -1;
static int hf_roofnet1_type = -1;
static int hf_roofnet1_nlinks = -1;
static int hf_roofnet1_next = -1;
static int hf_roofnet1_ttl = -1;
static int hf_roofnet1_cksum = -1;
static int hf_roofnet1_flags = -1;
static int hf_roofnet1_data_length = -1;
static int hf_roofnet1_query_dst = -1;
static int hf_roofnet1_seq = -1;
static int hf_roofnet1_seq2 = -1;
static int hf_roofnet1_links = -1;
static int hf_roofnet1_link_src = -1;
static int hf_roofnet1_link_forward = -1;
static int hf_roofnet1_link_rev = -1;
static int hf_roofnet1_link_seq = -1;
static int hf_roofnet1_link_age = -1;
static int hf_roofnet1_link_dst = -1;


static gint ett_roofnet1 = -1;
static gint ett_roofnet1_link = -1;

/* 
 * dissect the header of roofnet 
 */
static void dissect_roofnet1_header(proto_tree *tree, tvbuff_t *tvb, 
	guint *offset)
{
  ptvcursor_t *cursor = ptvcursor_new(tree, tvb, *offset);

  ptvcursor_add(cursor, hf_roofnet1_version, 1, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_type, 1, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_nlinks, 1, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_next, 1, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_ttl, 2, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_cksum, 2, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_flags, 2, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_data_length, 2, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_query_dst, 4, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_seq, 4, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_seq2, 4, FALSE);


  *offset = ptvcursor_current_offset(cursor);
  ptvcursor_free(cursor);
}


/*
 *dissect the description of link in roofnet
 */
static int dissect_roofnet1_link(proto_tree *tree, tvbuff_t *tvb, 
	  guint *offset, guint link)
{
  proto_item *it= NULL;
  proto_tree *subtree= NULL;

  ptvcursor_t *cursor= NULL;

  guint32 addr_src= 0;
  guint32 addr_dst= 0;

  addr_src= tvb_get_ipv4(tvb, *offset + ROOFNET1_LINK_OFFSET_SRC);
  addr_dst= tvb_get_ipv4(tvb, *offset + ROOFNET1_LINK_OFFSET_DST);
  
  it = proto_tree_add_text(tree, tvb, *offset, ROOFNET1_LINK_LEN, 
			    "link: %u, src: %s, dst: %s", 
			    link, 
			    (char*)get_hostname(addr_src), 
			    (char*)get_hostname(addr_dst));
  subtree= proto_item_add_subtree(it, ett_roofnet1_link);

  proto_tree_add_ipv4(subtree, hf_roofnet1_link_src, tvb, *offset, 4, addr_src);
  *offset += 4;

  cursor = ptvcursor_new(subtree, tvb, *offset);

  ptvcursor_add(cursor, hf_roofnet1_link_forward, 4, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_link_rev, 4, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_link_seq, 4, FALSE);
  ptvcursor_add(cursor, hf_roofnet1_link_age, 4, FALSE);

  ptvcursor_free(cursor);

  *offset = ptvcursor_current_offset(cursor);
  proto_tree_add_ipv4(subtree, hf_roofnet1_link_dst, tvb, *offset, 4, addr_dst);
  /* don't increment offset here because the dst of this link is the src of the next one */
}

static void dissect_roofnet1_data(proto_tree *tree, tvbuff_t *tvb, packet_info * pinfo, gint offset)
{
  guint16 roofnet1_datalen= 0;
  guint16 remaining_datalen= 0;

  roofnet1_datalen = tvb_get_ntohs(tvb, ROOFNET1_OFFSET_DATA_LENGTH);
  remaining_datalen= tvb_reported_length_remaining(tvb, offset);


  /* dissect on remaining_datalen */
   if (roofnet1_datalen < remaining_datalen)
     proto_tree_add_text(tree, tvb, offset, roofnet1_datalen, 
	 "[More payload data (%u) than told by Roofnet (%u)]", 
	 remaining_datalen, roofnet1_datalen);

  if (roofnet1_datalen == 0)
    return;

  /* dissect ip payload */
  call_dissector(ip_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);

}

/* 
 * entry point of the roofnet dissector
 */
static void dissect_roofnet1(tvbuff_t *tvb, packet_info *pinfo, 
				    proto_tree *tree)
{
  proto_item * it= NULL;
  proto_tree * roofnet1_tree= NULL;
  proto_tree * roofnet1_links_tree= NULL;
  guint offset= 0;

  guint8 roofnet1_msg_type= 0;
  guint8 roofnet1_nlinks= 0;
  guint8 nlink= 1;
  
  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Roofnet1");

  roofnet1_msg_type = tvb_get_guint8(tvb, ROOFNET1_OFFSET_TYPE);
  /* Clear out stuff in the info column */
  if (check_col(pinfo->cinfo, COL_INFO)) {
    col_clear(pinfo->cinfo, COL_INFO);
    col_append_fstr(pinfo->cinfo, COL_INFO, "Message Type: %s",
	match_strval(roofnet1_msg_type, roofnet1_pt_vals));
  }

  if (tree) {
    it = proto_tree_add_item(tree, proto_roofnet1, tvb, offset, -1, FALSE);
    roofnet1_tree = proto_item_add_subtree(it, ett_roofnet1);
  }

  dissect_roofnet1_header(roofnet1_tree, tvb, &offset);

  roofnet1_nlinks= tvb_get_guint8(tvb, ROOFNET1_OFFSET_NLINKS);
  /* Check that we do not have a malformed roofnet packet */
  if ((roofnet1_nlinks*6*4)+ROOFNET1_HEADER_LENGTH > ROOFNET1_MAX_LENGTH) {
    if (tree) {
      expert_add_info_format(pinfo, it, PI_MALFORMED, PI_ERROR, "Too many links (%u)\n", roofnet1_nlinks);
    }
    return;
  }

  for (; roofnet1_nlinks > 0; roofnet1_nlinks--) {
    /* Do we have enough buffer to decode the next link ? */
    if (tvb_reported_length_remaining(tvb, offset) < ROOFNET1_LINK_DESCRIPTION_LENGTH)
      return;
    dissect_roofnet1_link(roofnet1_tree, tvb, &offset, nlink++);
  }

  dissect_roofnet1_data(tree, tvb, pinfo, offset+4);
}

void proto_register_roofnet1(void)
{
  static hf_register_info hf[] = {
    /* Roofnet Header */
    { &hf_roofnet1_version,
      { "Version", "roofnet.version",
      FT_UINT8, BASE_DEC, NULL, 0x0, "Roofnet Version", HFILL }
    },

    { &hf_roofnet1_type,
      { "Type", "roofnet.type",
	FT_UINT8, BASE_DEC, VALS(&roofnet1_pt_vals), 0x0, "Roofnet Message Type", HFILL }
    },

    { &hf_roofnet1_nlinks,
      { "Number of Links", "roofnet.nlinks",
	FT_UINT8, BASE_DEC, NULL, 0x0, "Roofnet Number of Links", HFILL }
    },

    { &hf_roofnet1_next,
      { "Next Link", "roofnet.next",
	FT_UINT8, BASE_DEC, NULL, 0x0, "Roofnet Next Link to Use", HFILL }
    },

    { &hf_roofnet1_ttl,
      { "Time To Live", "roofnet.ttl",
	FT_UINT16, BASE_DEC, NULL, 0x0, "Roofnet Time to Live", HFILL }
    },

    { &hf_roofnet1_cksum,
      { "Checksum", "roofnet.cksum",
	FT_UINT16, BASE_DEC, NULL, 0x0, "Roofnet Header Checksum", HFILL }
    },

    { &hf_roofnet1_flags,
      { "Flags", "roofnet.flags",
	FT_UINT16, BASE_DEC, VALS(&roofnet1_flags_vals), 0x0, "Roofnet Flags", HFILL }
    },

    { &hf_roofnet1_data_length,
      { "Data Length", "roofnet.datalength",
	FT_UINT16, BASE_DEC, NULL, 0x0, "Data Payload Length", HFILL }
    },

    { &hf_roofnet1_query_dst,
      { "Query Dst", "roofnet.querydst",
	FT_IPv4, BASE_HEX, NULL, 0x0, "Roofnet Query Destination", HFILL }
    },

    { &hf_roofnet1_seq,
      { "Seq", "roofnet.seq",
	FT_UINT32, BASE_DEC, NULL, 0x0, "Roofnet Sequential Number", HFILL }
    },
    
    { &hf_roofnet1_seq2,
      { "Seq2", "roofnet.seq2",
	FT_UINT32, BASE_DEC, NULL, 0x0, "Roofnet Sequential Number 2", HFILL }
    },

    { &hf_roofnet1_links,
      { "Links", "roofnet.links",
      FT_NONE, BASE_DEC, NULL, 0x0, "", HFILL }
    },

    { &hf_roofnet1_link_src,
      { "Source IP", "roofnet.link.src",
      FT_IPv4, BASE_HEX, NULL, 0x0, "Roofnet Message Source", HFILL }
    },

    { &hf_roofnet1_link_forward,
      { "Forward", "roofnet.link.forward",
	FT_UINT32, BASE_DEC, NULL, 0x0, "Forward", HFILL }
    },

    { &hf_roofnet1_link_rev,
      { "Rev", "roofnet.link.rev",
	FT_UINT32, BASE_DEC, NULL, 0x0, "Revision Number", HFILL }
    },

    { &hf_roofnet1_link_seq,
      { "Seq", "roofnet.link.seq",
	FT_UINT32, BASE_DEC, NULL, 0x0, "Link Sequential Number", HFILL }
    },

    { &hf_roofnet1_link_age,
      { "Age", "roofnet.link.age",
	FT_UINT32, BASE_DEC, NULL, 0x0, "Information Age", HFILL }
    },

    { &hf_roofnet1_link_dst,
      { "Dst IP", "roofnet.link.dst",
	FT_IPv4, BASE_HEX, NULL, 0x0, "Roofnet Message Destination", HFILL }
    }
  };

    /* setup protocol subtree array */
    static gint *ett[] = {
      &ett_roofnet1,
      &ett_roofnet1_link
    };

    proto_roofnet1 = proto_register_protocol( 
				"Roofnet Protocol1", /* Name */
				"Roofnet1",	    /* Short Name */
				"roofnet1"	    /* Abbrev */
				);

    proto_register_field_array(proto_roofnet1, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
}

static dissector_handle_t roofnet1_handle;

void 
proto_reg_handoff_roofnet1(void)
{
  static gboolean initalised= FALSE;

  if (!initalised) {
    /* Until now there is no other option than having an IPv4 payload (maybe
     * extended one day to IPv6 or other? */
    ip_handle = find_dissector("ip");
    roofnet1_handle = create_dissector_handle(dissect_roofnet1, proto_roofnet1);
    /* I did not put the type numbers in the ethertypes.h as they only are
     * experimental and not official */
    dissector_add("ethertype", 0x0941, roofnet1_handle);
    dissector_add("ethertype", 0x0943, roofnet1_handle);
    dissector_add("ethertype", 0x0944, roofnet1_handle);
    dissector_add("ethertype", 0x0945, roofnet1_handle);
    initalised= TRUE;
  }
}