*
* Routines to dissect WTP component of WAP traffic.
*
- * $Id: packet-wtp.c,v 1.53 2003/11/05 04:23:59 guy Exp $
+ * $Id: packet-wtp.c,v 1.63 2004/02/28 22:56:36 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
"False"
};
-static const value_string vals_pdu_type[] = {
+static const value_string vals_wtp_pdu_type[] = {
{ 0, "Not Allowed" },
{ 1, "Invoke" },
{ 2, "Result" },
static int hf_wtp_fragment_multiple_tails = HF_EMPTY;
static int hf_wtp_fragment_too_long_fragment = HF_EMPTY;
static int hf_wtp_fragment_error = HF_EMPTY;
+static int hf_wtp_reassembled_in = HF_EMPTY;
/* Initialize the subtree pointers */
static gint ett_wtp = ETT_EMPTY;
&hf_wtp_fragment_multiple_tails,
&hf_wtp_fragment_too_long_fragment,
&hf_wtp_fragment_error,
- NULL,
+ &hf_wtp_reassembled_in,
"fragments"
};
int abortType = 0;
/* Set up structures we'll need to add the protocol subtree and manage it */
- proto_item *ti;
+ proto_item *ti = NULL;
proto_tree *wtp_tree = NULL;
char pdut;
int numMissing = 0; /* Number of missing packets in a negative ack */
int i;
tvbuff_t *wsp_tvb = NULL;
- fragment_data *fd_head = NULL;
guint8 psn = 0; /* Packet sequence number*/
guint16 TID = 0; /* Transaction-Id */
+ int dataOffset;
+ gint dataLen;
if (szInfo == NULL)
szInfo = g_string_sized_new(32);
ti = proto_tree_add_item(tree, proto_wtp,
tvb, offCur, 1, bo_little_endian);
wtp_tree = proto_item_add_subtree(ti, ett_wtp_sub_pdu_tree);
+ proto_item_append_text(ti, ", PDU concatenation");
}
offCur = 1;
i = 1;
while (offCur < (int) tvb_reported_length(tvb)) {
+ tvbuff_t *wtp_tvb;
+ /* The length of an embedded WTP PDU is coded as either:
+ * - a 7-bit value contained in one octet with highest bit == 0.
+ * - a 15-bit value contained in two octets (little endian)
+ * if the 1st octet has its highest bit == 1.
+ * This means that this is NOT encoded as an uintvar-integer!!!
+ */
b0 = tvb_get_guint8(tvb, offCur + 0);
if (b0 & 0x80) {
c_fieldlen = 2;
if (i > 1 && check_col(pinfo->cinfo, COL_INFO)) {
col_append_str(pinfo->cinfo, COL_INFO, ", ");
}
- wsp_tvb = tvb_new_subset(tvb, offCur + c_fieldlen, c_pdulen, c_pdulen);
- dissect_wtp_common(wsp_tvb, pinfo, wtp_tree);
+ /* Skip the length field for the WTP sub-tvb */
+ wtp_tvb = tvb_new_subset(tvb, offCur + c_fieldlen, c_pdulen, c_pdulen);
+ dissect_wtp_common(wtp_tvb, pinfo, wtp_tree);
offCur += c_fieldlen + c_pdulen;
i++;
}
+ if (tree) {
+ proto_item_append_text(ti, ", PDU count: %u", i);
+ }
return;
}
+ /* No concatenation */
fCon = b0 & 0x80;
fRID = retransmission_indicator(b0);
pdut = pdu_type(b0);
+#ifdef DEBUG
+ printf("WTP packet %u: tree = %p, pdu = %s (%u) length: %u\n",
+ pinfo->fd->num, tree,
+ match_strval(pdut, vals_wtp_pdu_type), pdut, tvb_length(tvb));
+#endif
+
/* Develop the string to put in the Info column */
g_string_sprintf(szInfo, "WTP %s",
- val_to_str(pdut, vals_pdu_type, "Unknown PDU type 0x%x"));
+ val_to_str(pdut, vals_wtp_pdu_type, "Unknown PDU type 0x%x"));
switch (pdut) {
case INVOKE:
fTTR = transmission_trailer(b0);
TID = tvb_get_ntohs(tvb, offCur + 1);
psn = tvb_get_guint8(tvb, offCur + 3);
+ if (psn != 0)
+ g_string_sprintfa(szInfo, " (%u)", psn);
cbHeader = 4;
break;
if (fRID) {
g_string_append( szInfo, " R" );
};
- if (fCon) { /* Scan variable part (TPI's), */
- /* determine length of it */
- unsigned char tCon;
- unsigned char tByte;
-
- do {
- tByte = tvb_get_guint8(tvb, offCur + cbHeader + vHeader);
- tCon = tByte & 0x80;
- if (tByte & 0x04) /* Long format */
- vHeader = vHeader + tvb_get_guint8(tvb,
- offCur + cbHeader + vHeader + 1) + 2;
- else
- vHeader = vHeader + (tByte & 0x03) + 1;
- } while (tCon);
- }
-
-#ifdef DEBUG
- fprintf( stderr, "dissect_wtp: cbHeader = %d\n", cbHeader );
-#endif
-
- /* Only update "Info" column when no data in this PDU will
- * be handed off to a subsequent dissector.
- */
- if (check_col(pinfo->cinfo, COL_INFO) &&
- ((tvb_length_remaining(tvb, offCur + cbHeader + vHeader) <= 0) ||
- (pdut == ACK) || (pdut==NEGATIVE_ACK) || (pdut==ABORT)) ) {
-#ifdef DEBUG
- fprintf(stderr, "dissect_wtp: (6) About to set info_col header to %s\n", szInfo->str);
-#endif
- col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
- };
/* In the interest of speed, if "tree" is NULL, don't do any work not
necessary to generate protocol tree items. */
if (tree) {
#ifdef DEBUG
fprintf(stderr, "dissect_wtp: cbHeader = %d\n", cbHeader);
#endif
- ti = proto_tree_add_item(tree, proto_wtp, tvb, offCur, cbHeader + vHeader, bo_little_endian);
+ /* NOTE - Length will be set when we process the TPI */
+ ti = proto_tree_add_item(tree, proto_wtp, tvb, offCur, 0, bo_little_endian);
#ifdef DEBUG
fprintf(stderr, "dissect_wtp: (7) Returned from proto_tree_add_item\n");
#endif
proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_flag_UP, tvb, offCur + 3, 1, bo_little_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_Reserved, tvb, offCur + 3, 1, bo_little_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_TransactionClass, tvb, offCur + 3, 1, bo_little_endian);
+ proto_item_append_text(ti,
+ ", PDU: Invoke (%u)"
+ ", Transaction Class: %s (%u)",
+ INVOKE,
+ match_strval(clsTransaction, vals_transaction_classes),
+ clsTransaction);
break;
case RESULT:
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, bo_little_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, bo_big_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
+ proto_item_append_text(ti, ", PDU: Result (%u)", RESULT);
break;
case ACK:
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, bo_little_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, bo_big_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
+ proto_item_append_text(ti, ", PDU: ACK (%u)", ACK);
break;
case ABORT:
if (abortType == PROVIDER)
{
+ guint8 reason = tvb_get_guint8(tvb, offCur + 3);
proto_tree_add_item( wtp_tree, hf_wtp_header_Abort_reason_provider , tvb, offCur + 3 , 1, bo_little_endian);
+ proto_item_append_text(ti,
+ ", PDU: Abort (%u)"
+ ", Type: Provider (%u)"
+ ", Reason: %s (%u)",
+ ABORT,
+ PROVIDER,
+ match_strval(reason, vals_abort_reason_provider),
+ reason);
}
else if (abortType == USER)
{
+ guint8 reason = tvb_get_guint8(tvb, offCur + 3);
proto_tree_add_item(wtp_tree, hf_wtp_header_Abort_reason_user , tvb, offCur + 3 , 1, bo_little_endian);
+ proto_item_append_text(ti,
+ ", PDU: Abort (%u)"
+ ", Type: User (%u)"
+ ", Reason: %s (%u)",
+ ABORT,
+ PROVIDER,
+ match_strval(reason, vals_wsp_reason_codes),
+ reason);
}
break;
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number , tvb, offCur + 3, 1, bo_little_endian);
+ proto_item_append_text(ti,
+ ", PDU: Segmented Invoke (%u)"
+ ", Packet Sequence Number: %u",
+ SEGMENTED_INVOKE, psn);
break;
case SEGMENTED_RESULT:
proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number , tvb, offCur + 3, 1, bo_little_endian);
+ proto_item_append_text(ti,
+ ", PDU: Segmented Result (%u)"
+ ", Packet Sequence Number: %u",
+ SEGMENTED_RESULT, psn);
break;
case NEGATIVE_ACK:
{
proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number, tvb, offCur + 4 + i, 1, bo_little_endian);
}
+ proto_item_append_text(ti,
+ ", PDU: Negative Ack (%u)"
+ ", Missing Packets: %u",
+ NEGATIVE_ACK, numMissing);
break;
default:
break;
};
+ if (fRID) {
+ proto_item_append_text(ti, ", Retransmission");
+ }
+ } else { /* tree is NULL */
+#ifdef DEBUG
+ fprintf(stderr, "dissect_wtp: (4) tree was %p\n", tree);
+#endif
+ }
+ /* Process the variable part */
if (fCon) { /* Now, analyze variable part */
unsigned char tCon;
unsigned char tByte;
offCur + cbHeader + vHeader + 1);
else
tpiLen = 1 + (tByte & 0x03);
+ if (tree)
+ {
tmp_tvb = tvb_new_subset(tvb, offCur + cbHeader + vHeader,
tpiLen, tpiLen);
wtp_handle_tpi(wtp_tree, tmp_tvb);
+ }
vHeader += tpiLen;
} while (tCon);
} else {
/* There is no variable part */
} /* End of variable part of header */
- } else {
+
+ /* Set the length of the WTP protocol part now we know the length of the
+ * fixed and variable WTP headers */
+ if (tree)
+ proto_item_set_len(ti, cbHeader + vHeader);
+
#ifdef DEBUG
- fprintf(stderr, "dissect_wtp: (4) tree was %p\n", tree);
+ fprintf( stderr, "dissect_wtp: cbHeader = %d\n", cbHeader );
#endif
- }
+
/*
* Any remaining data ought to be WSP data (if not WTP ACK, NACK
- * or ABORT pdu), so hand off (defragmented) to the WSP dissector.
+ * or ABORT pdu), so, if we have any remaining data, and it's
+ * not an ACK, NACK, or ABORT PDU, hand it off (defragmented) to the
+ * WSP dissector.
* Note that the last packet of a fragmented WTP message needn't
* contain any data, so we allow payloadless packets to be
* reassembled. (XXX - does the reassembly code handle this
* for packets other than the last packet?)
- */
- if ((tvb_reported_length_remaining(tvb, offCur + cbHeader + vHeader) >= 0) &&
- ! ((pdut==ACK) || (pdut==NEGATIVE_ACK) || (pdut==ABORT)))
+ *
+ * Try calling a subdissector only if:
+ * - The WTP payload is ressembled in this very packet,
+ * - The WTP payload is not fragmented across packets.
+ */
+ dataOffset = offCur + cbHeader + vHeader;
+ dataLen = tvb_reported_length_remaining(tvb, dataOffset);
+ if ((dataLen >= 0) &&
+ ! ((pdut==ACK) || (pdut==NEGATIVE_ACK) || (pdut==ABORT)))
{
- int dataOffset = offCur + cbHeader + vHeader;
- gint dataLen = tvb_reported_length_remaining(tvb, dataOffset);
- gboolean save_fragmented;
-
- if (((pdut == SEGMENTED_INVOKE) || (pdut == SEGMENTED_RESULT) ||
- (((pdut == INVOKE) || (pdut == RESULT)) && (!fTTR))) &&
- tvb_bytes_exist(tvb, dataOffset, dataLen))
- { /* 1st part of segment */
- save_fragmented = pinfo->fragmented;
- pinfo->fragmented = TRUE;
- fd_head = fragment_add_seq(tvb, dataOffset, pinfo, TID,
- wtp_fragment_table, psn, dataLen, !fTTR);
- if (fd_head != NULL) /* Reassembled */
- {
- /* Reassembly is complete; show the reassembled PDU */
- wsp_tvb = tvb_new_real_data(fd_head->data,
- fd_head->len,
- fd_head->len);
- tvb_set_child_real_data_tvbuff(tvb, wsp_tvb);
- add_new_data_source(pinfo, wsp_tvb,
- "Reassembled WTP");
- pinfo->fragmented = FALSE;
-
- /* show all fragments */
- show_fragment_seq_tree(fd_head, &wtp_frag_items,
- wtp_tree, pinfo, wsp_tvb);
-
- call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
- }
- else
- {
- /* Reassembly isn't complete; just show the fragment */
- if (check_col(pinfo->cinfo, COL_INFO)) /* Won't call WSP so display */
- col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
- if (tree != NULL)
- proto_tree_add_text(wtp_tree, tvb, dataOffset, -1, "Payload");
- }
- pinfo->fragmented = save_fragmented;
+ /* Try to reassemble if needed, and hand over to WSP
+ * A fragmented WTP packet is either:
+ * - An INVOKE with fTTR (transmission trailer) not set,
+ * - a SEGMENTED_INVOKE,
+ * - A RESULT with fTTR (transmission trailer) not set,
+ * - a SEGMENTED_RESULT.
+ */
+ if ( ( (pdut == SEGMENTED_INVOKE) || (pdut == SEGMENTED_RESULT)
+ || ( ((pdut == INVOKE) || (pdut == RESULT)) && (!fTTR) )
+ ) && tvb_bytes_exist(tvb, dataOffset, dataLen) )
+ {
+ /* Try reassembling fragments */
+ fragment_data *fd_wtp = NULL;
+ guint32 reassembled_in = 0;
+ gboolean save_fragmented = pinfo->fragmented;
+
+ pinfo->fragmented = TRUE;
+ fd_wtp = fragment_add_seq(tvb, dataOffset, pinfo, TID,
+ wtp_fragment_table, psn, dataLen, !fTTR);
+ /* XXX - fragment_add_seq() yields NULL unless Ethereal knows
+ * that the packet is part of a reassembled whole. This means
+ * that fd_wtp will be NULL as long as Ethereal did not encounter
+ * (and process) the packet containing the last fragment.
+ * This implies that Ethereal needs two passes over the data for
+ * correct reassembly. At the first pass, a capture containing
+ * three fragments plus a retransmssion of the last fragment
+ * will progressively show:
+ *
+ * Packet 1: (Unreassembled fragment 1)
+ * Packet 2: (Unreassembled fragment 2)
+ * Packet 3: (Reassembled WTP)
+ * Packet 4: (WTP payload reassembled in packet 3)
+ *
+ * However at subsequent evaluation (e.g., by applying a display
+ * filter) the packet summary will show:
+ *
+ * Packet 1: (WTP payload reassembled in packet 3)
+ * Packet 2: (WTP payload reassembled in packet 3)
+ * Packet 3: (Reassembled WTP)
+ * Packet 4: (WTP payload reassembled in packet 3)
+ *
+ * This is important to know, and also affects read filters!
+ */
+ wsp_tvb = process_reassembled_data(tvb, dataOffset, pinfo,
+ "Reassembled WTP", fd_wtp, &wtp_frag_items,
+ NULL, wtp_tree);
+#ifdef DEBUG
+ printf("WTP: Packet %u %s -> %d: wsp_tvb = %p, fd_wtp = %p, frame = %u\n",
+ pinfo->fd->num,
+ fd_wtp ? "Reassembled" : "Not reassembled",
+ fd_wtp ? fd_wtp->reassembled_in : -1,
+ wsp_tvb,
+ fd_wtp
+ );
+#endif
+ if (fd_wtp) {
+ /* Reassembled */
+ reassembled_in = fd_wtp->reassembled_in;
+ if (pinfo->fd->num == reassembled_in) {
+ /* Reassembled in this very packet:
+ * We can safely hand the tvb to the WSP dissector */
+ call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
+ } else {
+ /* Not reassembled in this packet */
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ "%s (WTP payload reassembled in packet %u)",
+ szInfo->str, fd_wtp->reassembled_in);
+ }
+ if (tree) {
+ proto_tree_add_text(wtp_tree, tvb, dataOffset, -1,
+ "Payload");
+ }
+ }
+ } else {
+ /* Not reassembled yet, or not reassembled at all */
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ "%s (Unreassembled fragment %u)",
+ szInfo->str, psn);
+ }
+ if (tree) {
+ proto_tree_add_text(wtp_tree, tvb, dataOffset, -1,
+ "Payload");
+ }
+ }
+ /* Now reset fragmentation information in pinfo */
+ pinfo->fragmented = save_fragmented;
+ }
+ else if ( ((pdut == INVOKE) || (pdut == RESULT)) && (fTTR) )
+ {
+ /* Non-fragmented payload */
+ wsp_tvb = tvb_new_subset(tvb, dataOffset, -1, -1);
+ /* We can safely hand the tvb to the WSP dissector */
+ call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
+ }
+ else
+ {
+ /* Nothing to hand to subdissector */
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
+ }
}
else
{
- /*
- * Normal packet, or not all the fragment data is available;
- * call next dissector, unless this is a segment of a
- * segmented invoke or result and isn't the first segment.
- */
- if ((pdut == SEGMENTED_INVOKE || pdut == SEGMENTED_RESULT) &&
- psn != 0) {
- /*
- * This is a segmented invoke or result, and not the first
- * segment; just show it as a segmented invoke or result,
- * don't try to dissect its payload.
- */
+ /* Nothing to hand to subdissector */
if (check_col(pinfo->cinfo, COL_INFO))
- col_add_fstr(pinfo->cinfo, COL_INFO,
- "WTP %s (%u)",
- (pdut == SEGMENTED_INVOKE ?
- "Segmented Invoke" : "Segmented Result"),
- psn);
- if (tree != NULL)
- proto_tree_add_text(wtp_tree, tvb, dataOffset, -1, "Payload");
- } else {
- wsp_tvb = tvb_new_subset(tvb, dataOffset, -1, -1);
- call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
- }
+ col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
}
- }
}
/*
* XXX - can this be called from any other dissector?
*/
static void
-dissect_wtp_fromwap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+dissect_wtp_fromwtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "WTLS+WTP+WSP" );
{ &hf_wtp_header_pdu_type,
{ "PDU Type",
"wtp.pdu_type",
- FT_UINT8, BASE_HEX, VALS( vals_pdu_type ), 0x78,
+ FT_UINT8, BASE_HEX, VALS( vals_wtp_pdu_type ), 0x78,
"PDU Type", HFILL
}
},
"Defragmentation error due to illegal fragments", HFILL
}
},
+ { &hf_wtp_reassembled_in,
+ { "Reassembled in",
+ "wtp.reassembled.in",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "WTP fragments are reassembled in the given packet", HFILL
+ }
+ },
{ &hf_wtp_fragment,
{ "WTP Fragment",
"wtp.fragment",
proto_wtp = proto_register_protocol(
"Wireless Transaction Protocol", /* protocol name for use by ethereal */
"WTP", /* short version of name */
- "wap-wsp-wtp" /* Abbreviated protocol name, should Match IANA
- < URL:http://www.isi.edu/in-notes/iana/assignments/port-numbers/ >
+ "wtp" /* Abbreviated protocol name, should Match IANA
+ < URL:http://www.iana.org/assignments/port-numbers/ >
*/
);
proto_register_field_array(proto_wtp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
- register_dissector("wtp", dissect_wtp_fromwap, proto_wtp);
+ register_dissector("wtp-wtls", dissect_wtp_fromwtls, proto_wtp);
register_dissector("wtp-udp", dissect_wtp_fromudp, proto_wtp);
register_init_routine(wtp_defragment_init);
-};
+}
void
proto_reg_handoff_wtp(void)
wtp_fromudp_handle = find_dissector("wtp-udp");
dissector_add("udp.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
- dissector_add("smpp.udh.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
+ dissector_add("gsm-sms-ud.udh.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
}