Support re-assembly of fragmented BIUs in SNA packets of TH FID 2

Other SNA TH types support fragmentation, but denote it much differently
thant TH FID2. Since I have only FID-2 fragmented packet traces, it's
the only type I could test, and thus the only FID type I coded for.

Many thanks to Nick Baldwin <nick.baldwin@satelcom.co.uk> for
providing the traces and for testing the changes.

svn path=/trunk/; revision=6325
This commit is contained in:
Gilbert Ramirez 2002-09-23 21:58:22 +00:00
parent 1b89d48835
commit 04a155f925
1 changed files with 224 additions and 24 deletions

View File

@ -2,7 +2,7 @@
* Routines for SNA
* Gilbert Ramirez <gram@alumni.rice.edu>
*
* $Id: packet-sna.c,v 1.42 2002/08/28 21:00:34 jmayer Exp $
* $Id: packet-sna.c,v 1.43 2002/09/23 21:58:22 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -32,6 +32,8 @@
#include "llcsaps.h"
#include "ppptypes.h"
#include <epan/sna-utils.h>
#include "prefs.h"
#include "reassemble.h"
/*
* http://www.wanresources.com/snacell.html
@ -155,6 +157,11 @@ static gint ett_sna_rh_2 = -1;
static dissector_handle_t data_handle;
/* Defragment fragmented SNA BIUs*/
static gboolean sna_defragment = FALSE;
static GHashTable *sna_fragment_table = NULL;
static GHashTable *sna_reassembled_table = NULL;
/* Format Identifier */
static const value_string sna_th_fid_vals[] = {
{ 0x0, "SNA device <--> Non-SNA Device" },
@ -172,11 +179,16 @@ static const value_string sna_th_fid_vals[] = {
};
/* Mapping Field */
#define MPF_MIDDLE_SEGMENT 0
#define MPF_LAST_SEGMENT 1
#define MPF_FIRST_SEGMENT 2
#define MPF_WHOLE_BIU 3
static const value_string sna_th_mpf_vals[] = {
{ 0, "Middle segment of a BIU" },
{ 1, "Last segment of a BIU" },
{ 2, "First segment of a BIU" },
{ 3 , "Whole BIU" },
{ MPF_MIDDLE_SEGMENT, "Middle segment of a BIU" },
{ MPF_LAST_SEGMENT, "Last segment of a BIU" },
{ MPF_FIRST_SEGMENT, "First segment of a BIU" },
{ MPF_WHOLE_BIU, "Whole BIU" },
{ 0, NULL }
};
@ -404,8 +416,20 @@ static const true_false_string sna_nlp_osi_truth =
{ "Optional segments present", "No optional segments present" };
/* Values to direct the top-most dissector what to dissect
* after the TH. */
enum next_dissection_enum {
stop_here,
rh_only,
everything
};
typedef enum next_dissection_enum next_dissection_t;
static int dissect_fid0_1 (tvbuff_t*, packet_info*, proto_tree*);
static int dissect_fid2 (tvbuff_t*, packet_info*, proto_tree*);
static int dissect_fid2 (tvbuff_t*, packet_info*, proto_tree*, tvbuff_t**,
next_dissection_t*);
static int dissect_fid3 (tvbuff_t*, proto_tree*);
static int dissect_fid4 (tvbuff_t*, packet_info*, proto_tree*);
static int dissect_fid5 (tvbuff_t*, proto_tree*);
@ -414,6 +438,13 @@ static void dissect_fid (tvbuff_t*, packet_info*, proto_tree*, proto_tree*);
static void dissect_nlp (tvbuff_t*, packet_info*, proto_tree*, proto_tree*);
static void dissect_rh (tvbuff_t*, int, proto_tree*);
static unsigned int
mpf_value(guint8 th_byte)
{
return (th_byte & 0x0c) >> 2;
}
static void
dissect_sna(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
@ -451,6 +482,8 @@ dissect_sna(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
}
#define RH_LEN 3
static void
dissect_fid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
proto_tree *parent_tree)
@ -459,8 +492,10 @@ dissect_fid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
proto_tree *th_tree = NULL, *rh_tree = NULL;
proto_item *th_ti = NULL, *rh_ti = NULL;
guint8 th_fid;
int sna_header_len = 0, th_header_len = 0;
int offset;
int th_header_len = 0;
int offset, rh_offset;
tvbuff_t *rh_tvb = NULL;
next_dissection_t continue_dissecting = everything;
/* Transmission Header Format Identifier */
th_fid = hi_nibble(tvb_get_guint8(tvb, 0));
@ -486,7 +521,8 @@ dissect_fid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
th_header_len = dissect_fid0_1(tvb, pinfo, th_tree);
break;
case 0x2:
th_header_len = dissect_fid2(tvb, pinfo, th_tree);
th_header_len = dissect_fid2(tvb, pinfo, th_tree, &rh_tvb,
&continue_dissecting);
break;
case 0x3:
th_header_len = dissect_fid3(tvb, th_tree);
@ -506,31 +542,156 @@ dissect_fid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
return;
}
sna_header_len += th_header_len;
offset = th_header_len;
/* Short-circuit ? */
if (continue_dissecting == stop_here) {
if (tree) {
proto_tree_add_text(tree, tvb, offset, -1,
"BIU segment data");
}
return;
}
/* If the FID dissector function didn't create an rh_tvb, then we just
* use the rest of our tvbuff as the rh_tvb. */
if (!rh_tvb) {
rh_tvb = tvb_new_subset(tvb, offset, -1, -1);
}
rh_offset = 0;
/* Process the rest of the SNA packet, starting with RH */
if (tree) {
proto_item_set_len(th_ti, th_header_len);
/* --- RH --- */
rh_ti = proto_tree_add_item(tree, hf_sna_rh, tvb, offset, 3, FALSE);
rh_ti = proto_tree_add_item(tree, hf_sna_rh, rh_tvb, rh_offset, RH_LEN, FALSE);
rh_tree = proto_item_add_subtree(rh_ti, ett_sna_rh);
dissect_rh(tvb, offset, rh_tree);
sna_header_len += 3;
offset += 3;
}
else {
sna_header_len += 3;
offset += 3;
dissect_rh(rh_tvb, rh_offset, rh_tree);
}
if (tvb_offset_exists(tvb, offset+1)) {
call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1),
rh_offset += RH_LEN;
if (tvb_offset_exists(rh_tvb, rh_offset+1)) {
/* Short-circuit ? */
if (continue_dissecting == rh_only) {
if (tree) {
proto_tree_add_text(tree, rh_tvb, rh_offset, -1,
"BIU segment data");
}
return;
}
call_dissector(data_handle, tvb_new_subset(rh_tvb, rh_offset, -1, -1),
pinfo, parent_tree);
}
}
#define FIRST_FRAG_NUMBER 0
#define MIDDLE_FRAG_NUMBER 1
#define LAST_FRAG_NUMBER 2
/* FID2 is defragged by sequence. The weird thing is that we have neither
* absolute sequence numbers, nor byte offets. Other FIDs have byte offsets
* (the DCF field), but not FID2. The only thing we have to go with is "FIRST",
* "MIDDLE", or "LAST". If the BIU is split into 3 frames, then everything is
* fine, * "FIRST", "MIDDLE", and "LAST" map nicely onto frag-number 0, 1,
* and 2. However, if the BIU is split into 2 frames, then we only have
* "FIRST" and "LAST", and the mapping *should* be frag-number 0 and 1,
* *NOT* 0 and 2.
*
* The SNA docs say "FID2 PIUs cannot be blocked because there is no DCF in the
* TH format for deblocking" (note on Figure 4-2 in the IBM SNA documention,
* see the FTP URL in the comment near the top of this file). I *think*
* this means that the fragmented frames cannot arrive out of order.
* Well, I *want* it to mean this, because w/o this limitation, if you
* get a "FIRST" frame and a "LAST" frame, how long should you wait to
* see if a "MIDDLE" frame every arrives????? Thus, if frames *have* to
* arrive in order, then we're saved.
*
* The problem then boils down to figuring out if "LAST" means frag-number 1
* (in the case of a BIU split into 2 frames) or frag-number 2
* (in the case of a BIU split into 3 frames).
*
* Assuming fragmented FID2 BIU frames *do* arrive in order, the obvious
* way to handle the mapping of "LAST" to either frag-number 1 or
* frag-number 2 is to keep a hash which tracks the frames seen, etc.
* This consumes resources. A trickier way, but a way which works, is to
* always map the "LAST" BIU segment to frag-number 2. Here's the trickery:
* if we add frag-number 2, which we know to be the "LAST" BIU segment,
* and the reassembly code tells us that the the BIU is still not reassmebled,
* then, owing to the, ahem, /fact/, that fragmented BIU segments arrive
* in order :), we know that 1) "FIRST" did come, and 2) there's no "MIDDLE",
* because this BIU was fragmented into 2 frames, not 3. So, we'll be
* tricky and add a zero-length "MIDDLE" BIU frame (i.e, frag-number 1)
* to complete the reassembly.
*/
static tvbuff_t*
defragment_by_sequence(packet_info *pinfo, tvbuff_t *tvb, int offset, int mpf, int id)
{
fragment_data *fd_head;
int frag_number = -1;
int more_frags = TRUE;
tvbuff_t *rh_tvb = NULL;
/* Determine frag_number and more_frags */
switch(mpf) {
case MPF_WHOLE_BIU:
/* nothing */
break;
case MPF_FIRST_SEGMENT:
frag_number = FIRST_FRAG_NUMBER;
break;
case MPF_MIDDLE_SEGMENT:
frag_number = MIDDLE_FRAG_NUMBER;
break;
case MPF_LAST_SEGMENT:
frag_number = LAST_FRAG_NUMBER;
more_frags = FALSE;
break;
default:
g_assert_not_reached();
}
/* If sna_defragment is on, and this is a fragment.. */
if (frag_number > -1) {
/* XXX - check length ??? */
fd_head = fragment_add_seq(tvb, offset, pinfo, id,
sna_fragment_table,
frag_number,
tvb_length_remaining(tvb, offset),
more_frags);
/* We added the LAST segment and reassembly didn't complete. Insert
* a zero-length MIDDLE segment to turn a 2-frame BIU-fragmentation
* into a 3-frame BIU-fragmentation (empty middle frag).
* See above long comment about this trickery. */
if (mpf == MPF_LAST_SEGMENT && !fd_head) {
fd_head = fragment_add_seq(tvb, offset, pinfo, id,
sna_fragment_table, MIDDLE_FRAG_NUMBER,
0, TRUE);
}
if (fd_head != NULL) {
/* We have the complete reassembled payload. */
rh_tvb = tvb_new_real_data(fd_head->data,
fd_head->len, fd_head->len);
/* Add the tvbuff to the chain of tvbuffs so that
* it will get cleaned up too. */
tvb_set_child_real_data_tvbuff(tvb, rh_tvb);
/* Add the defragmented data to the data source list. */
add_new_data_source(pinfo, rh_tvb, "Reassembled SNA BIU");
}
}
return rh_tvb;
}
#define SNA_FID01_ADDR_LEN 2
/* FID Types 0 and 1 */
@ -590,17 +751,21 @@ dissect_fid0_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* FID Type 2 */
static int
dissect_fid2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
dissect_fid2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
tvbuff_t **rh_tvb_ptr, next_dissection_t *continue_dissecting)
{
proto_tree *bf_tree;
proto_item *bf_item;
guint8 th_0=0, daf=0, oaf=0;
const guint8 *ptr;
unsigned int mpf, id;
const int bytes_in_header = 6;
th_0 = tvb_get_guint8(tvb, 0);
mpf = mpf_value(th_0);
if (tree) {
th_0 = tvb_get_guint8(tvb, 0);
daf = tvb_get_guint8(tvb, 2);
oaf = tvb_get_guint8(tvb, 3);
@ -613,6 +778,7 @@ dissect_fid2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree_add_uint(bf_tree, hf_sna_th_odai,tvb, 0, 1, th_0);
proto_tree_add_uint(bf_tree, hf_sna_th_efi, tvb, 0, 1, th_0);
/* Byte 1 */
proto_tree_add_text(tree, tvb, 1, 1, "Reserved");
@ -637,10 +803,25 @@ dissect_fid2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
SET_ADDRESS(&pinfo->net_src, AT_SNA, SNA_FID2_ADDR_LEN, ptr);
SET_ADDRESS(&pinfo->src, AT_SNA, SNA_FID2_ADDR_LEN, ptr);
id = tvb_get_ntohs(tvb, 4);
if (tree) {
proto_tree_add_item(tree, hf_sna_th_snf, tvb, 4, 2, FALSE);
proto_tree_add_uint(tree, hf_sna_th_snf, tvb, 4, 2, id);
}
if (mpf != MPF_WHOLE_BIU && !sna_defragment) {
if (mpf == MPF_FIRST_SEGMENT) {
*continue_dissecting = rh_only;
}
else {
*continue_dissecting = stop_here;
}
}
else if (sna_defragment) {
*rh_tvb_ptr = defragment_by_sequence(pinfo, tvb, bytes_in_header,
mpf, id);
}
return bytes_in_header;
}
@ -1136,6 +1317,14 @@ dissect_rh(tvbuff_t *tvb, int offset, proto_tree *tree)
/* XXX - check for sdi. If TRUE, the next 4 bytes will be sense data */
}
static void
sna_init(void)
{
fragment_table_init(&sna_fragment_table);
reassembled_table_init(&sna_reassembled_table);
}
void
proto_register_sna(void)
{
@ -1590,12 +1779,21 @@ proto_register_sna(void)
&ett_sna_rh_1,
&ett_sna_rh_2,
};
module_t *sna_module;
proto_sna = proto_register_protocol("Systems Network Architecture",
"SNA", "sna");
proto_register_field_array(proto_sna, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
register_dissector("sna", dissect_sna, proto_sna);
/* Register configuration options */
sna_module = prefs_register_protocol(proto_sna, NULL);
prefs_register_bool_preference(sna_module, "defragment",
"Reassemble fragmented BIUs",
"Whether fragmented BIUs should be reassembled",
&sna_defragment);
}
void
@ -1608,4 +1806,6 @@ proto_reg_handoff_sna(void)
/* RFC 2043 */
dissector_add("ppp.protocol", PPP_SNA, sna_handle);
data_handle = find_dissector("data");
register_init_routine(sna_init);
}