Wireshark-dev: [Wireshark-dev] Dissector for Cisco ITP packet logging facility
From: "Abhik Sarkar" <sarkar.abhik@xxxxxxxxx>
Date: Thu, 25 Jan 2007 17:04:03 +0400
Hello All,

This probably doesn't qualify as a "dissector", but this has been very
useful for me since I wrote it a couple of days back and I thought it
might be useful for someone else too...

First an introduction in the form of a quote from the Cisco ITP manual...

<quote>
The ITP Packet Logging facility uses the BSD syslog protocol (RFC
3164) to send selected (SS7) MSUs to a user-selected monitoring tool
via the UDP connectionless protocol (RFC 768). Cisco Systems, Inc.
does not provide monitoring tools specifically for receiving and
decoding messages sent by the facility. The user must obtain a
suitable tool for receiving syslog messages.
</quote>

I have seen a proprietary tool to receive and decode these messages;
however, that runs on only one platform (as far as I know) and I don't
always have access to that platform.

Now, since wireshark can already dissect syslog packets and mtp
packets, I thought of combining the two.

The MSU is encapsulated in the syslog message as "<some headers>
msu=<hex dump of the  MSU>". This dissector is simply an extension of
the syslog dissector. It extracts the hex dump from the packet and
creates a byte array of it and passes it to the mtp3 dissector. The
result is that the mtp3 and higher level protocols are automatically
decoded.

However, it should be noted that the protocol tree (packet details)
view and the packet bytes view don't correspond because the original
frame did not actually contain an SS7 MSU.

Also, I am only a hobbyist programmer and mainly use only Java for
development, so the code might not be as 'good quality' as it should.
Perhaps anyone else who finds this useful and has more C experience
will be willing to improve the code.

Best regards,
Abhik.
/* packet-paklog.c
 * Routines for decoding of Cisco ITP packet logging facility packets.
 *
 * Copyright 2007, Abhik Sarkar <sarkar.abhik@xxxxxxxxx>
 *
 * $Id$
 *
 * Copied from packet-syslog.c (packet-syslog.c 18196 2006-05-21 04:49:01Z)
 *
 * Note:
 * =================================
 * This dissector is more like glue between two dissectors, than a dissector
 * itself. Cisco ITP's packet logging facility allows selected MSUs to be 
 * to be encapsulated in syslog UDP datagrams and sent to a monitoring tool.
 * However, no actual tool to monitor/decode the MSUs are provided. The aim
 * of this "dissector" is to extract the hex dump of the MSU from the syslog
 * packet and pass it on to the mtp3 dissector for decoding.
 * 
 * 
 * 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; 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 <ctype.h>

#include <string.h>
#include <glib.h>
#include <epan/packet.h>
#include <epan/emem.h>

#define UDP_PORT_SYSLOG 514

#define PRIORITY_MASK 0x0007  /* 0000 0111 */
#define FACILITY_MASK 0x03f8  /* 1111 1000 */

/* The maximum number if priority digits to read in. */
#define MAX_DIGITS 3

static const value_string short_lev[] = {
  { 0,      "EMERG" },
  { 1,      "ALERT" },
  { 2,      "CRIT" },
  { 3,      "ERR" },
  { 4,      "WARNING" },
  { 5,      "NOTICE" },
  { 6,      "INFO" },
  { 7,      "DEBUG" },
  { 0, NULL }
};

static const value_string short_fac[] = {
  { 0,     "KERN" },
  { 1,     "USER" },
  { 2,     "MAIL" },
  { 3,     "DAEMON" },
  { 4,     "AUTH" },
  { 5,     "SYSLOG" },
  { 6,     "LPR" },
  { 7,     "NEWS" },
  { 8,     "UUCP" },
  { 9,     "CRON" },		/* The BSDs, Linux, and others */
  { 10,    "AUTHPRIV" },
  { 11,    "FTP" },
  { 12,    "NTP" },
  { 13,    "LOGAUDIT" },
  { 14,    "LOGALERT" },
  { 15,    "CRON" },		/* Solaris */
  { 16,    "LOCAL0" },
  { 17,    "LOCAL1" },
  { 18,    "LOCAL2" },
  { 19,    "LOCAL3" },
  { 20,    "LOCAL4" },
  { 21,    "LOCAL5" },
  { 22,    "LOCAL6" },
  { 23,    "LOCAL7" },
  { 0, NULL }
};

