Ethereal-dev: [Ethereal-dev] New dissector for Red Hat/Fedora netdump

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

From: Eric Paris <eparis@xxxxxxxxxxxxxx>
Date: Mon, 25 Apr 2005 14:10:20 -0400
Attached is a file packet-netdump.c which should dissect netdump
packets.  Netdump is the protocol used to send crash information like
the stack and memory contents to a netdump server when a linux machine
panics/opps.  

This is my first attempt at a dissector, so please let me have any
comments on any problems you see.

Netdump uses port UDP 6666 which is also defined by packet-sigcomp.c.  I
don't have any traces which result in parsing these type of packets so
I'm not sure how to make sure it is still picking those up.  I found
that just registering port 6666 with dissector_add caused the sigcomp to
still get tried and my netdump to never get tried.  So I registered with
heur_dissector_add and it started trying my dissector second.  So I can
only assume that it will get those others.   I'm not sure what the right
way is to do this.  Please comment if this was not right or if there is
a better way.

I have a netdump capture with netdump traffic but its about 40 megs long
(dumping memory does generate a lot of traffic), please let me know if
access to this is needed.

Eric
/* packet-netdump.c
 * 
 * Routines for netdump dissection.  Netdump is the crash dump
 * protocol used by Red Hat to write kernel stack trace and
 * memory contents to a network sever during an opps or panic
 *
 * Copyright 2000, Eric_Paris <ethereal-netdump@xxxxxxxxxxxxxx>
 *
 * $Id: README.developer 12979 2005-01-07 11:59:05Z guy $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * 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; either version 2
 * of the License, or (at your option) any later version.
 * 
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <address.h>
#include <epan/packet.h>
#include <epan/conversation.h>

#define UDP_PORT_NETDUMP 6666

static int proto_netdump = -1;
static int hf_netdump_magicnumber = -1;
static int hf_netdump_nr = -1;
static int hf_netdump_command = -1;
static int hf_netdump_from = -1;
static int hf_netdump_to = -1;
static int hf_netdump_code = -1;
static int hf_netdump_info = -1;
static int hf_netdump_version = -1;

static gint ett_netdump = -1;

static dissector_handle_t netdump_handle;

enum netdump_commands
{
  COMM_NONE = 0,
  COMM_SEND_MEM = 1,
  COMM_EXIT = 2,
  COMM_REBOOT = 3,
  COMM_HELLO = 4,
  COMM_GET_NR_PAGES = 5,
  COMM_GET_PAGE_SIZE = 6,
  COMM_START_NETDUMP_ACK = 7,
  COMM_GET_REGS = 8,
  COMM_SHOW_STATE = 9
};

static const value_string netdump_command_vals[] = {
  {COMM_NONE, "COMM_NONE, Unknown to Author"},
  {COMM_SEND_MEM, "COMM_SEND_MEM, Request to Send Memory"},
  {COMM_EXIT, "COMM_EXIT, Unknown to Author"},
  {COMM_REBOOT, "COMM_REBOOT, Request to Reboot Client"},
  {COMM_HELLO, "COMM_HELLO, Hello Message"},
  {COMM_GET_NR_PAGES, "COMM_GET_NR_PAGES, Request Number of Pages on Client"},
  {COMM_GET_PAGE_SIZE, "COMM_GET_PAGE_SIZE, Request the Size of a Page"},
  {COMM_START_NETDUMP_ACK,
   "COMM_START_NETDUMP_ACK, Server Responce to REPLY_START_NETDUMP"},
  {COMM_GET_REGS, "COMM_GET_REGS, Request for All Registers"},
  {COMM_SHOW_STATE, "COMM_SHOW_STATE, Request to Show Client State"}
};

enum netdump_replies
{
  REPLY_NONE = 0,
  REPLY_ERROR = 1,
  REPLY_LOG = 2,
  REPLY_MEM = 3,
  REPLY_RESERVED = 4,
  REPLY_HELLO = 5,
  REPLY_NR_PAGES = 6,
  REPLY_PAGE_SIZE = 7,
  REPLY_START_NETDUMP = 8,
  REPLY_END_NETDUMP = 9,
  REPLY_REGS = 10,
  REPLY_MAGIC = 11,
  REPLY_SHOW_STATE = 12
};

static const value_string netdump_reply_vals[] = {
  {REPLY_NONE, " REPLY_NONE, Unknown to Author"},
  {REPLY_ERROR, "REPLY_ERROR, Error"},
  {REPLY_LOG, "REPLY_LOG, Log Dump"},
  {REPLY_MEM, "REPLY_MEM, Memory Dump"},
  {REPLY_RESERVED, "REPLY_RESERVED, Unknown to Author"},
  {REPLY_HELLO,
   "REPLY_HELLO, Client Responce to COMM_HELLO (complete handshake)"},
  {REPLY_NR_PAGES, "REPLY_NR_PAGES, Client Number of Pages"},
  {REPLY_PAGE_SIZE, "REPLY_PAGE_SIZE, Client Page Size"},
  {REPLY_START_NETDUMP, "REPLY_START_NETDUMP, Request to Start a Netdump"},
  {REPLY_END_NETDUMP, "REPLY_END_NETDUMP, Unknown to Author"},
  {REPLY_REGS, "REPLY_REGS, Client Registers"},
  {REPLY_MAGIC, "REPLY_MAGIC, Magic Number"},
  {REPLY_SHOW_STATE, "REPLY_SHOW_STATE, Client State"}
};

struct netdump_conversation_data_t
{
  address client;
  address server;
};

/* Code to actually dissect the packets */
static int
dissect_netdump (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
{

/* Set up structures needed to add the protocol subtree and manage it */
  proto_item *ti;
  proto_tree *netdump_tree;
  conversation_t *conversation;
  struct netdump_conversation_data_t *netdump_conversation_data = NULL;


  /*
   * we are not netdump if talking to syslog, that is actually console log and should
   * be parsed as a standard syslog message
   */
  if (pinfo->destport == 514)
    return 0;

  /* hopefully this will make the conversation match exactly */
  conversation =
    find_conversation (pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
		       pinfo->srcport, pinfo->destport, 0);
  if (conversation == NULL)
    {
      /* 
       * The conversation is created matching exactly on the source/dest
       * address/port.  That way we won't accidentally match on other
       * messages.
       */
      guint options = 0;
      conversation =
	conversation_new (pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
			  pinfo->srcport, pinfo->destport, options);
      conversation_set_dissector (conversation, netdump_handle);
      netdump_conversation_data =
	malloc (sizeof (struct netdump_conversation_data_t));
      COPY_ADDRESS (&(netdump_conversation_data->client), &(pinfo->net_src));
      COPY_ADDRESS (&(netdump_conversation_data->server), &(pinfo->net_dst));
      conversation_add_proto_data (conversation, proto_netdump,
				   netdump_conversation_data);
    }
  else
    {
      netdump_conversation_data =
	conversation_get_proto_data (conversation, proto_netdump);
    }

/* Make entries in Protocol column and Info column on summary display */
  if (check_col (pinfo->cinfo, COL_PROTOCOL))
    col_set_str (pinfo->cinfo, COL_PROTOCOL, "NETDUMP");

  if (check_col (pinfo->cinfo, COL_INFO))
    col_clear (pinfo->cinfo, COL_INFO);

  if (check_col (pinfo->cinfo, COL_INFO))
    col_add_fstr (pinfo->cinfo, COL_INFO, "Net Dump");

  if (tree)
    {
      gint offset = 0;
      guint16 bytes_remaining = 0;

/* create display subtree for the protocol */
      ti = proto_tree_add_item (tree, proto_netdump, tvb, 0, -1, FALSE);

      netdump_tree = proto_item_add_subtree (ti, ett_netdump);

      bytes_remaining = tvb_reported_length_remaining (tvb, offset);
/* 
 * choose which info to display.  we pick server/client based on who initiated
 * the conversions.  The initiator information should be stored in the 
 * netdump_conversation_data->server field
 */
      if (ADDRESSES_EQUAL (&pinfo->src, &netdump_conversation_data->server))
	{
	  proto_tree_add_item (netdump_tree,
			       hf_netdump_magicnumber, tvb, offset, 8, FALSE);
	  offset += 8;

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_nr, tvb, offset, 4, FALSE);
	  offset += 4;

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_command, tvb, offset, 4, FALSE);
	  offset += 4;

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_from, tvb, offset, 4, FALSE);
	  offset += 4;

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_to, tvb, offset, 4, FALSE);
	  offset += 4;
	}
      else
	{

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_version, tvb, offset, 1, FALSE);
	  offset += 1;

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_nr, tvb, offset, 4, FALSE);
	  offset += 4;

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_code, tvb, offset, 4, FALSE);
	  offset += 4;

	  proto_tree_add_item (netdump_tree,
			       hf_netdump_info, tvb, offset, 4, FALSE);
	  offset += 4;

	  bytes_remaining = tvb_reported_length_remaining (tvb, offset);
	  if (bytes_remaining)
	    proto_tree_add_text (netdump_tree, tvb, offset, -1,
				 "Data (%d bytes)",
				 tvb_reported_length_remaining (tvb, offset));
	}
      return tvb_length (tvb);
    }
}

