Ethereal-dev: [Ethereal-dev] distcc dissector - need help with tcp desegmentation
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Brad Hards <bhards@xxxxxxxxxxxxxx>
Date: Thu, 6 Feb 2003 20:34:22 +1100
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 G'day, I've started a dissector for distcc, and it kind-of works. An indicative patch (not intended to be applied) is attached. I know about nmake, and will deal with that in the real patch. Be warned - it is ugly code. I'm having two problems with distcc: 1. Irrespective of this patch, it looks like ethereal can't capture every packet. This is particularly noticable when using the loopback interface for testing. I'm not sure why it occurs. I know that distcc does make heavy use of sendfile(), and perhaps pcap is running out of buffering space. I haven't tried tcpdump yet, nor have I done a lot of tests with a real network. 2. I think I need a smarter way to do desegmentation. Currently I'm doing it by hand, but it looks like ethereal can help. In packet_info.h, I see: gboolean fragmented; /* TRUE if the protocol is only a fragment */ guint16 can_desegment; /* >0 if this segment could be desegmented. A dissector that can offer this API (e.g. TCP) sets can_desegment=2, then can_desegment is decremented by 1 each time we pass to the next subdissector. Thus only the dissector immediately above the protocol which sets the flag can use it*/ int desegment_offset; /* offset of stuff needing desegmentation */ guint32 desegment_len; /* requested desegmentation additional length */ But I'm still not sure how to do it. I looked at a few examples that appear to use this stuff, but I had a bad combination of not understanding how those protocols worked, and not understanding of what the desegmentation was doing. Is it intended that fragmented is set by my dissector, or by the TCP dissector? I guess I only test can_desegment, never need to worry about setting this What is the reference for desegment_offset? How am I meant to use desegment_len? Does anyone have time to work through an example? Brad -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.0.6 (GNU/Linux) Comment: For info see http://www.gnupg.org iD8DBQE+QiweW6pHgIdAuOMRAkevAJ9MWgo2ixWCrc4YfuUsu4HUQkbeNgCcC13a M9fXuZhBWt0DAOp9ydEGtNI= =2GLu -----END PGP SIGNATURE-----
diff -Naur -x Makefile.in -x ltmain.sh clean/ethereal-0.9.9/Makefile.am ethereal-0.9.9/Makefile.am --- clean/ethereal-0.9.9/Makefile.am 2003-01-24 10:50:12.000000000 +1100 +++ ethereal-0.9.9/Makefile.am 2003-02-05 17:27:18.000000000 +1100 @@ -170,6 +170,7 @@ packet-dec-bpdu.c \ packet-dhcpv6.c \ packet-diameter.c \ + packet-distcc.c \ packet-dlsw.c \ packet-dns.c \ packet-dsi.c \ diff -Naur -x Makefile.in -x ltmain.sh clean/ethereal-0.9.9/packet-distcc.c ethereal-0.9.9/packet-distcc.c --- clean/ethereal-0.9.9/packet-distcc.c 1970-01-01 10:00:00.000000000 +1000 +++ ethereal-0.9.9/packet-distcc.c 2003-02-05 17:55:47.000000000 +1100 @@ -0,0 +1,522 @@ +/* packet-distcc.c + * Routines for distcc dissection + * Copyright 2003, Brad Hards <bradh@xxxxxxxxxxxxx> + * + * $Id: README.developer,v 1.65 2002/11/09 08:37:00 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@xxxxxxxxxxxx> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> + +#include <string.h> +#include <time.h> +#include <glib.h> + +#include <epan/packet.h> +#include <epan/strutil.h> +#include <epan/conversation.h> + +typedef enum _distcc_state { + DISTCC_DIST = 0, + DISTCC_ARGS = 1, + DISTCC_DOTI = 2, + DISTCC_DOTI_CONT = 3, + DISTCC_DONE = 4, + DISTCC_STAT = 5, + DISTCC_SERR = 6, + DISTCC_SOUT = 7, + DISTCC_DOTO = 8, + DISTCC_DOTO_CONT = 9 +} distcc_state_t; + +/* this is used to represent the _initial_ state of the frame */ +/* each frame can contain multiple protocol elements */ +struct distcc_frame_state_t { + distcc_state_t state; + int done_sub_len; + guint len_remaining; +}; + +/* this is a guide to the current conversation state */ +/* we need the length remaining because source and object elements can + easily span multiple frames, and we need to track the + internal"sub-state" in DOTI and DOTO */ +struct distcc_conversation_state_t { + distcc_state_t state; + guint len_remaining; +}; + +static int proto_distcc = -1; + +static int hf_distcc_hdr_magic = -1; +static int hf_distcc_hdr_version = -1; +static int hf_distcc_hdr_argc = -1; +static int hf_distcc_hdr_argv = -1; +static int hf_distcc_doti_magic = -1; +static int hf_distcc_done_magic = -1; +static int hf_distcc_done_version = -1; +static int hf_distcc_stat_magic = -1; +static int hf_distcc_stat_result = -1; +static int hf_distcc_serr_magic = -1; +static int hf_distcc_sout_magic = -1; +static int hf_distcc_doto_magic = -1; + +static gint ett_distcc = -1; + +dissector_handle_t distcc_handle; + + +#define TCP_PORT_DISTCC 3632 +#define UDP_PORT_DISTCC 3632 + +/* Packet dissection routine called by tcp & udp when port 3632 detected */ + +static void +dissect_distcc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + int offset = 0; + proto_item *ti; + proto_tree *distcc_tree; + conversation_t *conversation; + struct distcc_conversation_state_t *conversation_data; + struct distcc_frame_state_t *frame_data; + gchar cmd_line_argc_string[9]; + int cmd_line_argc; + int yalv = 0; + gchar cmd_line_argv_len_string[9]; + int cmd_line_argv_len; + gchar doti_length_string[9]; + gchar doti_magic[9]; + guint doti_length; + guint actual_bytes_remaining; + gchar sout_length_string[9]; + guint sout_length; + gchar serr_length_string[9]; + guint serr_length; + gchar doto_length_string[9]; + guint doto_length; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DISTCC"); + + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + if (conversation == NULL) { + conversation = conversation_new(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + conversation_data = malloc(sizeof(struct distcc_conversation_state_t)); + conversation_data->state = DISTCC_DIST; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + } + + conversation_set_dissector(conversation, distcc_handle); + + ti = proto_tree_add_item(tree, proto_distcc, tvb, 0, -1, FALSE); + + distcc_tree = proto_item_add_subtree(ti, ett_distcc); + + conversation_data = conversation_get_proto_data(conversation, proto_distcc); + + frame_data = p_get_proto_data(pinfo->fd, proto_distcc); + if (!frame_data) { + /* then we haven't seen this frame before */ + frame_data = malloc(sizeof(struct distcc_frame_state_t)); + frame_data->state = conversation_data->state; + frame_data->len_remaining = conversation_data->len_remaining; + p_add_proto_data(pinfo->fd, proto_distcc, frame_data); + } + + switch (frame_data->state) { + case DISTCC_DIST: + proto_tree_add_item(distcc_tree, hf_distcc_hdr_magic, tvb, offset, 4, TRUE); + offset += 4; + + proto_tree_add_item(distcc_tree, hf_distcc_hdr_version, tvb, offset, 8, TRUE); + offset += 8; + + conversation_data->state = DISTCC_ARGS; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + if (tvb_length_remaining(tvb, offset) == 0) + break; + + case DISTCC_ARGS: + proto_tree_add_item(distcc_tree, hf_distcc_hdr_argc, tvb, offset, 12, TRUE); + offset += 4; + + tvb_get_nstringz0(tvb, offset, 8, cmd_line_argc_string); + cmd_line_argc = strtoul(cmd_line_argc_string, NULL, 16); + proto_tree_add_text(distcc_tree, tvb, offset, 8, + "Number of arguments: %i (0x%x)", + cmd_line_argc, cmd_line_argc); + offset +=8; + for (yalv = 0; yalv<cmd_line_argc; yalv++) { + proto_tree_add_item(distcc_tree, hf_distcc_hdr_argv, tvb, offset, 12, TRUE); + offset += 4; + tvb_get_nstringz0(tvb, offset, 8, cmd_line_argv_len_string); + cmd_line_argv_len = strtoul(cmd_line_argv_len_string, NULL, 16); + proto_tree_add_text(distcc_tree, tvb, offset, 8, + "Length of argument %i: %i (0x%x)", + yalv, + cmd_line_argv_len, cmd_line_argv_len); + offset += 8; + proto_tree_add_text(distcc_tree, tvb, offset, cmd_line_argv_len, + "Argument %i: %s", + yalv, + tvb_format_text(tvb, offset, cmd_line_argv_len)); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, + COL_INFO, + "%s ", + tvb_format_text(tvb, offset, cmd_line_argv_len)); + } + offset += cmd_line_argv_len; + } + conversation_data->state = DISTCC_DOTI; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + if (tvb_length_remaining(tvb, offset) == 0) + break; + + case DISTCC_DOTI: + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_str(pinfo->cinfo, COL_INFO, "DOTI"); + } + proto_tree_add_item(distcc_tree, hf_distcc_doti_magic, tvb, offset, 4, TRUE); + offset += 4; + + tvb_get_nstringz0(tvb, offset, 8, doti_length_string); + doti_length = strtoul(doti_length_string, NULL, 16); + proto_tree_add_text(distcc_tree, tvb, offset, 8, + "DOTI Length: %i (0x%x)", + doti_length, doti_length); + offset +=8; + actual_bytes_remaining = tvb_length_remaining(tvb, offset); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, + " (%i of %i bytes)", actual_bytes_remaining, + doti_length); + } + if (actual_bytes_remaining >= doti_length) { + /* this is the case where we have all the data */ + proto_tree_add_text(distcc_tree, tvb, offset, doti_length, + "DOTI data: %s", + tvb_format_text(tvb, offset, doti_length)); + offset += doti_length; + conversation_data->state = DISTCC_DONE; + } else { + /* this is where we have only the start of the data, and + it continues in a later frame */ + proto_tree_add_text(distcc_tree, tvb, offset, actual_bytes_remaining, + "DOTI data: %s", + tvb_format_text(tvb, offset, actual_bytes_remaining)); + offset += actual_bytes_remaining; + conversation_data->state = DISTCC_DOTI_CONT; + conversation_data->len_remaining = doti_length - actual_bytes_remaining; + } + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + /* we always need to break out at this point */ + break; + + case DISTCC_DOTI_CONT: + /* do a sanity check, against dropped frames */ + tvb_get_nstringz0(tvb, offset, 8, doti_magic); + if (0 == strncmp(doti_magic, "DONE0000", 8)) { + printf("failed sanity checking - bailing out to DISTCC_DONE\n"); + conversation_data->state = DISTCC_DONE; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + } else { + actual_bytes_remaining = tvb_length(tvb); + if (actual_bytes_remaining >= frame_data->len_remaining) { + /* this is the case where we have all the data */ + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, + "DOTI Finalisation (%i bytes)", actual_bytes_remaining); + } + proto_tree_add_text(distcc_tree, tvb, offset, + conversation_data->len_remaining, + "DOTI data: ...%s", + tvb_format_text(tvb, offset, + frame_data->len_remaining)); + offset += conversation_data->len_remaining; + conversation_data->state = DISTCC_DONE; + } else { + /* this is where we have only the start of the data, and + it continues in a later frame */ + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, + "DOTI Continuation (%i bytes)", actual_bytes_remaining); + } + proto_tree_add_text(distcc_tree, tvb, offset, actual_bytes_remaining, + "DOTI data: ...%s...", + tvb_format_text(tvb, offset, actual_bytes_remaining)); + offset += actual_bytes_remaining; + conversation_data->state = DISTCC_DOTI_CONT; + /* this routine runs on display, not just on initial pass */ + /* so we use a flag to ensure we only subtract length once */ + if (frame_data->done_sub_len == 0) { + conversation_data->len_remaining -= actual_bytes_remaining; + frame_data->done_sub_len = 1; + p_add_proto_data(pinfo->fd, proto_distcc, frame_data); + } + } + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + + break; + } /* note that we fall through if we failed the sanity check */ + case DISTCC_DONE: + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_str(pinfo->cinfo, COL_INFO, "DONE "); + } + proto_tree_add_item(distcc_tree, hf_distcc_done_magic, tvb, offset, 4, TRUE); + offset += 4; + proto_tree_add_item(distcc_tree, hf_distcc_done_version, tvb, offset, 8, TRUE); + offset += 8; + + conversation_data->state = DISTCC_STAT; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + if (tvb_length_remaining(tvb, offset) == 0) + break; /* else fall through, since we have more data */ + + case DISTCC_STAT: + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_str(pinfo->cinfo, COL_INFO, "STAT "); + } + proto_tree_add_item(distcc_tree, hf_distcc_stat_magic, tvb, offset, 4, TRUE); + offset += 4; + proto_tree_add_item(distcc_tree, hf_distcc_stat_result, tvb, offset, 8, TRUE); + offset += 8; + + conversation_data->state = DISTCC_SERR; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + if (tvb_length_remaining(tvb, offset) == 0) + break; + + case DISTCC_SERR: + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_str(pinfo->cinfo, COL_INFO, "SERR "); + } + proto_tree_add_item(distcc_tree, hf_distcc_serr_magic, tvb, offset, 4, TRUE); + offset += 4; + tvb_get_nstringz0(tvb, offset, 8, serr_length_string); + serr_length = strtoul(serr_length_string, NULL, 16); + proto_tree_add_text(distcc_tree, tvb, offset, 8, + "SERR Length: %i (0x%x)", + serr_length, serr_length); + offset +=8; + if (serr_length > 0) { + proto_tree_add_text(distcc_tree, tvb, offset, + serr_length, + "SERR: %s", + tvb_format_text(tvb, offset, serr_length)); + offset += serr_length; + } + conversation_data->state = DISTCC_SOUT; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + if (tvb_length_remaining(tvb, offset) == 0) + break; + + case DISTCC_SOUT: + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_str(pinfo->cinfo, COL_INFO, "SOUT "); + } + proto_tree_add_item(distcc_tree, hf_distcc_sout_magic, tvb, offset, 4, TRUE); + offset += 4; + tvb_get_nstringz0(tvb, offset, 8, sout_length_string); + sout_length = strtoul(sout_length_string, NULL, 16); + proto_tree_add_text(distcc_tree, tvb, offset, 8, + "SOUT Length: %i (0x%x)", + sout_length, sout_length); + offset +=8; + if (sout_length > 0) { + proto_tree_add_text(distcc_tree, tvb, offset, + sout_length, + "SOUT: %s", + tvb_format_text(tvb, offset, sout_length)); + offset += sout_length; + } + conversation_data->state = DISTCC_DOTO; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + if (tvb_length_remaining(tvb, offset) == 0) + break; + + case DISTCC_DOTO: + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_str(pinfo->cinfo, COL_INFO, "DOTO "); + } + proto_tree_add_item(distcc_tree, hf_distcc_doto_magic, tvb, offset, 4, TRUE); + offset += 4; + tvb_get_nstringz0(tvb, offset, 8, doto_length_string); + doto_length = strtoul(doto_length_string, NULL, 16); + proto_tree_add_text(distcc_tree, tvb, offset, 8, + "DOTO Length: %i (0x%x)", + doto_length, doto_length); + offset +=8; + actual_bytes_remaining = tvb_length_remaining(tvb, offset); + proto_tree_add_text(distcc_tree, tvb, offset, 0, + "Bytes in this packet: %i (0x%x)", + actual_bytes_remaining, actual_bytes_remaining); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, "(%i of %i bytes)", + actual_bytes_remaining, doto_length); + } + + if (actual_bytes_remaining >= doto_length) { + /* this is the case where we have all the data */ + proto_tree_add_text(distcc_tree, tvb, offset, doto_length, + "DOTO data: %s", + tvb_format_text(tvb, offset, doto_length)); + offset += doto_length; + } else { + /* this is where we have only the start of the data, and + it continues in a later frame */ + proto_tree_add_text(distcc_tree, tvb, offset, actual_bytes_remaining, + "DOTO data: %s", + tvb_format_text(tvb, offset, actual_bytes_remaining)); + offset += actual_bytes_remaining; + conversation_data->state = DISTCC_DOTO_CONT; + conversation_data->len_remaining = doto_length - actual_bytes_remaining; + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + } + /* we always need to break out at this point */ + break; + + case DISTCC_DOTO_CONT: + actual_bytes_remaining = tvb_length(tvb); + if (actual_bytes_remaining >= frame_data->len_remaining) { + /* this is the case where we have all the data */ + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, + "DOTO Finalisation (%i bytes)", actual_bytes_remaining); + } + proto_tree_add_text(distcc_tree, tvb, offset, + frame_data->len_remaining, + "DOTO data: ...%s", + tvb_format_text(tvb, offset, + frame_data->len_remaining)); + offset += frame_data->len_remaining; + } else { + /* this is where we have only some of the data, and + it continues in a later frame */ + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, + "DOTO Continuation (%i bytes)", actual_bytes_remaining); + } + proto_tree_add_text(distcc_tree, tvb, offset, actual_bytes_remaining, + "DOTO data: ...%s...", + tvb_format_text(tvb, offset, actual_bytes_remaining)); + offset += actual_bytes_remaining; + conversation_data->state = DISTCC_DOTO_CONT; + /* this routine runs on display, not just on initial pass */ + /* so we use a flag to ensure we only subtract length once */ + /* we will never get DOTI and DOTO in the same frame, since + they go in opposing directions, so we can reuse the flag */ + if (frame_data->done_sub_len == 0) { + conversation_data->len_remaining -= actual_bytes_remaining; + printf("len_remaining = %i\n", conversation_data->len_remaining); + frame_data->done_sub_len = 1; + p_add_proto_data(pinfo->fd, proto_distcc, frame_data); + } + conversation_add_proto_data(conversation, proto_distcc, conversation_data); + } + + break; + } +} + +/* Register protocol with Ethereal. */ +void +proto_register_distcc(void) +{ + static hf_register_info hf[] = { + {&hf_distcc_hdr_magic, + {"Magic Header", "distcc.hdr_magic", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + {&hf_distcc_hdr_version, + {"Header Version", "distcc.hdr_version", + FT_STRING, BASE_NONE, NULL, 0x0, "DISTCC Version", HFILL } + }, + {&hf_distcc_hdr_argc, + {"ARGC", "distcc.hdr_argc", + FT_STRING, BASE_NONE, NULL, 0x0, "Argument Count Entry", HFILL } + }, + {&hf_distcc_hdr_argv, + {"ARGV label", "distcc.hdr_argv", + FT_STRING, BASE_NONE, NULL, 0x0, "Argument Vector Entry", HFILL } + }, + {&hf_distcc_doti_magic, + {"DOTI Magic Header", "distcc.doti_magic", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + {&hf_distcc_done_magic, + {"DONE Magic Header", "distcc.done_magic", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + {&hf_distcc_done_version, + {"Done Version", "distcc.done_version", + FT_STRING, BASE_NONE, NULL, 0x0, "DISTCC Daemon Version", HFILL } + }, + {&hf_distcc_stat_result, + {"STAT result", "distcc.stat_result", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + {&hf_distcc_doto_magic, + {"DOTO Magic Header", "distcc.doto_magic", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + {&hf_distcc_stat_magic, + {"STAT Magic Header", "distcc.stat_magic", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + {&hf_distcc_serr_magic, + {"SERR Magic Header", "distcc.serr_magic", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + {&hf_distcc_sout_magic, + {"SOUT Magic Header", "distcc.sout_magic", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } + }, + }; + + static gint *ett[] = { + &ett_distcc, + }; + + proto_distcc = proto_register_protocol("Distributed Compiler System", + "DISTCC", "distcc"); + proto_register_field_array(proto_distcc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); +} + +void +proto_reg_handoff_distcc(void) +{ + distcc_handle = create_dissector_handle(dissect_distcc, proto_distcc); + dissector_add("tcp.port", TCP_PORT_DISTCC, distcc_handle); + dissector_add("udp.port", UDP_PORT_DISTCC, distcc_handle); +}
- Follow-Ups:
- Prev by Date: [Ethereal-dev] Re: [Ethereal-cvs] cvs commit: ethereal packet-tns.c
- Next by Date: Re: [Ethereal-dev] distcc dissector - need help with tcp desegmentation
- Previous by thread: [Ethereal-dev] Re: [Ethereal-cvs] cvs commit: ethereal packet-tns.c
- Next by thread: Re: [Ethereal-dev] distcc dissector - need help with tcp desegmentation
- Index(es):