mirror of
https://github.com/ceph/ceph
synced 2024-12-26 21:43:10 +00:00
972d22e245
dissector, it has been re-worked to bring into line with Wireshark coding guidelines that aim to ensure portability. It currently only decodes a handful of messages as test cases, it needs more work to make it useful. See README.txt for a bit more background.
2662 lines
88 KiB
C
2662 lines
88 KiB
C
/* packet-ceph.c
|
|
* Routines for Ceph Protocols
|
|
* http://www.ceph.com
|
|
*
|
|
* Copyright 2013, Kevin Jones <k.j.jonez@gmail.com>
|
|
*
|
|
* This file contains parts of the original dissector code found in
|
|
* the CEPH source tree. The author of that code was not marked.
|
|
*
|
|
* 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.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "../../epan/packet.h"
|
|
#include "../../epan/tvbuff.h"
|
|
#include "../../epan/tvbuff-int.h"
|
|
#include "../../epan/prefs.h"
|
|
#include "../../epan/range.h"
|
|
#include "../../epan/report_err.h"
|
|
#include "../../epan/conversation.h"
|
|
#include "../../epan/expert.h"
|
|
#include "../../epan/dissectors/packet-tcp.h"
|
|
|
|
/* Protocol name */
|
|
#define PROTO_TAG_CEPH "CEPHalt"
|
|
|
|
/****************************************************************************
|
|
* Global handles
|
|
****************************************************************************/
|
|
|
|
/* Wireshark ID of the CEPH protocol */
|
|
static int g_proto_ceph = -1;
|
|
|
|
/* The registered dissector handle */
|
|
static dissector_handle_t g_ceph_handle;
|
|
|
|
/*
|
|
* The following hf_* variables are used to hold the Wireshark IDs of
|
|
* our header fields; they are filled out when we call
|
|
* proto_register_field_array() in proto_register_ceph()
|
|
*/
|
|
static gint hf_header = -1;
|
|
static gint hf_banner = -1;
|
|
static gint hf_entity_addr = -1;
|
|
static gint hf_entity_type = -1;
|
|
static gint hf_entity_num = -1;
|
|
static gint hf_banner_magic = -1;
|
|
static gint hf_banner_version = -1;
|
|
static gint hf_entity_erank = -1;
|
|
static gint hf_entity_nonce = -1;
|
|
static gint hf_sockaddr_in = -1;
|
|
static gint hf_sin_family = -1;
|
|
static gint hf_sin_port = -1;
|
|
static gint hf_sin_addr = -1;
|
|
static gint hf_hdr_tag = -1;
|
|
static gint hf_hdr_seq_ack = -1;
|
|
static gint hf_hdr_seq = -1;
|
|
static gint hf_hdr_tid = -1;
|
|
static gint hf_hdr_type = -1;
|
|
static gint hf_hdr_priority = -1;
|
|
static gint hf_hdr_version = -1;
|
|
static gint hf_hdr_mon_protocol = -1;
|
|
static gint hf_hdr_osd_protocol = -1;
|
|
static gint hf_hdr_mds_protocol = -1;
|
|
static gint hf_hdr_client_protocol = -1;
|
|
static gint hf_hdr_front_len = -1;
|
|
static gint hf_hdr_middle_len = -1;
|
|
static gint hf_hdr_data_off = -1;
|
|
static gint hf_hdr_data_len = -1;
|
|
static gint hf_hdr_src = -1;
|
|
static gint hf_hdr_crc = -1;
|
|
static gint hf_footer = -1;
|
|
static gint hf_footer_front_crc = -1;
|
|
static gint hf_footer_middle_crc = -1;
|
|
static gint hf_footer_data_crc = -1;
|
|
static gint hf_footer_sig = -1;
|
|
static gint hf_footer_flags = -1;
|
|
static gint hf_paxos_version = -1;
|
|
static gint hf_featureset_mask = -1;
|
|
static gint hf_featureset_id = -1;
|
|
static gint hf_featureset_name = -1;
|
|
static gint hf_compatset_compat = -1;
|
|
static gint hf_compatset_rocompat = -1;
|
|
static gint hf_compatset_incompat = -1;
|
|
|
|
static gint hf_connect_features = -1;
|
|
static gint hf_connect_host_type = -1;
|
|
static gint hf_connect_tag = -1;
|
|
static gint hf_connect_global_seq = -1;
|
|
static gint hf_connect_connect_seq = -1;
|
|
static gint hf_connect_protocol_version = -1;
|
|
static gint hf_connect_authorizer_protocol = -1;
|
|
static gint hf_connect_authorizer_len = -1;
|
|
static gint hf_connect_flags = -1;
|
|
static gint hf_connect_authentication_key = -1;
|
|
static gint hf_monmap = -1;
|
|
static gint hf_monmap_version = -1;
|
|
static gint hf_monmap_compat = -1;
|
|
static gint hf_monmap_epoch = -1;
|
|
static gint hf_monmap_name = -1;
|
|
static gint hf_monmap_lastchanged = -1;
|
|
static gint hf_monmap_created = -1;
|
|
static gint hf_monsubscribe = -1;
|
|
static gint hf_monsubscribe_name = -1;
|
|
static gint hf_monsubscribe_start = -1;
|
|
static gint hf_monsubscribe_flags = -1;
|
|
static gint hf_monsubscribeack = -1;
|
|
static gint hf_monsubscribeack_interval = -1;
|
|
static gint hf_mdsbeacon = -1;
|
|
static gint hf_mdsbeacon_globalid = -1;
|
|
static gint hf_mdsbeacon_state = -1;
|
|
static gint hf_mdsbeacon_seq = -1;
|
|
static gint hf_mdsbeacon_name = -1;
|
|
static gint hf_mdsbeacon_standbyforrank = -1;
|
|
static gint hf_mdsbeacon_standbyforname = -1;
|
|
static gint hf_auth = -1;
|
|
static gint hf_auth_protocol = -1;
|
|
static gint hf_auth_authlen = -1;
|
|
static gint hf_auth_authbytes = -1;
|
|
static gint hf_auth_monmapepoch = -1;
|
|
static gint hf_authreply = -1;
|
|
static gint hf_authreply_protocol = -1;
|
|
static gint hf_authreply_result = -1;
|
|
static gint hf_authreply_globalid = -1;
|
|
static gint hf_authreply_authlen = -1;
|
|
static gint hf_authreply_authbytes = -1;
|
|
static gint hf_authreply_msglen = -1;
|
|
static gint hf_authreply_msgstring = -1;
|
|
|
|
/* These are the ids of the subtrees that we may be creating */
|
|
static gint ett_ceph = -1;
|
|
static gint ett_header = -1;
|
|
static gint ett_banner = -1;
|
|
static gint ett_entity_addr = -1;
|
|
static gint ett_front = -1;
|
|
static gint ett_footer = -1;
|
|
static gint ett_sockaddr_in = -1;
|
|
static gint ett_entity_name = -1;
|
|
static gint ett_compat = -1;
|
|
static gint ett_rocompat = -1;
|
|
static gint ett_incompat = -1;
|
|
|
|
/****************************************************************************
|
|
* Conversation helper - to find exact matches only
|
|
****************************************************************************/
|
|
static conversation_t*
|
|
conv_find_exact(guint32 frame, const address* addr1, guint32 port1,
|
|
const address* addr2, guint32 port2) {
|
|
conversation_t* conv;
|
|
conv = find_conversation(frame, addr1, addr2,PT_TCP, port1, port2, 0);
|
|
if (conv != NULL) {
|
|
if (ADDRESSES_EQUAL(&conv->key_ptr->addr1, addr1) &&
|
|
ADDRESSES_EQUAL(&conv->key_ptr->addr2, addr2) &&
|
|
conv->key_ptr->port1 == port1 &&
|
|
conv->key_ptr->port2 == port2)
|
|
{
|
|
return conv;
|
|
}
|
|
if (ADDRESSES_EQUAL(&conv->key_ptr->addr1, addr2) &&
|
|
ADDRESSES_EQUAL(&conv->key_ptr->addr2, addr1) &&
|
|
conv->key_ptr->port1 == port2 &&
|
|
conv->key_ptr->port2 == port1)
|
|
{
|
|
return conv;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Entities (aka ceph service endpoints)
|
|
* These should match the CEPH_ENTITY_ macros to make conversion easy from
|
|
* on the wire formats.
|
|
****************************************************************************/
|
|
typedef enum _entity_type {
|
|
ENTITY_TYPE_UNKNOWN = 0x00,
|
|
ENTITY_TYPE_MON = 0x01,
|
|
ENTITY_TYPE_MDS = 0x02,
|
|
ENTITY_TYPE_OSD = 0x04,
|
|
ENTITY_TYPE_CLIENT = 0x08,
|
|
ENTITY_TYPE_AUTH = 0x20
|
|
} entity_type;
|
|
|
|
/*
|
|
* This is what we record (within conv state) about each known endpoint.
|
|
* Beware that conv_data maintains a pointer to these records so that should
|
|
* not be reallocated, see get_entity() for obtaining them. They are shared
|
|
* this way so conversations may pass info to each other about endpoints.
|
|
*/
|
|
typedef struct _entity_data {
|
|
entity_type type;
|
|
char* name;
|
|
address addr;
|
|
guint16 port;
|
|
} entity_data;
|
|
|
|
/*
|
|
* Map a entity type to a display string
|
|
*/
|
|
static const char*
|
|
entityTypeDescription(entity_type type) {
|
|
switch (type) {
|
|
case ENTITY_TYPE_UNKNOWN:
|
|
return "?";
|
|
case ENTITY_TYPE_MON:
|
|
return "mon";
|
|
case ENTITY_TYPE_MDS:
|
|
return "mds";
|
|
case ENTITY_TYPE_OSD:
|
|
return "osd";
|
|
case ENTITY_TYPE_CLIENT:
|
|
return "client";
|
|
case ENTITY_TYPE_AUTH:
|
|
return "auth";
|
|
}
|
|
assert(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Test for a valid entity type
|
|
*/
|
|
static gboolean
|
|
isEntityType(guint32 type) {
|
|
switch (type) {
|
|
case ENTITY_TYPE_MON:
|
|
case ENTITY_TYPE_MDS:
|
|
case ENTITY_TYPE_OSD:
|
|
case ENTITY_TYPE_CLIENT:
|
|
case ENTITY_TYPE_AUTH:
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Find or create a new entity data entry from an address/port pair.
|
|
*/
|
|
static entity_data*
|
|
conv_get_entity(const address* addr, guint32 port) {
|
|
conversation_t *conv=NULL;
|
|
entity_data *edata;
|
|
|
|
/*
|
|
* Have we seen this conversation before? We record this as a conversation
|
|
* to the same address/port. This is just to help keep the data together.
|
|
*/
|
|
conv = conv_find_exact(-1, addr, port, addr, port);
|
|
if (conv == NULL) {
|
|
edata = (entity_data*)se_alloc(sizeof(entity_data));
|
|
edata->type = ENTITY_TYPE_UNKNOWN;
|
|
edata->name = NULL;
|
|
SE_COPY_ADDRESS(&edata->addr,addr);
|
|
edata->port = port;
|
|
conv = conversation_new(0, addr, addr, PT_TCP, port, port, 0);
|
|
conversation_add_proto_data(conv, g_proto_ceph, edata);
|
|
} else {
|
|
edata=(entity_data*)conversation_get_proto_data(conv, g_proto_ceph);
|
|
}
|
|
return edata;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Data for tracking messages
|
|
* This is mainly to track the startup handling which was confusing in the
|
|
* old dissector because the connect_reply would appear before its matching
|
|
* request. Here we track in finer granularity and allow for some
|
|
* interweaving of messages. Of course the code also needs to handle traces
|
|
* which have conversations already running.
|
|
****************************************************************************/
|
|
enum msg_type {
|
|
SERVER_BANNER =0x1, /* Server send banner to client */
|
|
CLIENT_BANNER =0x2, /* Client responds to SERVER_BANNER */
|
|
SERVER_ADDRESS =0x4, /* Server sends its & client addresses to client */
|
|
CLIENT_ADDRESS =0x8, /* Client sends it own address to server */
|
|
CONNECT_REQUEST=0x10, /* Client send connection request to server */
|
|
CONNECT_REPLY =0x20, /* Server responds to connect request */
|
|
CONVERSING =0x40 /* All other messages */
|
|
};
|
|
#define MSG_ANY 0xFF /* Accept any type of message */
|
|
|
|
/*
|
|
* Per message data, we record these during conversation startup
|
|
*/
|
|
typedef struct _pdu_data {
|
|
guint32 position; /* The stream position msg seen at */
|
|
guint32 toserver; /* To/from the server */
|
|
enum msg_type type; /* The type of this message */
|
|
guint32 seen; /* Cumulative flags of previous messages */
|
|
} pdu_data;
|
|
|
|
/*
|
|
* Per conversation data, there should be one of these for every detected
|
|
* conversation between ceph entities. The to/from direction is arbitrary,
|
|
* we probably don't know who the entities are when we establish the
|
|
* conversation.
|
|
*/
|
|
typedef struct _conv_data {
|
|
emem_tree_t *pdu_table; /* Message tree, stores accumulated pdu_data */
|
|
entity_data* to; /* Who is this to */
|
|
entity_data* from; /* Who is this from */
|
|
guint direction_set; /* Do we know to/from set correctly */
|
|
char* displayT2F; /* Cached 'to' to 'from' display string */
|
|
char* displayF2T; /* Cached 'from' to 'to' display string */
|
|
} conv_data;
|
|
|
|
/*
|
|
* Find/create a conversation for the given source & destination
|
|
*/
|
|
static conv_data*
|
|
conv_get(packet_info *pinfo) {
|
|
conversation_t* conv;
|
|
conv_data* cdata;
|
|
|
|
/* Have we seen this conversation before? */
|
|
conv = conv_find_exact(pinfo->fd->num, &pinfo->src, pinfo->srcport,
|
|
&pinfo->dst, pinfo->destport);
|
|
if (conv == NULL) {
|
|
conv = conversation_new(pinfo->fd->num, &pinfo->src,
|
|
&pinfo->dst, pinfo->ptype,
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
conv = conv_find_exact(pinfo->fd->num, &pinfo->src, pinfo->srcport,
|
|
&pinfo->dst, pinfo->destport);
|
|
}
|
|
|
|
cdata=(conv_data*)conversation_get_proto_data(conv, g_proto_ceph);
|
|
if (cdata==NULL) {
|
|
cdata = (conv_data*)se_alloc(sizeof(conv_data));
|
|
cdata->pdu_table = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "ceph_pdu");
|
|
cdata->to = conv_get_entity(&pinfo->dst, pinfo->destport);
|
|
cdata->from = conv_get_entity(&pinfo->src, pinfo->srcport);
|
|
cdata->direction_set = FALSE;
|
|
cdata->displayT2F=NULL;
|
|
cdata->displayF2T=NULL;
|
|
conversation_add_proto_data(conv, g_proto_ceph, cdata);
|
|
}
|
|
return cdata;
|
|
}
|
|
|
|
/*
|
|
* Reverse to/from in a stream to correct a wrong guess
|
|
*/
|
|
static void
|
|
conv_fixup(guint clientSrc, packet_info *pinfo) {
|
|
conversation_t* conv;
|
|
conv_data* cdata;
|
|
entity_data* edata;
|
|
guint fromClient;
|
|
pdu_data* pdata;
|
|
guint position;
|
|
|
|
conv = conv_find_exact(pinfo->fd->num, &pinfo->src, pinfo->srcport,
|
|
&pinfo->dst, pinfo->destport);
|
|
if (conv != NULL) {
|
|
/* Do we need to reverse because from & to mixup */
|
|
cdata=(conv_data*)conversation_get_proto_data(conv, g_proto_ceph);
|
|
if (cdata->direction_set==FALSE) {
|
|
cdata->direction_set=TRUE;
|
|
fromClient = ADDRESSES_EQUAL(&pinfo->src, &cdata->from->addr) &&
|
|
pinfo->srcport == cdata->from->port;
|
|
if (clientSrc != fromClient) {
|
|
edata = cdata->from;
|
|
cdata->from = cdata->to;
|
|
cdata->to = edata;
|
|
cdata->displayF2T = NULL;
|
|
cdata->displayT2F = NULL;
|
|
}
|
|
|
|
/* & Reverse pdata as well correcting SERVER_BANNER if needed */
|
|
position = 0xFFFFFFFF;
|
|
while ((pdata = (pdu_data*)se_tree_lookup32_le(
|
|
cdata->pdu_table, position))!=NULL) {
|
|
if (clientSrc != fromClient)
|
|
pdata->toserver = !pdata->toserver;
|
|
if (pdata->toserver && pdata->type == SERVER_BANNER) {
|
|
pdata->type = CLIENT_BANNER;
|
|
pdata->seen &= (~SERVER_BANNER);
|
|
pdata->seen |= CLIENT_BANNER;
|
|
}
|
|
position = pdata->position -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate the stream index for a given offset
|
|
*/
|
|
static guint32
|
|
conv_position(packet_info *pinfo, tvbuff_t *tvb, guint32 offset) {
|
|
return pinfo->fd->cum_bytes - tvb_reported_length_remaining (tvb, offset);
|
|
}
|
|
|
|
static guint32
|
|
conv_expecting(packet_info *pinfo, tvbuff_t *tvb, guint32 offset) {
|
|
conv_data* cdata;
|
|
pdu_data* pdata;
|
|
guint32 allowed;
|
|
guint32 position;
|
|
|
|
cdata = conv_get(pinfo);
|
|
|
|
/*
|
|
* Return what should be happening next
|
|
*/
|
|
position = conv_position(pinfo, tvb, offset);
|
|
pdata = (pdu_data*)se_tree_lookup32_le(cdata->pdu_table, position);
|
|
|
|
/*
|
|
* If new may see any message type
|
|
*/
|
|
if (pdata == 0)
|
|
return MSG_ANY;
|
|
|
|
/*
|
|
* If exact match, return what we actually saw in previous dissect
|
|
*/
|
|
if (pdata->position == position)
|
|
return pdata->type;
|
|
|
|
/*
|
|
* Otherwise, work out what could be next, the difficulty here is that
|
|
* the client banner may appear before or after the server sends its
|
|
* addresses and also that we me mistake a client banner for a server
|
|
* banner
|
|
*/
|
|
allowed = 0;
|
|
switch (pdata->type) {
|
|
case SERVER_BANNER:
|
|
return (CLIENT_BANNER | SERVER_ADDRESS | CLIENT_ADDRESS);
|
|
case CLIENT_BANNER:
|
|
if ((pdata->seen & SERVER_ADDRESS) == 0)
|
|
allowed = SERVER_ADDRESS;
|
|
allowed |= CLIENT_ADDRESS;
|
|
break;
|
|
case SERVER_ADDRESS:
|
|
if ((pdata->seen & CLIENT_BANNER) == 0)
|
|
allowed = CLIENT_BANNER;
|
|
allowed |= CLIENT_ADDRESS;
|
|
break;
|
|
case CLIENT_ADDRESS:
|
|
allowed = CONNECT_REQUEST;
|
|
break;
|
|
case CONNECT_REQUEST:
|
|
allowed = CONNECT_REPLY;
|
|
break;
|
|
case CONNECT_REPLY:
|
|
default:
|
|
allowed = CONVERSING;
|
|
}
|
|
return allowed;
|
|
}
|
|
|
|
/*
|
|
* Tag a position in a conversation as a specific type
|
|
*/
|
|
static void
|
|
conv_tag(packet_info *pinfo, tvbuff_t *tvb,
|
|
guint32 offset, enum msg_type type) {
|
|
conv_data* cdata;
|
|
pdu_data* pdata;
|
|
pdu_data* previous_pdata;
|
|
guint32 position;
|
|
|
|
/*
|
|
* Get/Create a pdu_data to describe the message
|
|
*/
|
|
cdata = conv_get(pinfo);
|
|
position = conv_position(pinfo, tvb, offset);
|
|
pdata = (pdu_data*)se_tree_lookup32(cdata->pdu_table, position);
|
|
|
|
if (pdata == 0) {
|
|
pdata = (pdu_data*)se_alloc(sizeof(pdu_data));
|
|
pdata->position = position;
|
|
pdata->toserver = (ADDRESSES_EQUAL(&cdata->to->addr, &pinfo->dst) &&
|
|
cdata->to->port == pinfo->destport);
|
|
pdata->type = type;
|
|
pdata->seen = type;
|
|
|
|
/*
|
|
* If new we have to carry forward previously seen to allow for
|
|
* checking on interleaving of messages, a pain I know..
|
|
*/
|
|
previous_pdata = (pdu_data*)se_tree_lookup32_le(cdata->pdu_table,
|
|
position-1);
|
|
if (previous_pdata != 0) {
|
|
pdata->seen |= previous_pdata->seen;
|
|
}
|
|
|
|
/*
|
|
* Insert new record
|
|
*/
|
|
se_tree_insert32(cdata->pdu_table, position, pdata);
|
|
|
|
} else {
|
|
/*
|
|
* Just update the existing record
|
|
*/
|
|
pdata->type = type;
|
|
pdata->seen |= type;
|
|
}
|
|
}
|
|
|
|
static void
|
|
conv_setfrom(packet_info *pinfo, entity_type type) {
|
|
conv_data* cdata;
|
|
|
|
cdata = conv_get(pinfo);
|
|
cdata->from->type = type;
|
|
cdata->displayT2F = NULL;
|
|
cdata->displayF2T = NULL;
|
|
}
|
|
|
|
static char*
|
|
conv_format(const entity_data* from, const entity_data* to) {
|
|
const char* from_name;
|
|
const char* to_name;
|
|
|
|
from_name = entityTypeDescription(from->type);
|
|
if (from->name) {
|
|
from_name = se_strdup_printf("%s.%s",
|
|
entityTypeDescription(from->type), from->name);
|
|
}
|
|
|
|
to_name = entityTypeDescription(to->type);
|
|
if (to->name) {
|
|
to_name = se_strdup_printf("%s.%s",
|
|
entityTypeDescription(to->type), to->name);
|
|
}
|
|
|
|
return se_strdup_printf("%s -> %s", from_name, to_name);
|
|
}
|
|
|
|
/*
|
|
* Get conversation display string
|
|
*/
|
|
static char*
|
|
conv_display(packet_info *pinfo) {
|
|
conv_data* cdata;
|
|
guint to;
|
|
|
|
cdata = conv_get(pinfo);
|
|
to = ADDRESSES_EQUAL(&cdata->to->addr, &pinfo->dst) &&
|
|
cdata->to->port == pinfo->destport;
|
|
|
|
if (to) {
|
|
if (cdata->displayF2T == NULL) {
|
|
cdata->displayF2T = conv_format(cdata->from, cdata->to);
|
|
}
|
|
return cdata->displayF2T;
|
|
} else {
|
|
if (cdata->displayT2F == NULL) {
|
|
cdata->displayT2F = conv_format(cdata->to, cdata->from);
|
|
}
|
|
return cdata->displayT2F;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sockaddr details extracted from protocol
|
|
* Note: Don't map from tvb, only assign to avoid portability concerns
|
|
*/
|
|
typedef struct _sockaddr_info {
|
|
guint16 port;
|
|
guint32 addr;
|
|
} sockaddr_info;
|
|
|
|
/*
|
|
* Entity info as extracted from protocol
|
|
* Note: Don't map from tvb, only assign to avoid portability concerns
|
|
*/
|
|
typedef struct _entity_info {
|
|
guint32 erank;
|
|
guint32 nonce;
|
|
sockaddr_info addr;
|
|
} entity_info;
|
|
|
|
/*
|
|
* Recover conversation entity data from entity_info
|
|
*/
|
|
static entity_data*
|
|
conv_get_entity_frominfo(entity_info* info) {
|
|
address addr;
|
|
|
|
SET_ADDRESS(&addr, AT_IPv4, sizeof(info->addr.addr), &info->addr.addr);
|
|
return conv_get_entity(&addr, info->addr.port);
|
|
}
|
|
|
|
/*
|
|
* Set type & name for an entity
|
|
* nameOffset is offset into tvb of where to find FT_UINT_STRING
|
|
*/
|
|
static void
|
|
conv_set_entity_name(entity_data* edata, tvbuff_t *tvb,
|
|
guint32 nameOffset, entity_type type) {
|
|
guint32 len;
|
|
|
|
if (type != ENTITY_TYPE_UNKNOWN)
|
|
edata->type = type;
|
|
|
|
/* Same name? */
|
|
len = tvb_get_letohl(tvb, nameOffset);
|
|
if (edata->name) {
|
|
if (strlen(edata->name) == len &&
|
|
tvb_memeql(tvb, nameOffset+4, edata->name, len)) {
|
|
return;
|
|
}
|
|
}
|
|
edata->name = (char*)se_alloc(len+1);
|
|
tvb_memcpy(tvb, edata->name, nameOffset+4,len);
|
|
edata->name[len]=0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* INFO Column helpers
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* Flag for controlling formatting of INFO column handling, this needs
|
|
* a manual reset before decoding possible multiple PDUs
|
|
*/
|
|
static guint g_firstPdu;
|
|
|
|
/*
|
|
* Helper to prepare for a PDU display by setting info column.
|
|
*/
|
|
static void
|
|
setPDUInfo(packet_info *pinfo, const char* info) {
|
|
|
|
/*
|
|
* Reset info column, with a fence to stop being changed by other PDUs that
|
|
* might be contained in the same packet.
|
|
*/
|
|
if(check_col(pinfo->cinfo,COL_INFO)){
|
|
if (g_firstPdu == TRUE)
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, " %s", info);
|
|
else
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, ", %s", info);
|
|
col_set_fence(pinfo->cinfo,COL_INFO);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Start of dissector handlers
|
|
* NOTES:
|
|
* The coding style is odd, we avoid the normal use of structures to describe
|
|
* on the wire formats because of the difficulty of getting *all* compilers
|
|
* to handle the packing correctly. Wireshark coding style prefers direct
|
|
* decoding so that is what we do. I have included structures in comments
|
|
* just to make it easier to understand what the code is pulling apart.
|
|
*
|
|
* The dissector functions *may* check the data contents for sanity to allow
|
|
* them to be used where the content type is ambiguous. If the data provided
|
|
* can not be for that dissector the return will be 0. If there is not enough
|
|
* data then they return -1. Otherwise they return the number of bytes they
|
|
* consumed so they can be used to dissect multiple PDUs per packet.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* Message tag types, numbers must match protocol
|
|
* There are two types of tags here, most are used with connect_reply to
|
|
* indicate status, but CLOSE, MSG, ACK & KEEPALIVE tags are used for
|
|
* standalone messages. TAG_SEQ is another oddball, it indicates the server
|
|
* is requesting an exhachange of sequence numbers following the connect_reply.
|
|
*/
|
|
enum msg_tag {
|
|
MSGR_TAG_MIN=1,
|
|
MSGR_TAG_READY=1, /* S->C: ready for messages */
|
|
MSGR_TAG_RESETSESSION=2, /* S->C: reset, try again */
|
|
MSGR_TAG_WAIT=3, /* S->C: wait for racing incoming connection */
|
|
MSGR_TAG_RETRY_SESSION=4, /* S->C + cseq: try again with higher cseq */
|
|
MSGR_TAG_RETRY_GLOBAL=5, /* S->C + gseq: try again with higher gseq */
|
|
MSGR_TAG_CLOSE=6, /* closing pipe */
|
|
MSGR_TAG_MSG=7, /* message */
|
|
MSGR_TAG_ACK=8, /* message ack */
|
|
MSGR_TAG_KEEPALIVE=9, /* just a keepalive byte! */
|
|
MSGR_TAG_BADPROTOVER=10, /* bad protocol version */
|
|
MSGR_TAG_BADAUTHORIZER=11, /* bad authorizer */
|
|
MSGR_TAG_FEATURES=12, /* insufficient features */
|
|
MSGR_TAG_SEQ=13, /* 64-bit int follows with seen seq number */
|
|
MSGR_TAG_MAX=13
|
|
};
|
|
|
|
static guint
|
|
isMsgrTag(guint tag) {
|
|
return (tag>=MSGR_TAG_MIN && tag<=MSGR_TAG_MAX);
|
|
}
|
|
|
|
static guint
|
|
isMsgrConnectTag(guint tag) {
|
|
return isMsgrTag(tag) &&
|
|
(tag != MSGR_TAG_CLOSE) &&
|
|
(tag != MSGR_TAG_MSG) &&
|
|
(tag != MSGR_TAG_ACK) &&
|
|
(tag != MSGR_TAG_KEEPALIVE);
|
|
}
|
|
|
|
static const char*
|
|
replyTagDescription(guint tag) {
|
|
switch (tag) {
|
|
case MSGR_TAG_READY: return "Ready";
|
|
case MSGR_TAG_RESETSESSION: return "Reset Session";
|
|
case MSGR_TAG_WAIT: return "Wait";
|
|
case MSGR_TAG_RETRY_SESSION: return "Retry Session";
|
|
case MSGR_TAG_RETRY_GLOBAL: return "Retry Global";
|
|
case MSGR_TAG_BADPROTOVER: return "Bad Protocol";
|
|
case MSGR_TAG_BADAUTHORIZER: return "Bad Authorizer";
|
|
case MSGR_TAG_FEATURES: return "Missing Features";
|
|
case MSGR_TAG_SEQ: return "Sequence";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Handshake handling (Banners & Addresses)
|
|
****************************************************************************/
|
|
|
|
#define CEPH_BANNER "ceph v027"
|
|
#define CEPH_BANNER_LEN (sizeof(CEPH_BANNER)-1)
|
|
#define CEPH_BANNER_MAGIC_LEN 4
|
|
|
|
/*
|
|
* Dissect a banner, this can be used in either direction, client to server
|
|
* or server to client.
|
|
*/
|
|
static gint
|
|
dissect_banner(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset, guint client)
|
|
{
|
|
proto_tree *ceph_banner_tree = NULL;
|
|
proto_item *ceph_sub_item = NULL;
|
|
|
|
if (tvb_strneql(tvb, offset, CEPH_BANNER, CEPH_BANNER_LEN) == 0) {
|
|
|
|
/* All OK, just show the banner */
|
|
setPDUInfo(pinfo, client?"Client Banner":"Server Banner");
|
|
ceph_sub_item = proto_tree_add_item( tree, hf_banner, tvb, offset,
|
|
CEPH_BANNER_LEN, TRUE );
|
|
ceph_banner_tree = proto_item_add_subtree(ceph_sub_item, ett_banner);
|
|
proto_tree_add_item(ceph_banner_tree, hf_banner_magic, tvb, offset,
|
|
CEPH_BANNER_MAGIC_LEN, TRUE);
|
|
proto_tree_add_item(ceph_banner_tree, hf_banner_version, tvb, offset+
|
|
CEPH_BANNER_MAGIC_LEN, CEPH_BANNER_LEN-CEPH_BANNER_MAGIC_LEN, TRUE);
|
|
|
|
return CEPH_BANNER_LEN;
|
|
} else if (tvb_reported_length_remaining (tvb, offset) >=
|
|
(gint)CEPH_BANNER_LEN) {
|
|
/* Enough data, but not a banner */
|
|
return 0;
|
|
} else {
|
|
/* Not enough data */
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Supported family types, these are defined here for portability.
|
|
*/
|
|
typedef enum _families {
|
|
CEPH_AF_INET=2,
|
|
CEPH_AF_INET6=10
|
|
} families;
|
|
|
|
static guint
|
|
isFamily(guint32 family) {
|
|
return ((family==CEPH_AF_INET) || (family==CEPH_AF_INET6));
|
|
}
|
|
|
|
static const char*
|
|
familyDescription(guint family) {
|
|
switch (family) {
|
|
case CEPH_AF_INET: return "inet V4";
|
|
case CEPH_AF_INET6: return "inet V6";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Storage structure for addresses, can really only be AF_INET or AF_INET6
|
|
* but this is the structure used by the protocol.
|
|
*
|
|
struct ceph_sockaddr_storage {
|
|
guint16 ss_family;
|
|
guint8 __ss_pad1[6];
|
|
guint64 __ss_align;
|
|
guint8 __ss_pad2[112];
|
|
}
|
|
*/
|
|
#define SIZE_OF_SOCKADDR_STORAGE (2+6+8+112)
|
|
|
|
/*
|
|
* Dissect a sockaddr_storage. This may be a IPv4 or IPv6 format but currently
|
|
* only IPv4 is handled.
|
|
*/
|
|
static gint
|
|
dissect_sockaddr_in(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset, sockaddr_info* info)
|
|
{
|
|
proto_tree *ceph_sockaddr_tree;
|
|
proto_item *ceph_sub_item = NULL;
|
|
guint16 family;
|
|
|
|
ceph_sub_item = proto_tree_add_item( tree, hf_sockaddr_in, tvb, offset,
|
|
16, TRUE );
|
|
ceph_sockaddr_tree = proto_item_add_subtree(ceph_sub_item, ett_sockaddr_in);
|
|
|
|
family = tvb_get_ntohs(tvb, offset);
|
|
if (family == CEPH_AF_INET6) {
|
|
expert_add_info_format(pinfo, ceph_sub_item, PI_UNDECODED, PI_WARN,
|
|
"Address using IPv6 addressing, dissecting is not yet supported");
|
|
} else if (family == CEPH_AF_INET) {
|
|
proto_tree_add_uint_format_value(ceph_sockaddr_tree, hf_sin_family, tvb,
|
|
offset, 2, family, "%d (%s)", family, familyDescription(family));
|
|
proto_tree_add_item(ceph_sockaddr_tree, hf_sin_port, tvb, offset+2,
|
|
2, ENC_BIG_ENDIAN);
|
|
proto_tree_add_item(ceph_sockaddr_tree, hf_sin_addr, tvb, offset+4,
|
|
4, FALSE);
|
|
if (info) {
|
|
info->port = tvb_get_ntohs(tvb, offset+2);
|
|
info->addr = tvb_get_letohl(tvb, offset+4);
|
|
}
|
|
} else {
|
|
expert_add_info_format(pinfo, ceph_sub_item, PI_UNDECODED, PI_WARN,
|
|
"Unknown family (%d) being used in address", family);
|
|
}
|
|
offset += 16;
|
|
return offset;
|
|
}
|
|
|
|
/*
|
|
* entity_addr - endpoint details including IP address
|
|
*
|
|
struct ceph_entity_addr {
|
|
guint32 type;
|
|
guint32 nonce; // unique id for process (e.g. pid)
|
|
struct ceph_sockaddr_storage in_addr;
|
|
};
|
|
*/
|
|
#define SIZE_OF_ENTITY_ADDR (4+4+SIZE_OF_SOCKADDR_STORAGE)
|
|
|
|
/*
|
|
* Dissect an entity address, just a type
|
|
*/
|
|
static gint
|
|
dissect_entity_addr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset, entity_info* info)
|
|
{
|
|
proto_tree *ceph_entity_tree = NULL;
|
|
proto_item *ceph_sub_item = NULL;
|
|
guint16 family;
|
|
|
|
/* Check the sockaddr_in looks sane before full decoding */
|
|
if (tvb_reported_length_remaining (tvb, offset) < SIZE_OF_ENTITY_ADDR)
|
|
return -1;
|
|
|
|
/* TODO: Could really do with better sanity checking here */
|
|
family = tvb_get_ntohs(tvb, offset+8);
|
|
if (family != CEPH_AF_INET && family!= CEPH_AF_INET6) {
|
|
return 0;
|
|
}
|
|
|
|
/* Decode it */
|
|
if (tree) {
|
|
ceph_sub_item = proto_tree_add_item( tree, hf_entity_addr, tvb, offset,
|
|
SIZE_OF_ENTITY_ADDR, TRUE );
|
|
ceph_entity_tree = proto_item_add_subtree(ceph_sub_item,
|
|
ett_entity_addr);
|
|
proto_tree_add_item(ceph_entity_tree, hf_entity_erank, tvb, offset,
|
|
4, TRUE);
|
|
proto_tree_add_item(ceph_entity_tree, hf_entity_nonce, tvb, offset+4,
|
|
4, TRUE);
|
|
}
|
|
|
|
dissect_sockaddr_in(tvb, pinfo, ceph_entity_tree, offset+8,
|
|
info? &info->addr:NULL);
|
|
|
|
if (info) {
|
|
info->erank = tvb_get_letohl(tvb, offset);
|
|
info->nonce = tvb_get_letohl(tvb, offset+4);
|
|
}
|
|
|
|
return SIZE_OF_ENTITY_ADDR;
|
|
}
|
|
|
|
/*
|
|
* server-address - passed from server to client during handshake
|
|
*
|
|
struct ceph_server_address {
|
|
struct ceph_entity_addr server_addr;
|
|
struct ceph_entity_addr client_addr;
|
|
};
|
|
*/
|
|
static gint
|
|
dissect_server_address(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset) {
|
|
gint len1;
|
|
gint len2;
|
|
|
|
len1 = dissect_entity_addr(tvb, pinfo, tree, offset, NULL);
|
|
if (len1 > 0) {
|
|
offset += len1;
|
|
len2 = dissect_entity_addr(tvb, pinfo, tree, offset, NULL);
|
|
if (len2>0) {
|
|
setPDUInfo(pinfo, "Server Addresses");
|
|
conv_fixup(FALSE, pinfo);
|
|
return len1+len2;
|
|
}
|
|
return len2;
|
|
}
|
|
return len1;
|
|
}
|
|
|
|
/*
|
|
* client-address - passed from client to server during handshake
|
|
*
|
|
struct ceph_client_address {
|
|
struct ceph_entity_addr client_addr;
|
|
};
|
|
*/
|
|
static gint
|
|
dissect_client_address(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset) {
|
|
gint len;
|
|
|
|
/* To avoid identifying a server address as a client address we rely on the
|
|
* server sending its data un-fragmented after a banner, so either we have
|
|
* data for two valid addresses or this must be a client address.
|
|
*/
|
|
if (tvb_reported_length_remaining (tvb, offset) >= 2*SIZE_OF_ENTITY_ADDR) {
|
|
/* Might be a server address, peek to see */
|
|
if (isFamily(tvb_get_ntohs(tvb, offset+8)) &&
|
|
isFamily(tvb_get_ntohs(tvb, offset+SIZE_OF_ENTITY_ADDR+8)))
|
|
return 0;
|
|
}
|
|
|
|
/* Is it just a client addr */
|
|
len=dissect_entity_addr(tvb, pinfo, tree, offset, NULL);
|
|
if (len>0) {
|
|
setPDUInfo(pinfo,"Client Address");
|
|
conv_fixup(TRUE, pinfo);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Connection Request & Reply
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* msg_connect - Client->Server connection request
|
|
*
|
|
struct ceph_msg_connect {
|
|
guint64 features; // supported feature bits
|
|
guint32 host_type; // CEPH_ENTITY_TYPE_*
|
|
guint32 global_seq; // count connections initiated by this host
|
|
guint32 connect_seq; // count connections initiated in this session
|
|
guint32 protocol_version;
|
|
guint32 authorizer_protocol;
|
|
guint32 authorizer_len;
|
|
guint8 flags; // CEPH_MSG_CONNECT_*
|
|
}
|
|
*/
|
|
#define SIZE_OF_MSG_CONNECT (8+4+4+4+4+4+4+1)
|
|
|
|
#define FEATURE_UID G_GINT64_CONSTANT(1<<0)
|
|
#define FEATURE_NOSRCADDR G_GINT64_CONSTANT(1<<1)
|
|
#define FEATURE_MONCLOCKCHECK G_GINT64_CONSTANT(1<<2)
|
|
#define FEATURE_FLOCK G_GINT64_CONSTANT(1<<3)
|
|
#define FEATURE_SUBSCRIBE2 G_GINT64_CONSTANT(1<<4)
|
|
#define FEATURE_MONNAMES G_GINT64_CONSTANT(1<<5)
|
|
#define FEATURE_RECONNECT_SEQ G_GINT64_CONSTANT(1<<6)
|
|
#define FEATURE_DIRLAYOUTHASH G_GINT64_CONSTANT(1<<7)
|
|
#define FEATURE_OBJECTLOCATOR G_GINT64_CONSTANT(1<<8)
|
|
#define FEATURE_PGID64 G_GINT64_CONSTANT(1<<9)
|
|
#define FEATURE_INCSUBOSDMAP G_GINT64_CONSTANT(1<<10)
|
|
#define FEATURE_PGPOOL3 G_GINT64_CONSTANT(1<<11)
|
|
#define FEATURE_OSDREPLYMUX G_GINT64_CONSTANT(1<<12)
|
|
#define FEATURE_OSDENC G_GINT64_CONSTANT(1<<13)
|
|
#define FEATURE_OMAP G_GINT64_CONSTANT(1<<14)
|
|
#define FEATURE_MONENC G_GINT64_CONSTANT(1<<15)
|
|
#define FEATURE_QUERY_T G_GINT64_CONSTANT(1<<16)
|
|
#define FEATURE_INDEP_PG_MAP G_GINT64_CONSTANT(1<<17)
|
|
#define FEATURE_CRUSH_TUNABLES G_GINT64_CONSTANT(1<<18)
|
|
#define FEATURE_CHUNKY_SCRUB G_GINT64_CONSTANT(1<<19)
|
|
#define FEATURE_MON_NULLROUTE G_GINT64_CONSTANT(1<<20)
|
|
#define FEATURE_MON_GV G_GINT64_CONSTANT(1<<21)
|
|
#define FEATURE_BACKFILL_RESERVATION G_GINT64_CONSTANT(1<<22)
|
|
#define FEATURE_MSG_AUTH G_GINT64_CONSTANT(1<<23)
|
|
#define FEATURE_RECOVERY_RESERVATION G_GINT64_CONSTANT(1<<24)
|
|
#define FEATURE_CRUSH_TUNABLES2 G_GINT64_CONSTANT(1<<25)
|
|
#define FEATURE_CREATEPOOLID G_GINT64_CONSTANT(1<<26)
|
|
#define FEATURE_REPLY_CREATE_INODE G_GINT64_CONSTANT(1<<27)
|
|
#define FEATURE_OSD_HBMSGS G_GINT64_CONSTANT(1<<28)
|
|
#define FEATURE_MDSENC G_GINT64_CONSTANT(1<<29)
|
|
#define FEATURE_OSDHASHPSPOOL G_GINT64_CONSTANT(1<<30)
|
|
#define FEATURE_MON_SINGLE_PAXOS G_GINT64_CONSTANT(1<<31)
|
|
#define FEATURE_OSD_SNAPMAPPER G_GINT64_CONSTANT(0x100000000)
|
|
|
|
#define FEATURES_ALL \
|
|
(FEATURE_UID | \
|
|
FEATURE_NOSRCADDR | \
|
|
FEATURE_MONCLOCKCHECK | \
|
|
FEATURE_FLOCK | \
|
|
FEATURE_SUBSCRIBE2 | \
|
|
FEATURE_MONNAMES | \
|
|
FEATURE_RECONNECT_SEQ | \
|
|
FEATURE_DIRLAYOUTHASH | \
|
|
FEATURE_OBJECTLOCATOR | \
|
|
FEATURE_PGID64 | \
|
|
FEATURE_INCSUBOSDMAP | \
|
|
FEATURE_PGPOOL3 | \
|
|
FEATURE_OSDREPLYMUX | \
|
|
FEATURE_OSDENC | \
|
|
FEATURE_OMAP | \
|
|
FEATURE_QUERY_T | \
|
|
FEATURE_MONENC | \
|
|
FEATURE_INDEP_PG_MAP | \
|
|
FEATURE_CRUSH_TUNABLES | \
|
|
FEATURE_CHUNKY_SCRUB | \
|
|
FEATURE_MON_NULLROUTE | \
|
|
FEATURE_MON_GV | \
|
|
FEATURE_BACKFILL_RESERVATION | \
|
|
FEATURE_MSG_AUTH | \
|
|
FEATURE_RECOVERY_RESERVATION | \
|
|
FEATURE_CRUSH_TUNABLES2 | \
|
|
FEATURE_CREATEPOOLID | \
|
|
FEATURE_REPLY_CREATE_INODE | \
|
|
FEATURE_OSD_HBMSGS | \
|
|
FEATURE_MDSENC | \
|
|
FEATURE_OSDHASHPSPOOL | \
|
|
FEATURE_MON_SINGLE_PAXOS | \
|
|
FEATURE_OSD_SNAPMAPPER)
|
|
|
|
struct feature_description {
|
|
guint64 feature;
|
|
const char* description;
|
|
};
|
|
|
|
struct feature_description fdesc[] = {
|
|
{FEATURE_UID, "uid"},
|
|
{FEATURE_NOSRCADDR, "nosrcaddr"},
|
|
{FEATURE_MONCLOCKCHECK, "monclockcheck"},
|
|
{FEATURE_FLOCK, "flock"},
|
|
{FEATURE_SUBSCRIBE2, "subscribe2"},
|
|
{FEATURE_MONNAMES, "monnames"},
|
|
{FEATURE_RECONNECT_SEQ, "reconnect_seq"},
|
|
{FEATURE_DIRLAYOUTHASH, "dirlayouthash"},
|
|
{FEATURE_OBJECTLOCATOR, "objectlocator"},
|
|
{FEATURE_PGID64, "pgid64"},
|
|
{FEATURE_INCSUBOSDMAP, "incsubosdmap"},
|
|
{FEATURE_PGPOOL3, "pgpool3"},
|
|
{FEATURE_OSDREPLYMUX, "osdreplymux"},
|
|
{FEATURE_OSDENC, "osdenc"},
|
|
{FEATURE_OMAP, "omap"},
|
|
{FEATURE_QUERY_T, "query_t"},
|
|
{FEATURE_MONENC, "monenc"},
|
|
{FEATURE_INDEP_PG_MAP, "indep_pg_map"},
|
|
{FEATURE_CRUSH_TUNABLES, "crush_tunables"},
|
|
{FEATURE_CHUNKY_SCRUB, "chunk_scrub"},
|
|
{FEATURE_MON_NULLROUTE, "mon_nullroute"},
|
|
{FEATURE_MON_GV, " mon_gv"},
|
|
{FEATURE_BACKFILL_RESERVATION, "backfill_reservation"},
|
|
{FEATURE_MSG_AUTH, "msg_auth"},
|
|
{FEATURE_RECOVERY_RESERVATION, "recovery_reservation"},
|
|
{FEATURE_CRUSH_TUNABLES2, "crush_tunables2"},
|
|
{FEATURE_CREATEPOOLID, "createpoolid"},
|
|
{FEATURE_REPLY_CREATE_INODE, "reply_create_inode"},
|
|
{FEATURE_OSD_HBMSGS, "osd_hbmsgs"},
|
|
{FEATURE_MDSENC, "mdsenc"},
|
|
{FEATURE_OSDHASHPSPOOL, "osdhashpspool"},
|
|
{FEATURE_MON_SINGLE_PAXOS, "mon_single_paxos"},
|
|
{FEATURE_OSD_SNAPMAPPER, "osd_snapmapper"}
|
|
};
|
|
#define FEATURE_COUNT (sizeof(fdesc)/sizeof(struct feature_description))
|
|
|
|
static char* featureDescription(guint64 features) {
|
|
guint i;
|
|
char *desc;
|
|
char *next;
|
|
|
|
if (features == (guint64)FEATURES_ALL) {
|
|
return g_strdup("All features");
|
|
} else {
|
|
desc=g_strdup("");
|
|
for (i=0; i< FEATURE_COUNT; i++) {
|
|
if (features & fdesc[i].feature) {
|
|
next = g_strconcat(desc, fdesc[i].description, " ", NULL);
|
|
g_free(desc);
|
|
desc = next;
|
|
}
|
|
}
|
|
return desc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Connect message flags, numbers must match protocol
|
|
* There is only a lossy flag, maybe more will be added later
|
|
*/
|
|
typedef enum _connect_flags {
|
|
MSG_CONNECT_MIN=1,
|
|
MSG_CONNECT_LOSSY=1,
|
|
MSG_CONNECT_MAX=1
|
|
} connect_flags;
|
|
|
|
static const char*
|
|
connectFlagDescription(guint flag) {
|
|
switch (flag) {
|
|
case MSG_CONNECT_LOSSY: return "Lossy";
|
|
}
|
|
return "No Flags";
|
|
}
|
|
|
|
static void
|
|
format_connect_features(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset) {
|
|
proto_item* item;
|
|
guint64 features;
|
|
char* desc;
|
|
char linebuf[201];
|
|
guint len, start, end;
|
|
|
|
features = tvb_get_letoh64(tvb, offset);
|
|
desc = featureDescription(features);
|
|
len = strlen(desc);
|
|
if (len < 150) {
|
|
/* Short description on one line */
|
|
item = proto_tree_add_uint64_format_value(tree, hf_connect_features,
|
|
tvb, offset, 8, features, "0x%" G_GINT64_MODIFIER "x (%s)",
|
|
features, desc);
|
|
} else {
|
|
|
|
/* Format as multi-line entry */
|
|
item = proto_tree_add_uint64_format_value(tree, hf_connect_features,
|
|
tvb, offset, 8, features, "0x%" G_GINT64_MODIFIER "x", features);
|
|
|
|
start = 0;
|
|
while (start < len) {
|
|
end = start + (strlen(&desc[start])>200 ? 200 :
|
|
strlen(&desc[start]));
|
|
while (end>len || desc[end-1] != ' ')
|
|
end--;
|
|
strncpy(linebuf, &desc[start], end-start);
|
|
linebuf[end-start]=0;
|
|
proto_tree_add_uint64_format_value(tree, hf_connect_features, tvb,
|
|
offset, 8, features, "%s", linebuf);
|
|
start = end;
|
|
}
|
|
}
|
|
g_free(desc);
|
|
|
|
if ((features & (~FEATURES_ALL)) != 0) {
|
|
expert_add_info_format(pinfo, item, PI_UNDECODED, PI_WARN,
|
|
"Unexpected feature flags seen, dissector may need updating");
|
|
}
|
|
}
|
|
|
|
static gint
|
|
dissect_connect_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset) {
|
|
|
|
gint auth_len;
|
|
entity_type host_type;
|
|
guint flags;
|
|
|
|
if (tvb_reported_length_remaining (tvb, offset) < SIZE_OF_MSG_CONNECT)
|
|
return -1;
|
|
|
|
host_type = (entity_type)tvb_get_letohl(tvb, offset+8);
|
|
if (!isEntityType(host_type))
|
|
return 0;
|
|
|
|
auth_len = tvb_get_letohl(tvb, offset+28);
|
|
if (tvb_reported_length_remaining (tvb, offset) <
|
|
SIZE_OF_MSG_CONNECT+auth_len)
|
|
return -1;
|
|
|
|
/* All here, just decode */
|
|
setPDUInfo(pinfo, "Connect Request");
|
|
format_connect_features(tvb, pinfo, tree, offset);
|
|
proto_tree_add_uint_format_value(tree, hf_connect_host_type, tvb, offset+8,
|
|
4, host_type, "%d (%s)", host_type, entityTypeDescription(host_type));
|
|
proto_tree_add_item(tree, hf_connect_global_seq, tvb, offset+12, 4, TRUE);
|
|
proto_tree_add_item(tree, hf_connect_connect_seq, tvb, offset+16, 4, TRUE);
|
|
proto_tree_add_item(tree, hf_connect_protocol_version, tvb, offset+20,
|
|
4, TRUE);
|
|
proto_tree_add_item(tree, hf_connect_authorizer_protocol, tvb, offset+24,
|
|
4, TRUE);
|
|
proto_tree_add_item(tree, hf_connect_authorizer_len, tvb, offset+28,
|
|
4, TRUE);
|
|
flags = tvb_get_guint8(tvb, offset+32);
|
|
proto_tree_add_uint_format_value(tree, hf_connect_flags, tvb, offset+32, 1,
|
|
flags, "%d (%s)", flags, connectFlagDescription(flags));
|
|
|
|
if (auth_len >0)
|
|
proto_tree_add_item(tree, hf_connect_authentication_key, tvb, offset+33,
|
|
auth_len, TRUE);
|
|
|
|
/* Tag it in conv */
|
|
conv_fixup(TRUE, pinfo);
|
|
conv_tag(pinfo, tvb, offset, CONNECT_REQUEST);
|
|
conv_setfrom(pinfo, (entity_type)tvb_get_letohl(tvb, offset+8));
|
|
|
|
return SIZE_OF_MSG_CONNECT+auth_len;
|
|
}
|
|
|
|
/*
|
|
* msg_connect_reply - Server->Client connection reply following a request
|
|
*
|
|
struct ceph_msg_connect_reply {
|
|
guint8 tag;
|
|
guint64 features;
|
|
guint32 global_seq;
|
|
guint32 connect_seq;
|
|
guint32 protocol_version;
|
|
guint32 authorizer_len;
|
|
guint8 flags;
|
|
}
|
|
*/
|
|
#define SIZE_OF_MSG_CONNECT_REPLY (1+8+4+4+4+4+1)
|
|
|
|
static gint
|
|
dissect_connect_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint offset) {
|
|
|
|
gint auth_len;
|
|
guint tag;
|
|
guint flags;
|
|
|
|
/* Test we have enough data including auth */
|
|
if (tvb_reported_length_remaining (tvb, offset) < SIZE_OF_MSG_CONNECT_REPLY)
|
|
return -1;
|
|
|
|
/* TODO: This isn't an ideal test but we need something :-( */
|
|
tag = tvb_get_guint8(tvb, offset);
|
|
if (!isMsgrConnectTag(tag))
|
|
return 0;
|
|
|
|
auth_len = tvb_get_letohl(tvb, offset+21);
|
|
if (tvb_reported_length_remaining (tvb, offset) <
|
|
SIZE_OF_MSG_CONNECT_REPLY+auth_len)
|
|
return -1;
|
|
|
|
setPDUInfo(pinfo, "Connect Reply");
|
|
proto_tree_add_uint_format_value(tree, hf_connect_tag, tvb, offset, 1,
|
|
tag, "%d (%s)", tag, replyTagDescription(tag));
|
|
format_connect_features(tvb, pinfo, tree, offset+1);
|
|
proto_tree_add_item(tree, hf_connect_global_seq, tvb, offset+9, 4, TRUE);
|
|
proto_tree_add_item(tree, hf_connect_connect_seq, tvb, offset+13, 4, TRUE);
|
|
proto_tree_add_item(tree, hf_connect_protocol_version, tvb, offset+17,
|
|
4, TRUE);
|
|
proto_tree_add_item(tree, hf_connect_authorizer_len, tvb, offset+21,
|
|
4, TRUE);
|
|
flags = tvb_get_guint8(tvb, offset+25);
|
|
proto_tree_add_uint_format_value(tree, hf_connect_flags, tvb, offset+25, 1,
|
|
flags, "%d (%s)", flags, connectFlagDescription(flags));
|
|
|
|
/* Tag it in conversation */
|
|
conv_fixup(FALSE, pinfo);
|
|
conv_tag(pinfo, tvb, offset, CONNECT_REPLY);
|
|
return SIZE_OF_MSG_CONNECT_REPLY+auth_len;
|
|
}
|
|
|
|
/*
|
|
* msg_ack - Simple ack of previous messages
|
|
*
|
|
struct ceph_msg_ack {
|
|
guint8 tag;
|
|
guint64 seq;
|
|
}
|
|
*/
|
|
#define SIZE_OF_MSG_ACK (1+8)
|
|
|
|
static guint
|
|
dissect_ack(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
|
|
{
|
|
if (tvb_reported_length_remaining (tvb, offset) < SIZE_OF_MSG_ACK)
|
|
return -1;
|
|
|
|
if (tree) {
|
|
setPDUInfo(pinfo, "Ack");
|
|
proto_tree_add_item(tree, hf_hdr_tag, tvb, offset, 1, TRUE);
|
|
proto_tree_add_item(tree, hf_hdr_seq_ack, tvb, offset+1, 8, TRUE);
|
|
}
|
|
return SIZE_OF_MSG_ACK;
|
|
}
|
|
|
|
/*
|
|
* entity_name
|
|
*
|
|
struct ceph_entity_name {
|
|
guint8 type; //
|
|
guint64 num;
|
|
}
|
|
*/
|
|
#define SIZE_OF_ENTITY_NAME (1+8)
|
|
|
|
static guint
|
|
dissect_entity_name(tvbuff_t *tvb, proto_tree *tree, gint hf, int offset) {
|
|
proto_item *entity_name_item;
|
|
proto_tree *entity_name_tree;
|
|
|
|
if (tvb_reported_length_remaining (tvb, offset) < SIZE_OF_ENTITY_NAME)
|
|
return -1;
|
|
|
|
if (tree) {
|
|
entity_name_item = proto_tree_add_item( tree, hf, tvb, offset,
|
|
SIZE_OF_ENTITY_NAME, TRUE );
|
|
entity_name_tree = proto_item_add_subtree(entity_name_item,
|
|
ett_entity_name);
|
|
|
|
proto_tree_add_item(entity_name_tree, hf_entity_type, tvb, offset,
|
|
1, TRUE);
|
|
proto_tree_add_item(entity_name_tree, hf_entity_num, tvb, offset+1,
|
|
8, TRUE);
|
|
}
|
|
return SIZE_OF_ENTITY_NAME;
|
|
}
|
|
|
|
/*
|
|
* msg_header
|
|
*
|
|
struct ceph_msg_header {
|
|
guint64 seq; // message seq# for this session
|
|
guint64 tid; // transaction id
|
|
guint16 type; // message type
|
|
guint16 priority; // priority. higher value == higher priority
|
|
guint16 version; // version of message encoding
|
|
|
|
guint32 front_len; // bytes in main payload
|
|
guint32 middle_len; // bytes in middle payload
|
|
guint32 data_len; // bytes of data payload
|
|
guint16 data_off; // sender: include full offset;
|
|
// receiver: mask against ~PAGE_MASK
|
|
|
|
struct ceph_entity_name src;
|
|
guint32 reserved;
|
|
guint32 crc; // header crc32c
|
|
}
|
|
*/
|
|
#define SIZE_OF_MSG_HEADER (8+8+2+2+2 +4+4+4+2 +SIZE_OF_ENTITY_NAME+4+4)
|
|
#define HEADER_TYPE_OFFSET (8+8)
|
|
#define HEADER_ETYPE_OFFSET (8+8+2+2+2 +4+4+4+2)
|
|
#define HEADER_CRC_OFFSET (SIZE_OF_MSG_HEADER-4)
|
|
|
|
typedef struct _header_info {
|
|
guint32 type;
|
|
guint32 front_len;
|
|
guint32 middle_len;
|
|
guint32 data_len;
|
|
} header_info;
|
|
|
|
static gint
|
|
dissect_msg_header(tvbuff_t *tvb, proto_tree *tree, int offset,
|
|
header_info* info) {
|
|
proto_item *header_item;
|
|
proto_tree *header_tree;
|
|
|
|
if (tvb_reported_length_remaining (tvb, offset) < SIZE_OF_MSG_HEADER)
|
|
return -1;
|
|
|
|
if (info) {
|
|
info->type = tvb_get_letohs(tvb, offset+16);
|
|
info->front_len = tvb_get_letohl(tvb, offset+22);
|
|
info->middle_len = tvb_get_letohl(tvb, offset+26);
|
|
info->data_len = tvb_get_letohl(tvb, offset+30);
|
|
}
|
|
|
|
if (tree) {
|
|
header_item = proto_tree_add_item( tree, hf_header, tvb, offset,
|
|
SIZE_OF_MSG_HEADER, TRUE );
|
|
header_tree = proto_item_add_subtree(header_item, ett_header);
|
|
|
|
proto_tree_add_item(header_tree, hf_hdr_seq, tvb, offset+0, 8, TRUE);
|
|
proto_tree_add_item(header_tree, hf_hdr_tid, tvb, offset+8, 8, TRUE);
|
|
proto_tree_add_item(header_tree, hf_hdr_type, tvb, offset+16, 2, TRUE);
|
|
proto_tree_add_item(header_tree, hf_hdr_priority, tvb, offset+18,
|
|
2, TRUE);
|
|
proto_tree_add_item(header_tree, hf_hdr_version, tvb, offset+20,
|
|
2, TRUE);
|
|
|
|
proto_tree_add_item(header_tree, hf_hdr_front_len, tvb, offset+22,
|
|
4, TRUE);
|
|
proto_tree_add_item(header_tree, hf_hdr_middle_len, tvb, offset+26,
|
|
4, TRUE);
|
|
proto_tree_add_item(header_tree, hf_hdr_data_len, tvb, offset+30,
|
|
4, TRUE);
|
|
proto_tree_add_item(header_tree, hf_hdr_data_off, tvb, offset+34,
|
|
2, TRUE);
|
|
|
|
dissect_entity_name(tvb, header_tree, hf_hdr_src, offset+36);
|
|
|
|
proto_tree_add_item(header_tree, hf_hdr_crc, tvb,
|
|
offset+36+4+SIZE_OF_ENTITY_NAME, 4, TRUE);
|
|
}
|
|
return SIZE_OF_MSG_HEADER;
|
|
}
|
|
|
|
/*
|
|
* msg_footer
|
|
*
|
|
struct ceph_msg_footer {
|
|
guint32 front_crc;
|
|
guint32 middle_crc;
|
|
guint32 data_crc;
|
|
guint64 sig;
|
|
guint8 flags;
|
|
}
|
|
*/
|
|
#define SIZE_OF_MSG_FOOTER (4+4+4+8+1)
|
|
|
|
static guint32
|
|
dissect_footer(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
|
|
{
|
|
proto_item *footer_item;
|
|
proto_tree *footer_tree;
|
|
|
|
if (tvb_reported_length_remaining (tvb, offset) < SIZE_OF_MSG_FOOTER)
|
|
return -1;
|
|
|
|
if (tree) {
|
|
footer_item = proto_tree_add_item( tree, hf_footer, tvb, offset,
|
|
SIZE_OF_MSG_FOOTER, TRUE );
|
|
footer_tree = proto_item_add_subtree(footer_item, ett_footer);
|
|
|
|
proto_tree_add_item(footer_tree, hf_footer_front_crc, tvb, offset,
|
|
4, TRUE);
|
|
proto_tree_add_item(footer_tree, hf_footer_middle_crc, tvb, offset+4,
|
|
4, TRUE);
|
|
proto_tree_add_item(footer_tree, hf_footer_data_crc, tvb, offset+8,
|
|
4, TRUE);
|
|
proto_tree_add_item(footer_tree, hf_footer_sig, tvb, offset+12,
|
|
8, TRUE);
|
|
proto_tree_add_item(footer_tree, hf_footer_flags, tvb, offset+20,
|
|
1, TRUE);
|
|
}
|
|
|
|
return SIZE_OF_MSG_FOOTER;
|
|
}
|
|
|
|
static guint32
|
|
dissect_fsid(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
|
|
{
|
|
guint32 fsid[4];
|
|
|
|
tvb_memcpy(tvb, &fsid, offset, sizeof(fsid));
|
|
proto_tree_add_text(tree, tvb, offset, sizeof(fsid), "fsid: %x-%x-%x-%x",
|
|
g_ntohl(fsid[0]), g_ntohl(fsid[1]), g_ntohl(fsid[2]), g_ntohl(fsid[3]));
|
|
return sizeof(fsid);
|
|
}
|
|
|
|
/*
|
|
* Message types
|
|
* There is no order to these as yet, maybe better split by source->target type?
|
|
*/
|
|
typedef enum _msg_ctype {
|
|
MSG_SHUTDOWN=1,
|
|
MSG_PING=2,
|
|
MSG_MON_MAP=4,
|
|
MSG_MON_GET_MAP=5,
|
|
MSG_STATFS=13,
|
|
MSG_STATFS_REPLY=14,
|
|
MSG_MON_SUBSCRIBE=15,
|
|
MSG_MON_SUBSCRIBE_ACK=16,
|
|
MSG_AUTH=17,
|
|
MSG_AUTH_REPLY=18,
|
|
MSG_MON_GET_VERSION=19,
|
|
MSG_MON_GET_VERSION_REPLY=20,
|
|
MSG_MDS_MAP=21,
|
|
MSG_CLIENT_SESSION=22,
|
|
MSG_CLIENT_RECONNECT=23,
|
|
MSG_CLIENT_REQUEST=24,
|
|
MSG_CLIENT_REQUEST_FORWARD=25,
|
|
MSG_CLIENT_REPLY=26,
|
|
MSG_CLIENT_CAPS=0x310,
|
|
MSG_CLIENT_LEASE=0x311,
|
|
MSG_CLIENT_SNAP=0x312,
|
|
MSG_CLIENT_CAPRELEASE=0x313,
|
|
MSG_POOLOP_REPLY=48,
|
|
MSG_POOLOP=49,
|
|
MSG_OSD_MAP=41,
|
|
MSG_OSD_OP=42,
|
|
MSG_OSD_OPREPLY=43,
|
|
MSG_WATCH_NOTIFY=44,
|
|
MSG_MON_ELECTION=65,
|
|
MSG_MON_PAXOS=66,
|
|
MSG_MON_PROBE=67,
|
|
MSG_MON_JOIN=68,
|
|
MSG_MON_SYNC=69,
|
|
MSG_MON_COMMAND=50,
|
|
MSG_MON_COMMAND_ACK=51,
|
|
MSG_LOG=52,
|
|
MSG_LOGACK=53,
|
|
MSG_CLASS=56,
|
|
MSG_CLASS_ACK=57,
|
|
MSG_GETPOOLSTATS=58,
|
|
MSG_GETPOOLSTATSREPLY=59,
|
|
MSG_MON_GLOBAL_ID=60,
|
|
MSG_ROUTE=47,
|
|
MSG_FORWARD=46,
|
|
MSG_PAXOS=40,
|
|
MSG_OSD_PING=70,
|
|
MSG_OSD_BOOT=71,
|
|
MSG_OSD_FAILURE=72,
|
|
MSG_OSD_ALIVE=73,
|
|
MSG_OSD_MARK_ME_DOWN=74,
|
|
MSG_OSD_SUBOP=76,
|
|
MSG_OSD_SUBOPREPLY=77,
|
|
MSG_OSD_PGTEMP=78,
|
|
MSG_OSD_PG_NOTIFY=80,
|
|
MSG_OSD_PG_QUERY=81,
|
|
MSG_OSD_PG_SUMMARY=82,
|
|
MSG_OSD_PG_LOG=83,
|
|
MSG_OSD_PG_REMOVE=84,
|
|
MSG_OSD_PG_INFO=85,
|
|
MSG_OSD_PG_TRIM=86,
|
|
MSG_PGSTATS=87,
|
|
MSG_PGSTATSACK=88,
|
|
MSG_OSD_PG_CREATE=89,
|
|
MSG_REMOVE_SNAPS=90,
|
|
MSG_OSD_SCRUB=91,
|
|
MSG_OSD_PG_MISSING=92,
|
|
MSG_OSD_REP_SCRUB=93,
|
|
MSG_OSD_PG_SCAN=94,
|
|
MSG_OSD_PG_BACKFILL=95,
|
|
MSG_COMMAND=97,
|
|
MSG_COMMAND_REPLY=98,
|
|
MSG_OSD_BACKFILL_RESERVE=99,
|
|
MSG_OSD_RECOVERY_RESERVE=150,
|
|
MSG_MDS_BEACON=100,
|
|
MSG_MDS_SLAVE_REQUEST=101,
|
|
MSG_MDS_TABLE_REQUEST=102,
|
|
MSG_MDS_RESOLVE=0x200,
|
|
MSG_MDS_RESOLVEACK=0x201,
|
|
MSG_MDS_CACHEREJOIN=0x202,
|
|
MSG_MDS_DISCOVER=0x203,
|
|
MSG_MDS_DISCOVERREPLY=0x204,
|
|
MSG_MDS_INODEUPDATE=0x205,
|
|
MSG_MDS_DIRUPDATE=0x206,
|
|
MSG_MDS_CACHEEXPIRE=0x207,
|
|
MSG_MDS_DENTRYUNLINK=0x208,
|
|
MSG_MDS_FRAGMENTNOTIFY=0x209,
|
|
MSG_MDS_OFFLOAD_TARGETS=0x20a,
|
|
MSG_MDS_DENTRYLINK=0x20c,
|
|
MSG_MDS_FINDINO=0x20d,
|
|
MSG_MDS_FINDINOREPLY=0x20e,
|
|
MSG_MDS_OPENINO=0x20f,
|
|
MSG_MDS_OPENINOREPLY=0x210,
|
|
MSG_MDS_LOCK=0x300,
|
|
MSG_MDS_INODEFILECAPS=0x301,
|
|
MSG_MDS_EXPORTDIRDISCOVER=0x449,
|
|
MSG_MDS_EXPORTDIRDISCOVERACK=0x450,
|
|
MSG_MDS_EXPORTDIRCANCEL=0x451,
|
|
MSG_MDS_EXPORTDIRPREP=0x452,
|
|
MSG_MDS_EXPORTDIRPREPACK=0x453,
|
|
MSG_MDS_EXPORTDIRWARNING=0x454,
|
|
MSG_MDS_EXPORTDIRWARNINGACK=0x455,
|
|
MSG_MDS_EXPORTDIR=0x456,
|
|
MSG_MDS_EXPORTDIRACK=0x457,
|
|
MSG_MDS_EXPORTDIRNOTIFY=0x458,
|
|
MSG_MDS_EXPORTDIRNOTIFYACK=0x459,
|
|
MSG_MDS_EXPORTDIRFINISH=0x460,
|
|
MSG_MDS_EXPORTCAPS=0x470,
|
|
MSG_MDS_EXPORTCAPSACK=0x471,
|
|
MSG_MDS_HEARTBEAT=0x500,
|
|
MSG_TIMECHECK=0x600,
|
|
MSG_MON_HEALTH=0x601
|
|
} msg_ctype;
|
|
|
|
static value_string msg_ctype_values[]={
|
|
{MSG_SHUTDOWN, "Shutdown"},
|
|
{MSG_PING, "Ping"},
|
|
{MSG_MON_MAP, "MON Map"},
|
|
{MSG_MON_GET_MAP, "MON Get Map"},
|
|
{MSG_STATFS, "Stat FS"},
|
|
{MSG_STATFS_REPLY, "Stat FS Reply"},
|
|
{MSG_MON_SUBSCRIBE, "MON Subscribe"},
|
|
{MSG_MON_SUBSCRIBE_ACK, "MON Subscribe Ack"},
|
|
{MSG_AUTH, "Auth"},
|
|
{MSG_AUTH_REPLY, "Auth Reply"},
|
|
{MSG_MON_GET_VERSION, "MON Get Version"},
|
|
{MSG_MON_GET_VERSION_REPLY, "MON Get Version Reply"},
|
|
{MSG_MDS_MAP, "MDS Map"},
|
|
{MSG_CLIENT_SESSION, "Client Session"},
|
|
{MSG_CLIENT_RECONNECT, "Client Reconnect"},
|
|
{MSG_CLIENT_REQUEST, "Client Request"},
|
|
{MSG_CLIENT_REQUEST_FORWARD, "Client Request Forward"},
|
|
{MSG_CLIENT_REPLY, "Client Reply"},
|
|
{MSG_PAXOS, "Paxos"},
|
|
{MSG_OSD_MAP, "OSD Map"},
|
|
{MSG_OSD_OP, "OSD Op"},
|
|
{MSG_OSD_OPREPLY, "OSD Op Reply"},
|
|
{MSG_WATCH_NOTIFY, "Watch Notify"},
|
|
{MSG_FORWARD, "Forward"},
|
|
{MSG_ROUTE, "Route"},
|
|
{MSG_POOLOP_REPLY, "Pool Op Reply"},
|
|
{MSG_POOLOP, "Pool Op"},
|
|
{MSG_MON_COMMAND, "MON Command"},
|
|
{MSG_MON_COMMAND_ACK, "MON Command Ack"},
|
|
{MSG_LOG, "Log"},
|
|
{MSG_LOGACK, "Log Ack"},
|
|
{MSG_CLASS, "Class"},
|
|
{MSG_CLASS_ACK, "Class Ack"},
|
|
{MSG_GETPOOLSTATS, "Get Pool Stats"},
|
|
{MSG_GETPOOLSTATSREPLY, "Get Pools Stats Reply"},
|
|
{MSG_MON_GLOBAL_ID, "MON Global Id"},
|
|
{MSG_MON_ELECTION, "MON Election"},
|
|
{MSG_MON_PAXOS, "MON Paxos"},
|
|
{MSG_MON_PROBE, "MON Probe"},
|
|
{MSG_MON_JOIN, "MON Join"},
|
|
{MSG_MON_SYNC, "MON Sync"},
|
|
{MSG_OSD_PING, "OSD Ping"},
|
|
{MSG_OSD_BOOT, "OSD Boot"},
|
|
{MSG_OSD_FAILURE, "OSD Failure"},
|
|
{MSG_OSD_ALIVE, "OSD Alive"},
|
|
{MSG_OSD_MARK_ME_DOWN, "OSD Mark Me Down"},
|
|
{MSG_OSD_SUBOP, "OSD Subop"},
|
|
{MSG_OSD_SUBOPREPLY, "OSD Subop Reply"},
|
|
{MSG_OSD_PGTEMP, "OSD PG Temp"},
|
|
{MSG_OSD_PG_NOTIFY, "OSD PG Notify"},
|
|
{MSG_OSD_PG_QUERY, "OSD PG Query"},
|
|
{MSG_OSD_PG_SUMMARY, "OSD PG Summary"},
|
|
{MSG_OSD_PG_LOG, "OSD PG Log"},
|
|
{MSG_OSD_PG_REMOVE, "OSD PG Remove"},
|
|
{MSG_OSD_PG_INFO, "OSD PG Info"},
|
|
{MSG_OSD_PG_TRIM, "OSD PG Trim"},
|
|
{MSG_PGSTATS, "MPN PG Stats"},
|
|
{MSG_PGSTATSACK, "MON PG Stats Ack"},
|
|
{MSG_OSD_PG_CREATE, "OSD PG Create"},
|
|
{MSG_REMOVE_SNAPS, "Remove Snaps"},
|
|
{MSG_OSD_SCRUB, "OSD Scrub"},
|
|
{MSG_OSD_PG_MISSING, "OSD PG Missing"},
|
|
{MSG_OSD_REP_SCRUB, "OSD REP Scrub"},
|
|
{MSG_OSD_PG_SCAN, "OSD PG Scan"},
|
|
{MSG_OSD_PG_BACKFILL, "OSD PG Backfill"},
|
|
{MSG_COMMAND, "Command"},
|
|
{MSG_COMMAND_REPLY, "Command Reply"},
|
|
{MSG_OSD_BACKFILL_RESERVE, "OSD Backfill Reserve"},
|
|
{MSG_MDS_BEACON, "MDS Beacon"},
|
|
{MSG_MDS_SLAVE_REQUEST, "MDS Slave Request"},
|
|
{MSG_MDS_TABLE_REQUEST, "MDS Table Request"},
|
|
{MSG_OSD_RECOVERY_RESERVE, "OSD Recovery Reserve"},
|
|
{MSG_MDS_RESOLVE, "MDS Resolve"},
|
|
{MSG_MDS_RESOLVEACK, "MDS Resolve Ack"},
|
|
{MSG_MDS_CACHEREJOIN, "MDS Cache Rejoin"},
|
|
{MSG_MDS_DISCOVER, "MDS Discover"},
|
|
{MSG_MDS_DISCOVERREPLY, "MDS Discover Reply"},
|
|
{MSG_MDS_INODEUPDATE, "MDS INode Update"},
|
|
{MSG_MDS_DIRUPDATE, "MDS Dir Update"},
|
|
{MSG_MDS_CACHEEXPIRE, "MDS Cache Expire"},
|
|
{MSG_MDS_DENTRYUNLINK, "MDS DEntry Unlink"},
|
|
{MSG_MDS_FRAGMENTNOTIFY, "MDS Fragment Notify"},
|
|
{MSG_MDS_OFFLOAD_TARGETS, "MDS Offload Targets"},
|
|
{MSG_MDS_DENTRYLINK, "MDS DEntry Link"},
|
|
{MSG_MDS_FINDINO, "MDS Find INO"},
|
|
{MSG_MDS_FINDINOREPLY, "MDS Find INO Reply"},
|
|
{MSG_MDS_OPENINO, "MDS Open INO"},
|
|
{MSG_MDS_OPENINOREPLY, "MDS Open INO Reply"},
|
|
{MSG_MDS_LOCK, "MDS Lock"},
|
|
{MSG_MDS_INODEFILECAPS, "MDS INODE File Caps"},
|
|
{MSG_CLIENT_CAPS, "Client Caps"},
|
|
{MSG_CLIENT_LEASE, "Client Lease"},
|
|
{MSG_CLIENT_SNAP, "Client Snap"},
|
|
{MSG_CLIENT_CAPRELEASE, "Client Cap Release"},
|
|
{MSG_MDS_EXPORTDIRDISCOVER, "MDS Export DIR Discover"},
|
|
{MSG_MDS_EXPORTDIRDISCOVERACK, "MDS Export DIR Discover Ack"},
|
|
{MSG_MDS_EXPORTDIRCANCEL, "MDS Export DIR Cancel"},
|
|
{MSG_MDS_EXPORTDIRPREP, "MDS Export DIR Prep"},
|
|
{MSG_MDS_EXPORTDIRPREPACK, "MDS Export DIR Prepack"},
|
|
{MSG_MDS_EXPORTDIRWARNING, "MDS Export DIR Warning"},
|
|
{MSG_MDS_EXPORTDIRWARNINGACK, "MDS Export DIR Warning Ack"},
|
|
{MSG_MDS_EXPORTDIR, "MDS Export DIR"},
|
|
{MSG_MDS_EXPORTDIRACK, "MDS Export DIR Ack"},
|
|
{MSG_MDS_EXPORTDIRNOTIFY, "MDS Export DIR Notify"},
|
|
{MSG_MDS_EXPORTDIRNOTIFYACK, "MDS Export DIR Notify Ack"},
|
|
{MSG_MDS_EXPORTDIRFINISH, "MDS Export DIR Finish"},
|
|
{MSG_MDS_EXPORTCAPS, "MDS Export Caps"},
|
|
{MSG_MDS_EXPORTCAPSACK, "MDS Export Caps Ack"},
|
|
{MSG_MDS_HEARTBEAT, "MDS Heartbeat"},
|
|
{MSG_TIMECHECK, "Timecheck"},
|
|
{MSG_MON_HEALTH, "MON Health"}
|
|
};
|
|
|
|
static gboolean
|
|
isMsgCType(guint32 type) {
|
|
return (try_val_to_str(type, msg_ctype_values)!=NULL);
|
|
}
|
|
|
|
static const char*
|
|
msgCTypeDescription(msg_ctype type) {
|
|
return val_to_str(type, msg_ctype_values, "Unknown Message Type: %d");
|
|
}
|
|
|
|
/*
|
|
* PaxosServiceMessage
|
|
*
|
|
struct {
|
|
guint64 version;
|
|
gint16 deprecated_session_mon;
|
|
guint64 deprecated_session_mon_tid;
|
|
}
|
|
*/
|
|
#define SIZE_OF_PAXOS (8+2+8)
|
|
|
|
static guint32
|
|
dissect_paxos(tvbuff_t *tvb, proto_tree *tree, guint32 offset){
|
|
|
|
proto_tree_add_item(tree, hf_paxos_version, tvb, offset, 8, TRUE);
|
|
return SIZE_OF_PAXOS;
|
|
}
|
|
|
|
/*
|
|
* feature_set
|
|
*
|
|
struct {
|
|
uint64_t mask;
|
|
map <uint64_t,string> names;
|
|
}
|
|
*/
|
|
static guint32
|
|
dissect_featureset(tvbuff_t *tvb, proto_tree *tree, guint32 offset, int hinfo,
|
|
gint ett) {
|
|
proto_item *litem;
|
|
proto_tree *ltree;
|
|
guint32 i, items;
|
|
guint32 at;
|
|
|
|
litem = proto_tree_add_item( tree, hinfo, tvb, offset, 1, TRUE );
|
|
ltree = proto_item_add_subtree(litem, ett);
|
|
|
|
at = offset;
|
|
proto_tree_add_item(ltree, hf_featureset_mask, tvb, at, 8, TRUE); at += 8;
|
|
items = tvb_get_letohl(tvb, at); at+=4;
|
|
for (i=0; i<items; i++) {
|
|
proto_tree_add_item(ltree, hf_featureset_id, tvb, at, 8, TRUE); at += 8;
|
|
proto_tree_add_item(ltree, hf_featureset_name, tvb, at, 4, TRUE);
|
|
at += tvb_get_letohl(tvb, at)+ 4;
|
|
}
|
|
|
|
proto_item_set_len(litem, (at-offset));
|
|
return (at-offset);
|
|
}
|
|
|
|
static guint32
|
|
dissect_compatset(tvbuff_t *tvb, proto_tree *tree, guint32 offset) {
|
|
|
|
guint32 at;
|
|
|
|
at = offset;
|
|
at +=dissect_featureset(tvb, tree, at, hf_compatset_compat, ett_compat);
|
|
at +=dissect_featureset(tvb, tree, at, hf_compatset_rocompat, ett_rocompat);
|
|
at +=dissect_featureset(tvb, tree, at, hf_compatset_incompat, ett_incompat);
|
|
|
|
return (at-offset);
|
|
}
|
|
|
|
/*
|
|
* MAuth
|
|
*
|
|
struct {
|
|
paxos paxos;
|
|
guint32 protocol;
|
|
guint32 auth_len;
|
|
guint8 auth[];
|
|
guint32 monmap_epoch;
|
|
}
|
|
*/
|
|
#define SIZE_OF_MSG_AUTH (SIZE_OF_PAXOS+4+4+0+4)
|
|
|
|
static guint32
|
|
dissect_auth(tvbuff_t *tvb, proto_tree *tree, guint32 offset){
|
|
proto_item *auth_item;
|
|
proto_tree *auth_tree;
|
|
gint auth_len;
|
|
guint8* auth_bytes;
|
|
|
|
auth_len = tvb_get_letohl(tvb, offset+SIZE_OF_PAXOS+4);
|
|
|
|
if (tree) {
|
|
auth_item = proto_tree_add_item( tree, hf_auth, tvb, offset,
|
|
SIZE_OF_MSG_AUTH+auth_len, TRUE );
|
|
auth_tree = proto_item_add_subtree(auth_item, ett_front);
|
|
|
|
offset += dissect_paxos(tvb, auth_tree, offset);
|
|
proto_tree_add_item(auth_tree, hf_auth_protocol, tvb, offset, 4, TRUE);
|
|
proto_tree_add_item(auth_tree, hf_auth_authlen, tvb, offset+4, 4, TRUE);
|
|
auth_bytes = (guint8*)tvb_memdup(tvb, offset+8, auth_len);
|
|
proto_tree_add_bytes(auth_tree, hf_auth_authbytes, tvb, offset+8,
|
|
auth_len, auth_bytes);
|
|
g_free(auth_bytes);
|
|
proto_tree_add_item(auth_tree, hf_auth_monmapepoch, tvb,
|
|
offset+8+auth_len, 4, TRUE);
|
|
}
|
|
return SIZE_OF_MSG_AUTH+auth_len;
|
|
}
|
|
|
|
/*
|
|
* msg_auth_reply
|
|
*
|
|
struct ceph_msg_auth_reply {
|
|
guint32 protocol;
|
|
gint32 result;
|
|
guint64 global_id;
|
|
guint32 auth_len;
|
|
guint8 auth[];
|
|
guint32 msg_len;
|
|
char msg[];
|
|
}
|
|
*/
|
|
#define SIZE_OF_MSG_AUTH_REPLY (4+4+8+4+0+4+0)
|
|
|
|
static guint32
|
|
dissect_auth_reply(tvbuff_t *tvb, proto_tree *tree, guint32 offset){
|
|
proto_item *auth_item;
|
|
proto_tree *auth_tree;
|
|
gint auth_len;
|
|
guint8* auth_bytes;
|
|
gint msg_len;
|
|
char* msg_string;
|
|
|
|
auth_len = tvb_get_letohl(tvb, offset+16);
|
|
msg_len = tvb_get_letohl(tvb, offset+20+auth_len);
|
|
|
|
if (tree) {
|
|
auth_item = proto_tree_add_item( tree, hf_authreply, tvb, offset,
|
|
SIZE_OF_MSG_AUTH_REPLY+auth_len+msg_len, TRUE );
|
|
auth_tree = proto_item_add_subtree(auth_item, ett_front);
|
|
|
|
proto_tree_add_item(auth_tree, hf_authreply_protocol, tvb, offset+0,
|
|
4, TRUE);
|
|
proto_tree_add_item(auth_tree, hf_authreply_result, tvb, offset+4,
|
|
4, TRUE);
|
|
proto_tree_add_item(auth_tree, hf_authreply_globalid, tvb, offset+8,
|
|
8, TRUE);
|
|
proto_tree_add_item(auth_tree, hf_authreply_authlen, tvb, offset+16,
|
|
4, TRUE);
|
|
|
|
auth_bytes = (guint8*)tvb_memdup(tvb, offset+20, auth_len);
|
|
proto_tree_add_bytes(auth_tree, hf_authreply_authbytes, tvb, offset+20,
|
|
auth_len, auth_bytes);
|
|
g_free(auth_bytes);
|
|
|
|
proto_tree_add_item(auth_tree, hf_authreply_msglen, tvb,
|
|
offset+20+auth_len, 4, TRUE);
|
|
msg_string = (char*)tvb_memdup(tvb, offset+20+auth_len+4, msg_len);
|
|
proto_tree_add_string(auth_tree, hf_authreply_msgstring, tvb,
|
|
offset+20+auth_len+4, msg_len, msg_string);
|
|
g_free(msg_string);
|
|
}
|
|
return SIZE_OF_MSG_AUTH_REPLY+auth_len+msg_len;
|
|
}
|
|
|
|
/*
|
|
* msg_mon_map
|
|
*
|
|
struct ceph_msg_monmap {
|
|
guint32 length;
|
|
guint8 version;
|
|
guint8 compat;
|
|
guint32 entry_size;
|
|
ceph_msg_monmap_entry entries[];
|
|
};
|
|
|
|
struct ceph_msg_monmap_entry {
|
|
guint8[16] fsid
|
|
guint32 epoch
|
|
map<string,entity_addr_t> mon_addr
|
|
guint64 last_changed
|
|
guint64 created
|
|
}
|
|
*/
|
|
|
|
static guint32
|
|
dissect_mon_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint32 offset) {
|
|
proto_item *litem = NULL;
|
|
proto_tree *ltree = NULL;
|
|
guint32 i, at, name_at;
|
|
guint32 map_len, addr_len, entries_len;
|
|
entity_info einfo;
|
|
|
|
map_len = tvb_get_letohl(tvb, offset)+sizeof(map_len);
|
|
|
|
litem = proto_tree_add_item( tree, hf_monmap, tvb, offset, map_len, TRUE );
|
|
ltree = proto_item_add_subtree(litem, ett_front);
|
|
|
|
proto_tree_add_item(ltree, hf_monmap_version, tvb, offset+4, 1, TRUE);
|
|
proto_tree_add_item(ltree, hf_monmap_compat, tvb, offset+5, 1, TRUE);
|
|
|
|
entries_len = tvb_get_letohl(tvb, offset+6);
|
|
offset += 10;
|
|
while (entries_len>0) {
|
|
at = offset;
|
|
at += dissect_fsid(tvb, ltree, at);
|
|
proto_tree_add_item(ltree, hf_monmap_epoch, tvb, at, 4, TRUE); at+=4;
|
|
|
|
addr_len = tvb_get_letohl(tvb, at); at+=4;
|
|
for (i=0; i< addr_len; i++) {
|
|
name_at = at;
|
|
proto_tree_add_item(ltree, hf_monmap_name, tvb, at, 4, TRUE);
|
|
at += tvb_get_letohl(tvb, at)+ 4;
|
|
at += dissect_entity_addr(tvb, pinfo, ltree, at, &einfo);
|
|
conv_set_entity_name(conv_get_entity_frominfo(&einfo),
|
|
tvb, name_at, ENTITY_TYPE_MON);
|
|
}
|
|
|
|
proto_tree_add_item(ltree, hf_monmap_lastchanged, tvb, at, 8, TRUE);
|
|
at+=8;
|
|
proto_tree_add_item(ltree, hf_monmap_created, tvb, at, 8, TRUE);
|
|
at+=8;
|
|
|
|
entries_len -= (at-offset);
|
|
offset = at;
|
|
}
|
|
if (entries_len !=0 )
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Length mismatch in decoding mon map entries");
|
|
|
|
return map_len;
|
|
}
|
|
|
|
/*
|
|
* msg_mon_subscribe
|
|
*
|
|
struct ceph_mon_subscribe_item {
|
|
guint64 start;
|
|
guint8 flags;
|
|
};
|
|
|
|
struct ceph_msg_monsubscribe {
|
|
map<string, ceph_mon_subscribe_item> what
|
|
};
|
|
*/
|
|
static guint32
|
|
dissect_mon_subscribe(tvbuff_t *tvb, proto_tree *tree, guint32 offset,
|
|
guint32 length) {
|
|
proto_item *litem = NULL;
|
|
proto_tree *ltree = NULL;
|
|
guint32 i, items;
|
|
guint32 at;
|
|
|
|
litem = proto_tree_add_item( tree, hf_monsubscribe, tvb, offset,
|
|
length, TRUE );
|
|
ltree = proto_item_add_subtree(litem, ett_front);
|
|
|
|
items = tvb_get_letohl(tvb, offset);
|
|
at = offset +4;
|
|
for (i=0; i< items; i++) {
|
|
proto_tree_add_item(ltree, hf_monsubscribe_name, tvb, at, 4, TRUE);
|
|
at += tvb_get_letohl(tvb, at)+ 4;
|
|
|
|
proto_tree_add_item(ltree, hf_monsubscribe_start, tvb, at, 8, TRUE);
|
|
at += 8;
|
|
proto_tree_add_item(ltree, hf_monsubscribe_flags, tvb, at, 1, TRUE);
|
|
at += 1;
|
|
}
|
|
|
|
return (at-offset);
|
|
}
|
|
|
|
/*
|
|
* msg_mon_subscribeack
|
|
*
|
|
struct ceph_msg_mon_subscribeack {
|
|
guint32 interval;
|
|
guint8 fsid[16];
|
|
};
|
|
*/
|
|
static guint32
|
|
dissect_mon_subscribeack(tvbuff_t *tvb, proto_tree *tree, guint32 offset,
|
|
guint32 length) {
|
|
proto_item *litem = NULL;
|
|
proto_tree *ltree = NULL;
|
|
|
|
litem = proto_tree_add_item( tree, hf_monsubscribeack, tvb, offset,
|
|
length, TRUE );
|
|
ltree = proto_item_add_subtree(litem, ett_front);
|
|
|
|
proto_tree_add_item(ltree, hf_monsubscribeack_interval, tvb, offset,
|
|
4, TRUE);
|
|
dissect_fsid(tvb, ltree, offset+4);
|
|
return 20;
|
|
}
|
|
|
|
/*
|
|
* mds_beacon
|
|
*
|
|
struct mds_beacon {
|
|
paxos
|
|
guint8[16] fsid;
|
|
guint64 global_id;
|
|
guint32 state;
|
|
guint64 seq;
|
|
string name;
|
|
gint32 standby_for_rank;
|
|
string standby_for_name;
|
|
CompatSet compat;
|
|
};
|
|
*/
|
|
static guint32
|
|
dissect_mdsbeacon(tvbuff_t *tvb, proto_tree *tree,
|
|
guint32 offset, guint32 length) {
|
|
proto_item *litem = NULL;
|
|
proto_tree *ltree = NULL;
|
|
guint32 at;
|
|
|
|
litem = proto_tree_add_item( tree, hf_mdsbeacon, tvb, offset, length, TRUE);
|
|
ltree = proto_item_add_subtree(litem, ett_front);
|
|
|
|
at = offset;
|
|
at += dissect_paxos(tvb, ltree, at);
|
|
at += dissect_fsid(tvb, ltree, at);
|
|
proto_tree_add_item(ltree, hf_mdsbeacon_globalid, tvb, at, 8, TRUE); at+=8;
|
|
proto_tree_add_item(ltree, hf_mdsbeacon_state, tvb, at, 4, TRUE); at+=4;
|
|
proto_tree_add_item(ltree, hf_mdsbeacon_seq, tvb, at, 8, TRUE); at+=8;
|
|
proto_tree_add_item(ltree, hf_mdsbeacon_name, tvb, at, 4, TRUE);
|
|
at += tvb_get_letohl(tvb, at)+ 4;
|
|
proto_tree_add_item(ltree, hf_mdsbeacon_standbyforrank, tvb, at, 4, TRUE);
|
|
at+=4;
|
|
proto_tree_add_item(ltree, hf_mdsbeacon_standbyforname, tvb, at, 4, TRUE);
|
|
at += tvb_get_letohl(tvb, at)+ 4;
|
|
at += dissect_compatset(tvb, ltree, at);
|
|
|
|
return (at-offset);
|
|
}
|
|
|
|
/*
|
|
* Pull apart a contained message.
|
|
* Dissectors called from here don't need to length check just return how
|
|
* many bytes they actually consumed.
|
|
*/
|
|
static guint
|
|
dissect_msg_ctype(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
int offset, msg_ctype type, guint32 length)
|
|
{
|
|
tvbuff_t* ltvb;
|
|
guint32 len=0;
|
|
|
|
/* Create a subset to isolate the dissectors
|
|
so they don't really need to length check */
|
|
|
|
ltvb = tvb_new_subset_length(tvb, offset, length);
|
|
|
|
switch (type) {
|
|
case MSG_MON_MAP:
|
|
len = dissect_mon_map(ltvb, pinfo, tree, 0);
|
|
break;
|
|
case MSG_AUTH:
|
|
len = dissect_auth(ltvb, tree, 0);
|
|
break;
|
|
case MSG_AUTH_REPLY:
|
|
len = dissect_auth_reply(ltvb, tree, 0);
|
|
break;
|
|
case MSG_MON_SUBSCRIBE:
|
|
len = dissect_mon_subscribe(ltvb, tree, 0, length);
|
|
break;
|
|
case MSG_MON_SUBSCRIBE_ACK:
|
|
len = dissect_mon_subscribeack(ltvb, tree, 0, length);
|
|
break;
|
|
case MSG_MDS_BEACON:
|
|
len = dissect_mdsbeacon(ltvb, tree, 0, length);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static guint
|
|
dissect_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) {
|
|
guint start;
|
|
gint len=0;
|
|
header_info info;
|
|
|
|
start = offset;
|
|
len = dissect_msg_header(tvb, tree, offset, &info);
|
|
if (len <0 ) return len;
|
|
offset += len;
|
|
|
|
if (info.front_len) {
|
|
if ((guint)tvb_reported_length_remaining (tvb, offset) < info.front_len)
|
|
return -1;
|
|
|
|
len = dissect_msg_ctype(tvb, pinfo, tree, offset,
|
|
(msg_ctype)info.type, info.front_len);
|
|
if (len <0 ) return len;
|
|
else if (len == 0)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Undecoded: %s", msgCTypeDescription((msg_ctype)info.type));
|
|
else if ((guint32)len != info.front_len)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Header front length (%u) does not match message length (%u)",
|
|
info.front_len, len);
|
|
offset += info.front_len;
|
|
}
|
|
|
|
offset += info.middle_len;
|
|
offset += info.data_len;
|
|
len = dissect_footer(tvb, tree, offset);
|
|
if (len <0 ) return len;
|
|
|
|
offset += len;
|
|
setPDUInfo(pinfo, msgCTypeDescription((msg_ctype)info.type));
|
|
return (offset-start);
|
|
}
|
|
|
|
/*
|
|
* Create an expert warning that part of the packet could not be decoded.
|
|
*/
|
|
static void
|
|
undecodedWarning(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
guint32 offset, guint32 expecting) {
|
|
|
|
/*
|
|
* Post warning message
|
|
*/
|
|
setPDUInfo(pinfo, "Undecoded data");
|
|
if ((expecting & SERVER_BANNER) != 0)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Expected server to send banner, ignoring packet");
|
|
if ((expecting & CLIENT_BANNER) != 0)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Expected client to send banner, ignoring packet");
|
|
if ((expecting & SERVER_ADDRESS) != 0)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Expected server to send addresses, ignoring packet");
|
|
if ((expecting & CLIENT_ADDRESS) != 0)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Expected client to send addresses, ignoring packet");
|
|
if ((expecting & CONNECT_REQUEST) != 0)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Expected client to send connection request, ignoring packet");
|
|
if ((expecting & CONNECT_REPLY) != 0)
|
|
expert_add_info_format(pinfo, tree, PI_UNDECODED, PI_WARN,
|
|
"Expected server to send connection reply, ignoring packet");
|
|
|
|
/*
|
|
* Assume we jumped into middle of conversation
|
|
*/
|
|
conv_tag(pinfo, tvb, offset, CONVERSING);
|
|
}
|
|
|
|
static void dissect_ceph(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree){
|
|
|
|
proto_item *ceph_item = NULL;
|
|
proto_tree *ceph_tree = NULL;
|
|
guint pduNum;
|
|
guint offset;
|
|
gint32 len;
|
|
guint32 expecting;
|
|
guint32 needMore;
|
|
guint8 type;
|
|
|
|
/* Tag as CEPH & prep info */
|
|
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_CEPH);
|
|
if(check_col(pinfo->cinfo,COL_INFO)){
|
|
col_clear(pinfo->cinfo,COL_INFO);
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "[%s]",conv_display(pinfo));
|
|
col_set_fence(pinfo->cinfo,COL_INFO);
|
|
}
|
|
|
|
/*
|
|
* Loop reading PDUs
|
|
*/
|
|
needMore = FALSE;
|
|
offset=0;
|
|
pduNum=0;
|
|
while (tvb_reported_length_remaining (tvb, offset) != 0) {
|
|
|
|
/* Flag if this is first PDU in packet, helps formating info */
|
|
g_firstPdu = (pduNum++ == 0);
|
|
|
|
/* Create a new tree for it */
|
|
if (tree) {
|
|
ceph_item = proto_tree_add_item(tree, g_proto_ceph, tvb, 0,
|
|
-1, TRUE);
|
|
ceph_tree = proto_item_add_subtree(ceph_item, ett_ceph);
|
|
} else {
|
|
ceph_tree = NULL;
|
|
}
|
|
|
|
/*
|
|
* Recover what type of message we are expecting to see both for
|
|
* error checking and guiding the dissector to do the right thing
|
|
*/
|
|
expecting = conv_expecting(pinfo, tvb, offset);
|
|
|
|
/*
|
|
* Try decode something
|
|
*/
|
|
if ((expecting & SERVER_BANNER) != 0) {
|
|
len = dissect_banner(tvb, pinfo, ceph_tree, offset, FALSE);
|
|
if (len == -1) needMore = TRUE;
|
|
else if (len>0) {
|
|
conv_tag(pinfo, tvb, offset, SERVER_BANNER);
|
|
offset += len;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((expecting & CLIENT_BANNER) != 0) {
|
|
len = dissect_banner(tvb, pinfo, ceph_tree, offset, TRUE);
|
|
if (len == -1) needMore = TRUE;
|
|
else if (len>0) {
|
|
conv_tag(pinfo, tvb, offset, CLIENT_BANNER);
|
|
offset += len;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((expecting & CLIENT_ADDRESS) != 0) {
|
|
len = dissect_client_address(tvb, pinfo, ceph_tree, offset);
|
|
if (len == -1) needMore = TRUE;
|
|
else if (len>0) {
|
|
conv_tag(pinfo, tvb, offset, CLIENT_ADDRESS);
|
|
offset += len;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((expecting & SERVER_ADDRESS) != 0) {
|
|
len = dissect_server_address(tvb, pinfo, ceph_tree, offset);
|
|
if (len == -1) needMore = TRUE;
|
|
else if (len>0) {
|
|
conv_tag(pinfo, tvb, offset, SERVER_ADDRESS);
|
|
offset += len;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((expecting & CONNECT_REQUEST) != 0) {
|
|
len = dissect_connect_request(tvb, pinfo, ceph_tree, offset);
|
|
if (len == -1) needMore = TRUE;
|
|
else if (len>0) {
|
|
conv_tag(pinfo, tvb, offset, CONNECT_REQUEST);
|
|
offset += len;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((expecting & CONNECT_REPLY) != 0) {
|
|
len = dissect_connect_reply(tvb, pinfo, ceph_tree, offset);
|
|
if (len == -1) needMore = TRUE;
|
|
else if (len>0) {
|
|
conv_tag(pinfo, tvb, offset, CONNECT_REPLY);
|
|
offset += len;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((expecting & CONVERSING) != 0) {
|
|
type = tvb_get_guint8(tvb, offset);
|
|
|
|
if(type == MSGR_TAG_CLOSE){
|
|
setPDUInfo(pinfo, "Close");
|
|
conv_tag(pinfo, tvb, offset, CONVERSING);
|
|
offset++;
|
|
}
|
|
else if (type == MSGR_TAG_KEEPALIVE){
|
|
setPDUInfo(pinfo, "Keep Alive");
|
|
conv_tag(pinfo, tvb, offset, CONVERSING);
|
|
offset++;
|
|
}
|
|
else if (type == MSGR_TAG_ACK) {
|
|
len = dissect_ack(tvb, pinfo, ceph_tree, offset);
|
|
if (len == -1) needMore = TRUE;
|
|
if (len>0) {
|
|
conv_tag(pinfo, tvb, offset, CONVERSING);
|
|
offset += len;
|
|
continue;
|
|
}
|
|
}
|
|
else if (type == MSGR_TAG_MSG) {
|
|
len = dissect_msg(tvb, pinfo, ceph_tree, offset+1);
|
|
if (len == -1) needMore = TRUE;
|
|
if (len>0) {
|
|
conv_tag(pinfo, tvb, offset+1, CONVERSING);
|
|
offset += (1+len);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We have fallen through without decoding something */
|
|
if (needMore) {
|
|
/* One of decoders wants to see more data, so request it */
|
|
pinfo->desegment_offset = offset;
|
|
pinfo->desegment_len=DESEGMENT_ONE_MORE_SEGMENT;
|
|
} else {
|
|
/* Throw data away, we don't know what it is */
|
|
undecodedWarning(tvb, pinfo, ceph_tree, offset, expecting);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Initialisation code
|
|
****************************************************************************/
|
|
|
|
static gboolean
|
|
dissect_ceph_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
void *data) {
|
|
|
|
conversation_t* conversation;
|
|
(void)data; /* unused but required */
|
|
|
|
if (tvb_memeql(tvb,0, "ceph",4)!=0) {
|
|
if (tvb_reported_length_remaining (tvb, 0) >= 1+SIZE_OF_MSG_HEADER) {
|
|
if (!isMsgrTag(tvb_get_guint8(tvb, 0)))
|
|
return FALSE;
|
|
|
|
if (!isEntityType(tvb_get_guint8(tvb, 1+HEADER_ETYPE_OFFSET)))
|
|
return FALSE;
|
|
|
|
if (!isMsgCType(tvb_get_letohs(tvb, 1+HEADER_TYPE_OFFSET)))
|
|
return FALSE;
|
|
|
|
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* This is for us */
|
|
conversation = find_or_create_conversation(pinfo);
|
|
conversation_set_dissector(conversation, g_ceph_handle);
|
|
dissect_ceph(tvb, pinfo, tree);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Create the dissector, this is called once after proto_register_ceph
|
|
* has been invoked.
|
|
* Note. the dissector is only valid on a range of ports, you can
|
|
* get around this by writing a 'heuristic' detector but that is
|
|
* rather more complex to do.
|
|
*/
|
|
void proto_reg_handoff_ceph(void)
|
|
{
|
|
g_ceph_handle = create_dissector_handle(dissect_ceph, g_proto_ceph);
|
|
heur_dissector_add("tcp", dissect_ceph_heur, g_proto_ceph);
|
|
}
|
|
|
|
/*
|
|
* This is the first part of the two-part registration for the plugin,
|
|
* see proto_reg_handoff_ceph() for part 2.
|
|
*/
|
|
void proto_register_ceph (void)
|
|
{
|
|
/* A header field is something you can search/filter on.
|
|
*
|
|
* We create a structure to register our fields. It consists of an
|
|
* array of hf_register_info structures, each of which are of the format
|
|
* {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}.
|
|
*/
|
|
static hf_register_info hf[] = {
|
|
{ &hf_paxos_version,
|
|
{ "version", "ceph.paxosservicemessage.version", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_featureset_mask,
|
|
{ "mask", "ceph.featureset.mask", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_featureset_id,
|
|
{ "id", "ceph.featureset.id", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_featureset_name,
|
|
{ "name", "ceph.featureset.name", FT_UINT_STRING ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_compatset_compat,
|
|
{ "Compat", "ceph.compat", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_compatset_rocompat,
|
|
{ "RO Compat", "ceph.rocompat", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_compatset_incompat,
|
|
{ "Incompat", "ceph.incompat", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monmap,
|
|
{ "MON map", "ceph.monmap", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monmap_version,
|
|
{ "version", "ceph.monmap.version", FT_UINT8 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monmap_compat,
|
|
{ "compat", "ceph.monmap.compat", FT_UINT8 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monmap_epoch,
|
|
{ "epoch", "ceph.monmap.epoch", FT_UINT32 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monmap_name,
|
|
{ "name", "ceph.monmap.name", FT_UINT_STRING ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monmap_lastchanged,
|
|
{ "last changed", "ceph.monmap.lastchanged", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monmap_created,
|
|
{ "created", "ceph.monmap.created", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monsubscribe,
|
|
{ "MON subscribe", "ceph.monsubscribe", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monsubscribe_name,
|
|
{ "name", "ceph.monsubscribe.name", FT_UINT_STRING ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monsubscribe_start,
|
|
{ "start", "ceph.monsubscribe.start", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monsubscribe_flags,
|
|
{ "flags", "ceph.monsubscribe.flags", FT_UINT8 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monsubscribeack,
|
|
{ "MON subscribe ack", "ceph.monsubscribeack", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_monsubscribeack_interval,
|
|
{ "interval", "ceph.monsubscribeack.interval", FT_UINT32 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_mdsbeacon,
|
|
{ "MDS Beacon", "ceph.mdsbeacon", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_mdsbeacon_globalid,
|
|
{ "global_id", "ceph.mdsbeason.globalid", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_mdsbeacon_state,
|
|
{ "state", "ceph.mdsbeason.state", FT_UINT32 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_mdsbeacon_seq,
|
|
{ "seq", "ceph.mdsbeason.seq", FT_UINT64 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_mdsbeacon_name,
|
|
{ "name", "ceph.mdsbeason.name", FT_UINT_STRING ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_mdsbeacon_standbyforrank,
|
|
{ "standby for rank", "ceph.mdsbeason.standbyforrank", FT_INT32 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_mdsbeacon_standbyforname,
|
|
{ "standby for name", "ceph.mdsbeason.standbyforname", FT_UINT_STRING ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_auth,
|
|
{ "Authentication Request", "ceph.auth.request", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_auth_protocol,
|
|
{ "protocol", "ceph.auth.protocol", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_auth_authlen,
|
|
{ "auth len", "ceph.auth.authlen", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_auth_authbytes,
|
|
{ "auth bytes", "ceph.auth.authbytes", FT_BYTES,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_auth_monmapepoch,
|
|
{ "monmap epoch", "ceph.auth.monmapepoch", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_authreply,
|
|
{ "Authentication reply", "ceph.auth.reply", FT_NONE ,
|
|
BASE_NONE, NULL, 0, NULL, HFILL }},
|
|
{ &hf_authreply_protocol,
|
|
{ "protocol", "ceph.authreply.protocol", FT_UINT32 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_authreply_result,
|
|
{ "result", "ceph.authreply.result", FT_INT32 ,
|
|
BASE_DEC, NULL, 0, NULL, HFILL }},
|
|
{ &hf_authreply_globalid,
|
|
{ "global_id", "ceph.authreply.global_id", FT_UINT64 ,
|
|
BASE_HEX, NULL, 0, NULL, HFILL }},
|
|
{ &hf_authreply_authlen,
|
|
{ "auth len", "ceph.authreply.authlen", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_authreply_authbytes,
|
|
{ "auth bytes", "ceph.authreply.authbytes", FT_BYTES,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_authreply_msglen,
|
|
{ "msg len", "ceph.authreply.msglen", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_authreply_msgstring,
|
|
{ "msg bytes", "ceph.authreply.msgbytes", FT_STRINGZ,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_header,
|
|
{ "Header", "ceph.header", FT_NONE,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_banner,
|
|
{ "Banner", "ceph.connect.banner", FT_STRING,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_entity_type,
|
|
{ "entity type", "ceph.entity.type", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_entity_num,
|
|
{ "entity num", "ceph.entity.num", FT_UINT64,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_entity_addr,
|
|
{ "Entity Addr", "ceph.entity.addr", FT_NONE,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_banner_magic,
|
|
{ "Banner Magic", "ceph.connect.banner.magic", FT_STRING,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_banner_version,
|
|
{ "Banner Version", "ceph.connect.banner.ver", FT_STRING,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_entity_erank,
|
|
{ "erank", "ceph.entity.erank", FT_UINT32,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_entity_nonce,
|
|
{ "nonce", "ceph.entity.nonce", FT_UINT32,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_sockaddr_in,
|
|
{ "sockaddr_in", "ceph.sockaddr_in", FT_NONE,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_sin_family,
|
|
{ "sin_family", "ceph.sin_family", FT_UINT16,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_sin_port,
|
|
{ "sin_port", "ceph.sin_port", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_sin_addr,
|
|
{ "ip addr", "ceph.addr", FT_IPv4,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_features,
|
|
{ "features", "ceph.connect.features", FT_UINT64,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_host_type,
|
|
{ "host_type", "ceph.connect.host_type", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_tag,
|
|
{ "tag", "ceph.connect.tag", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_global_seq,
|
|
{ "global_seq", "ceph.connect.global_seq", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_connect_seq,
|
|
{ "connect_seq", "ceph.connect.connect_seq", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_protocol_version,
|
|
{ "protocol_version", "ceph.connect.protocol_version", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_authorizer_protocol,
|
|
{ "authorizer_protocol", "ceph.connect.authorizer_protocol", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_authorizer_len,
|
|
{ "authorizer_len", "ceph.connect.authorizer_len", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_flags,
|
|
{ "flags", "ceph.connect.flags", FT_UINT8,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_connect_authentication_key,
|
|
{ "authentication_key", "ceph.connect.authentication_key", FT_BYTES,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_tag,
|
|
{ "tag", "ceph.tag", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_seq_ack,
|
|
{ "ack seq", "ceph.ack.seq", FT_UINT64,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_seq,
|
|
{ "seq", "ceph.seq", FT_UINT64,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_tid,
|
|
{ "tid", "ceph.tid", FT_UINT64,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_type,
|
|
{ "type", "ceph.type", FT_UINT16,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_priority,
|
|
{ "priority", "ceph.priority", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_version,
|
|
{ "version", "ceph.version", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_mon_protocol,
|
|
{ "mon_protocol", "ceph.mon_protocol", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_osd_protocol,
|
|
{ "osd_protocol", "ceph.osd_protocol", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_mds_protocol,
|
|
{ "mds_protocol", "ceph.mds_protocol", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_client_protocol,
|
|
{ "client_protocol", "ceph.client_protocol", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_front_len,
|
|
{ "front_len", "ceph.front_len", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_middle_len,
|
|
{ "middle_len", "ceph.middle_len", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_data_off,
|
|
{ "data_off", "ceph.data_off", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_data_len,
|
|
{ "data_len", "ceph.data_len", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_src,
|
|
{ "src entity name", "ceph.src", FT_NONE,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_hdr_crc,
|
|
{ "crc", "ceph.crc", FT_UINT32,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_footer,
|
|
{ "Footer", "ceph.footer", FT_NONE,
|
|
BASE_NONE, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_footer_front_crc,
|
|
{ "front_crc", "ceph.footer.front_crc", FT_UINT32,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_footer_middle_crc,
|
|
{ "middle_crc", "ceph.footer.middle_crc", FT_UINT32,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_footer_data_crc,
|
|
{ "data_crc", "ceph.footer.data_crc", FT_UINT32,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_footer_sig,
|
|
{ "sig", "ceph.footer.sig", FT_UINT64,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
{ &hf_footer_flags,
|
|
{ "flags", "ceph.footer.flags", FT_UINT8,
|
|
BASE_HEX, NULL, 0x0, NULL, HFILL }},
|
|
};
|
|
static gint *ett[] = {
|
|
&ett_ceph,
|
|
&ett_header,
|
|
&ett_banner,
|
|
&ett_entity_addr,
|
|
&ett_front,
|
|
&ett_footer,
|
|
&ett_sockaddr_in,
|
|
&ett_entity_name,
|
|
&ett_compat,
|
|
&ett_rocompat,
|
|
&ett_incompat,
|
|
};
|
|
|
|
/*
|
|
* Register the CEPH protocol
|
|
*/
|
|
g_proto_ceph = proto_register_protocol ("CEPH Protocol", "CEPH", "ceph");
|
|
proto_register_field_array (g_proto_ceph, hf, array_length (hf));
|
|
proto_register_subtree_array (ett, array_length (ett));
|
|
register_dissector("ceph", dissect_ceph, g_proto_ceph);
|
|
}
|
|
|
|
|