static const value_string long_lev[] = {
  { 0,      "EMERG - system is unusable" },
  { 1,      "ALERT - action must be taken immediately" },
  { 2,      "CRIT - critical conditions" },
  { 3,      "ERR - error conditions" },
  { 4,      "WARNING - warning conditions" },
  { 5,      "NOTICE - normal but significant condition" },
  { 6,      "INFO - informational" },
  { 7,      "DEBUG - debug-level messages" },
  { 0, NULL }
};

static const value_string long_fac[] = {
  { 0,     "KERN - kernel messages" },
  { 1,     "USER - random user-level messages" },
  { 2,     "MAIL - mail system" },
  { 3,     "DAEMON - system daemons" },
  { 4,     "AUTH - security/authorization messages" },
  { 5,     "SYSLOG - messages generated internally by syslogd" },
  { 6,     "LPR - line printer subsystem" },
  { 7,     "NEWS - network news subsystem" },
  { 8,     "UUCP - UUCP subsystem" },
  { 9,     "CRON - clock daemon (BSD, Linux)" },
  { 10,    "AUTHPRIV - security/authorization messages (private)" },
  { 11,    "FTP - ftp daemon" },
  { 12,    "NTP - ntp subsystem" },
  { 13,    "LOGAUDIT - log audit" },
  { 14,    "LOGALERT - log alert" },
  { 15,    "CRON - clock daemon (Solaris)" },
  { 16,    "LOCAL0 - reserved for local use" },
  { 17,    "LOCAL1 - reserved for local use" },
  { 18,    "LOCAL2 - reserved for local use" },
  { 19,    "LOCAL3 - reserved for local use" },
  { 20,    "LOCAL4 - reserved for local use" },
  { 21,    "LOCAL5 - reserved for local use" },
  { 22,    "LOCAL6 - reserved for local use" },
  { 23,    "LOCAL7 - reserved for local use" },
  { 0, NULL }
};

static gint proto_syslog = -1;
static gint hf_syslog_level = -1;
static gint hf_syslog_facility = -1;
static gint hf_syslog_msg = -1;

static gint ett_syslog = -1;
static dissector_handle_t mtp_handle;

/* The message format is defined in RFC 3164 */

