From f4ab3f492f5b2dfef55a65900ec256655c110e23 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Jun 2016 15:10:32 -0400 Subject: ceph: distingush between client and server by checking for a second entity_addr_t The current ceph dissector assumes that the server will always send its initial connection negotiation first, but that's not necessarily the case, especially with the kernel client which sends its banner as soon as the socket is created. So, we need a better mechanism to determine which end is client and which is the server. The server sends its own address and then the address of the client, but the client only sends its own address. We can determine whether the initial negotiation message is from the client or server by looking at the data after the first entity addr and seeing whether it also looks like an entity addr. This patch takes that approach. It just grabs the address family from the second address and sees whether it's IPv4 or IPv6. If it's not one of those, then it assumes that it's not an entity_addr_t at all and is therefore a request from the client. We could go farther and try to verify the port and address as well, but that's probably overkill. The address family is at the same offset as the host_type field in the client's Connect request, but it's big endian and the host_type is little endian. As long as we don't end up with host_types that are 0x200 or 0xA00, this scheme should be OK. Change-Id: I161d02da86d978272eff95497c6df66766b02ebc Signed-off-by: Jeff Layton Reviewed-on: https://code.wireshark.org/review/16043 Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Michael Mann Reviewed-by: Alexis La Goutte --- epan/dissectors/packet-ceph.c | 76 ++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/epan/dissectors/packet-ceph.c b/epan/dissectors/packet-ceph.c index 41a2f31f8a..044b6fb533 100644 --- a/epan/dissectors/packet-ceph.c +++ b/epan/dissectors/packet-ceph.c @@ -1545,30 +1545,29 @@ c_pkt_data_init(c_pkt_data *d, packet_info *pinfo, guint off) if (!d->convd) /* New conversation. */ { d->convd = c_conv_data_new(); - - /* Note: Server sends banner first. */ - - copy_address_wmem(wmem_file_scope(), &d->convd->server.addr, &pinfo->src); - d->convd->server.port = pinfo->srcport; - copy_address_wmem(wmem_file_scope(), &d->convd->client.addr, &pinfo->dst); - d->convd->client.port = pinfo->destport; conversation_add_proto_data(d->conv, proto_ceph, d->convd); } - /*** Set up src and dst pointers correctly. ***/ - if (addresses_equal(&d->convd->client.addr, &pinfo->src) && - d->convd->client.port == pinfo->srcport) - { - d->src = &d->convd->client; - d->dst = &d->convd->server; - } - else - { - d->src = &d->convd->server; - d->dst = &d->convd->client; + /* + * Set up src and dst pointers correctly, if the client port is + * already set. Otherwise, we need to wait until we have enough + * data to determine which is which. + */ + if (d->convd->client.port != 0xFFFF) { + if (addresses_equal(&d->convd->client.addr, &pinfo->src) && + d->convd->client.port == pinfo->srcport) + { + d->src = &d->convd->client; + d->dst = &d->convd->server; + } + else + { + d->src = &d->convd->server; + d->dst = &d->convd->client; + } + DISSECTOR_ASSERT(d->src); + DISSECTOR_ASSERT(d->dst); } - DISSECTOR_ASSERT(d->src); - DISSECTOR_ASSERT(d->dst); c_header_init(&d->header); d->item_root = NULL; @@ -6918,8 +6917,41 @@ guint c_dissect_pdu(proto_tree *root, } static -guint c_pdu_end(tvbuff_t *tvb, guint off, c_pkt_data *data) +guint c_pdu_end(tvbuff_t *tvb, packet_info *pinfo, guint off, c_pkt_data *data) { + c_inet af; + + /* + * If we don't already know, then figure out which end of the + * connection is the client. It's icky, but the only way to know is to + * see whether the info after the first entity_addr_t looks like + * another entity_addr_t. + */ + if (data->convd->client.port == 0xFFFF) { + if (!tvb_bytes_exist(tvb, off, C_BANNER_SIZE + C_SIZE_ENTITY_ADDR + 8 + 2)) + return C_NEEDMORE; + + /* We have enough to determine client vs. server */ + af = (c_inet)tvb_get_ntohs(tvb, off + C_BANNER_SIZE + C_SIZE_ENTITY_ADDR + 8); + if (af != C_IPv4 && af != C_IPv6) { + /* Client */ + copy_address_wmem(wmem_file_scope(), &data->convd->client.addr, &pinfo->src); + data->convd->client.port = pinfo->srcport; + copy_address_wmem(wmem_file_scope(), &data->convd->server.addr, &pinfo->dst); + data->convd->server.port = pinfo->destport; + data->src = &data->convd->client; + data->dst = &data->convd->server; + } else { + /* Server */ + copy_address_wmem(wmem_file_scope(), &data->convd->server.addr, &pinfo->src); + data->convd->server.port = pinfo->srcport; + copy_address_wmem(wmem_file_scope(), &data->convd->client.addr, &pinfo->dst); + data->convd->client.port = pinfo->destport; + data->src = &data->convd->server; + data->dst = &data->convd->client; + } + } + switch (data->src->state) { case C_STATE_NEW: @@ -7010,7 +7042,7 @@ int dissect_ceph(tvbuff_t *tvb, packet_info *pinfo, if (off) c_pkt_data_save(&data, pinfo, off); - offt = c_pdu_end(tvb, off, &data); + offt = c_pdu_end(tvb, pinfo, off, &data); if (offt == C_INVALID) { return 0; -- cgit v1.2.3