static gboolean
dissect_netdump_heur (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
{
  return dissect_netdump (tvb, pinfo, tree);
}

/* Register the protocol with Ethereal */

void
proto_register_netdump (void)
{
  static hf_register_info hf[] = {
    {&hf_netdump_magicnumber,
     {"magicnumber", "netdump.magicnumber",
      FT_UINT64, BASE_HEX, NULL, 0x0,
      "Magic Number", HFILL}
     },
    {&hf_netdump_nr,
     {"nr", "netdump.nr",
      FT_UINT32, BASE_DEC, NULL, 0x0,
      "nr Number", HFILL}
     },
    {&hf_netdump_command,
     {"command", "netdump.command",
      FT_UINT32, BASE_DEC, VALS (netdump_command_vals), 0x0,
      "Command", HFILL}
     },
    {&hf_netdump_from,
     {"from", "netdump.from",
      FT_UINT32, BASE_HEX, NULL, 0x0,
      "From", HFILL}
     },
    {&hf_netdump_to,
     {"to", "netdump.to",
      FT_UINT32, BASE_HEX, NULL, 0x0,
      "To", HFILL}
     },
    {&hf_netdump_version,
     {"version", "netdump.version",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      "Effective Version", HFILL}
     },
    {&hf_netdump_code,
     {"code", "netdump.code",
      FT_UINT32, BASE_DEC, VALS (netdump_reply_vals), 0x0,
      "Code", HFILL}
     },
    {&hf_netdump_info,
     {"info", "netdump.info",
      FT_UINT32, BASE_DEC, NULL, 0x0,
      "Info", HFILL}
     },
  };

  static gint *ett[] = {
    &ett_netdump,
  };

  proto_netdump = proto_register_protocol ("Net Dump", "NetDump", "netdump");
  proto_register_field_array (proto_netdump, hf, array_length (hf));
  proto_register_subtree_array (ett, array_length (ett));
  netdump_handle = new_create_dissector_handle (dissect_netdump,
						proto_netdump);
}

void
proto_reg_handoff_netdump (void)
{
  dissector_add ("udp.port", UDP_PORT_NETDUMP, netdump_handle);
  heur_dissector_add ("udp", dissect_netdump_heur, proto_netdump);
}