static void dissect_paklog(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  gint pri = -1, lev = -1, fac = -1;
  gint msg_off = 0, msg_len;
  proto_item *ti;
  proto_tree *syslog_tree;
  const char *msg_str;

  /* Variables for paklog decoding */
  guint8 nibble0;
  guint8 nibble1;
  guint8 msu_byte;
  gint hex_dump_len;
  gint counter;
  GString *paklog_str;
  GString *msu_hex_dump;
  tvbuff_t *next_buf;
  guint8 *byte_array;
  gboolean packet_contains_msu;


  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Paklog");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  if (tvb_get_guint8(tvb, msg_off) == '<') {
    /* A facility and level follow. */
    msg_off++;
    pri = 0;
    while (tvb_bytes_exist(tvb, msg_off, 1) &&
           isdigit(tvb_get_guint8(tvb, msg_off)) && msg_off <= MAX_DIGITS) {
      pri = pri * 10 + (tvb_get_guint8(tvb, msg_off) - '0');
      msg_off++;
    }
    if (tvb_get_guint8(tvb, msg_off) == '>')
      msg_off++;
    fac = (pri & FACILITY_MASK) >> 3;
    lev = pri & PRIORITY_MASK;
  }

  msg_len = tvb_ensure_length_remaining(tvb, msg_off);
  msg_str = tvb_format_text(tvb, msg_off, msg_len);

  /* start of paklog detection/decoding */
  nibble0 = 0;
  nibble1 = 0;
  msu_byte = 0;
  packet_contains_msu = FALSE;

  if (strstr(msg_str, "msu=") != NULL) {
    paklog_str = g_string_new(msg_str);
    packet_contains_msu = TRUE;
    msu_hex_dump = g_string_new(g_strsplit(paklog_str->str, "msu=",2)[1]);
    hex_dump_len = msu_hex_dump->len;

    if (hex_dump_len == 0) {
      /* This should not really happen, but just in case... */
      packet_contains_msu = FALSE;
    } else {
      byte_array = ep_alloc_array(guint8, hex_dump_len/2);

      for (counter = 0; counter < hex_dump_len; counter++) {
        if (counter % 2 == 0) {
          nibble0 = (guint8) g_ascii_xdigit_value(msu_hex_dump->str[counter]);
        } else {
          nibble1 = (guint8) g_ascii_xdigit_value(msu_hex_dump->str[counter]);
          msu_byte = (nibble0 << 4) | nibble1;
          byte_array[counter/2] = msu_byte;
        }
      }

        next_buf = tvb_new_real_data(byte_array, hex_dump_len/2, hex_dump_len/2);
    }
    g_string_free(msu_hex_dump, TRUE);
    g_string_free(paklog_str, TRUE);
  }

  /* end of paklog detection/decoding */

  if (check_col(pinfo->cinfo, COL_INFO)) {
    if (pri >= 0) {
      col_add_fstr(pinfo->cinfo, COL_INFO, "%s.%s: %s",
        val_to_str(fac, short_fac, "UNKNOWN"),
        val_to_str(lev, short_lev, "UNKNOWN"), msg_str);
    } else {
      col_add_str(pinfo->cinfo, COL_INFO, msg_str);
    }
  }

  if (tree) {
    if (pri >= 0) {
      ti = proto_tree_add_protocol_format(tree, proto_syslog, tvb, 0, -1,
        "Syslog message: %s.%s: %s",
        val_to_str(fac, short_fac, "UNKNOWN"),
        val_to_str(lev, short_lev, "UNKNOWN"), msg_str);
    } else {
      ti = proto_tree_add_protocol_format(tree, proto_syslog, tvb, 0, -1,
        "Syslog message: (unknown): %s", msg_str);
    }
    syslog_tree = proto_item_add_subtree(ti, ett_syslog);
    if (pri >= 0) {
      ti = proto_tree_add_uint(syslog_tree, hf_syslog_facility, tvb, 0,
        msg_off, pri);
      ti = proto_tree_add_uint(syslog_tree, hf_syslog_level, tvb, 0,
        msg_off, pri);
    }
    proto_tree_add_item(syslog_tree, hf_syslog_msg, tvb, msg_off,
      msg_len, FALSE);
  }

  /* Call MTP dissector if encapsulated MSU was found */
  if ( packet_contains_msu == TRUE ) {
    call_dissector(mtp_handle, next_buf, pinfo, tree);
  }

  return;
}

/* Register the protocol with Wireshark */
void proto_register_paklog(void)
{

  /* Setup list of header fields */
  static hf_register_info hf[] = {
    { &hf_syslog_facility,
      { "Facility",           "syslog.facility",
      FT_UINT8, BASE_DEC, VALS(long_fac), FACILITY_MASK,
      "Message facility", HFILL }
    },
    { &hf_syslog_level,
      { "Level",              "syslog.level",
      FT_UINT8, BASE_DEC, VALS(long_lev), PRIORITY_MASK,
      "Message level", HFILL }
    },
    { &hf_syslog_msg,
      { "Message",            "syslog.msg",
      FT_STRING, BASE_NONE, NULL, 0x0,
      "Message Text", HFILL }
    },
  };

  /* Setup protocol subtree array */
  static gint *ett[] = {
    &ett_syslog,
  };

  /* Register the protocol name and description */
  proto_syslog = proto_register_protocol("Cisco ITP Packet Logging message", "Paklog", "paklog");

  /* Required function calls to register the header fields and subtrees used */
  proto_register_field_array(proto_syslog, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_paklog(void)
{
  dissector_handle_t syslog_handle;

  syslog_handle = create_dissector_handle(dissect_paklog, proto_syslog);
  dissector_add("udp.port", UDP_PORT_SYSLOG, syslog_handle);

  /* Find the mtp3 dissector */
  mtp_handle = find_dissector("mtp3");
}