2009-02-25 00:36:31 +00:00
|
|
|
/* packet-nat-pmp.c
|
2009-02-25 00:22:47 +00:00
|
|
|
* Routines for NAT Port Mapping Protocol packet disassembly.
|
2009-02-25 00:36:31 +00:00
|
|
|
* draft-cheshire-nat-pmp-03
|
|
|
|
* http://files.dns-sd.org/draft-cheshire-nat-pmp.txt
|
2009-02-25 00:22:47 +00:00
|
|
|
*
|
2009-03-15 21:13:27 +00:00
|
|
|
* Copyright 2009, Stig Bjorlykke <stig@bjorlykke.org>
|
2009-02-25 00:22:47 +00:00
|
|
|
*
|
2013-02-01 01:29:58 +00:00
|
|
|
* Routines for Port Control Protocol packet disassembly
|
|
|
|
* (backwards compatible with NAT Port Mapping protocol)
|
|
|
|
* http://tools.ietf.org/html/draft-ietf-pcp-base-24
|
|
|
|
* http://tools.ietf.org/html/draft-ietf-pcp-base-29
|
|
|
|
*
|
|
|
|
* Copyright 2012, Michael Mann
|
|
|
|
*
|
|
|
|
*
|
2009-02-25 00:22:47 +00:00
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* 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
|
2012-06-28 22:56:06 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2009-02-25 00:22:47 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <epan/packet.h>
|
|
|
|
#include <epan/expert.h>
|
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
#define PCP_STATUS_PORT 5350
|
|
|
|
#define PCP_PORT 5351
|
2009-02-25 00:22:47 +00:00
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
/* NAT Port opcodes */
|
2009-02-25 00:22:47 +00:00
|
|
|
#define EXTERNAL_ADDRESS_REQUEST 0
|
|
|
|
#define MAP_UDP_REQUEST 1
|
|
|
|
#define MAP_TCP_REQUEST 2
|
|
|
|
#define EXTERNAL_ADDRESS_RESPONSE 128
|
|
|
|
#define MAP_UDP_RESPONSE 129
|
|
|
|
#define MAP_TCP_RESPONSE 130
|
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
/* Port Control opcodes */
|
|
|
|
#define ANNOUNCE_REQUEST 0
|
|
|
|
#define MAP_REQUEST 1
|
|
|
|
#define PEER_REQUEST 2
|
|
|
|
#define ANNOUNCE_RESPONSE 128
|
|
|
|
#define MAP_RESPONSE 129
|
|
|
|
#define PEER_RESPONSE 130
|
|
|
|
|
|
|
|
/* Port Control options */
|
|
|
|
#define OPT_THIRD_PARTY 1
|
|
|
|
#define OPT_PREFER_FAILURE 2
|
|
|
|
#define OPT_FILTER 3
|
|
|
|
|
2009-02-25 00:36:31 +00:00
|
|
|
static int proto_nat_pmp = -1;
|
2013-02-01 01:29:58 +00:00
|
|
|
static int proto_pcp = -1;
|
2009-02-25 00:22:47 +00:00
|
|
|
|
|
|
|
static int hf_version = -1;
|
|
|
|
static int hf_opcode = -1;
|
|
|
|
static int hf_result_code = -1;
|
|
|
|
static int hf_sssoe = -1;
|
|
|
|
static int hf_external_ip = -1;
|
|
|
|
static int hf_reserved = -1;
|
|
|
|
static int hf_internal_port = -1;
|
|
|
|
static int hf_external_port_requested = -1;
|
|
|
|
static int hf_external_port_mapped = -1;
|
|
|
|
static int hf_rpmlis = -1;
|
|
|
|
static int hf_pmlis = -1;
|
|
|
|
|
2009-02-25 00:36:31 +00:00
|
|
|
static gint ett_nat_pmp = -1;
|
2009-02-25 00:22:47 +00:00
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
/* Port Control Protocol */
|
|
|
|
static int hf_pcp_version = -1;
|
|
|
|
static int hf_request = -1;
|
|
|
|
static int hf_response = -1;
|
|
|
|
static int hf_pcp_opcode = -1;
|
|
|
|
static int hf_pcp_result_code = -1;
|
|
|
|
static int hf_reserved1 = -1;
|
|
|
|
static int hf_reserved2 = -1;
|
|
|
|
static int hf_reserved8 = -1;
|
|
|
|
static int hf_req_lifetime = -1;
|
|
|
|
static int hf_rsp_lifetime = -1;
|
|
|
|
static int hf_client_ip = -1;
|
|
|
|
static int hf_epoch_time = -1;
|
|
|
|
static int hf_map_nonce = -1;
|
|
|
|
static int hf_map_protocol = -1;
|
|
|
|
static int hf_map_reserved1 = -1;
|
|
|
|
static int hf_map_internal_port = -1;
|
|
|
|
static int hf_map_req_sug_external_port = -1;
|
|
|
|
static int hf_map_req_sug_ext_ip = -1;
|
|
|
|
static int hf_map_rsp_assigned_external_port = -1;
|
|
|
|
static int hf_map_rsp_assigned_ext_ip = -1;
|
|
|
|
static int hf_peer_nonce = -1;
|
|
|
|
static int hf_peer_protocol = -1;
|
|
|
|
static int hf_peer_reserved1 = -1;
|
|
|
|
static int hf_peer_internal_port = -1;
|
|
|
|
static int hf_peer_req_sug_external_port = -1;
|
|
|
|
static int hf_peer_req_sug_ext_ip = -1;
|
|
|
|
static int hf_peer_remote_peer_port = -1;
|
|
|
|
static int hf_peer_reserved2 = -1;
|
|
|
|
static int hf_peer_remote_peer_ip = -1;
|
|
|
|
static int hf_peer_rsp_assigned_external_port = -1;
|
|
|
|
static int hf_peer_rsp_assigned_ext_ip = -1;
|
|
|
|
static int hf_option_code = -1;
|
|
|
|
static int hf_option_reserved = -1;
|
|
|
|
static int hf_option_length = -1;
|
|
|
|
static int hf_option_third_party_internal_ip = -1;
|
|
|
|
static int hf_option_filter_reserved = -1;
|
|
|
|
static int hf_option_filter_prefix_length = -1;
|
|
|
|
static int hf_option_filter_remote_peer_port = -1;
|
|
|
|
static int hf_option_filter_remote_peer_ip = -1;
|
|
|
|
|
|
|
|
static gint ett_pcp = -1;
|
|
|
|
static gint ett_opcode = -1;
|
|
|
|
static gint ett_option = -1;
|
|
|
|
static gint ett_suboption = -1;
|
|
|
|
|
|
|
|
|
2009-02-25 00:22:47 +00:00
|
|
|
static const value_string opcode_vals[] = {
|
|
|
|
{ EXTERNAL_ADDRESS_REQUEST, "External Address Request" },
|
|
|
|
{ EXTERNAL_ADDRESS_RESPONSE, "External Address Response" },
|
|
|
|
{ MAP_UDP_REQUEST, "Map UDP Request" },
|
|
|
|
{ MAP_UDP_RESPONSE, "Map UDP Response" },
|
|
|
|
{ MAP_TCP_REQUEST, "Map TCP Request" },
|
|
|
|
{ MAP_TCP_RESPONSE, "Map TCP Response" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string result_vals[] = {
|
|
|
|
{ 0, "Success" },
|
|
|
|
{ 1, "Unsupported Version" },
|
|
|
|
{ 2, "Not Authorized/Refused" },
|
|
|
|
{ 3, "Network Failure" },
|
|
|
|
{ 4, "Out of resources" },
|
|
|
|
{ 5, "Unsupported opcode" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
static const value_string pcp_opcode_vals[] = {
|
|
|
|
{ ANNOUNCE_REQUEST, "Announce Request" },
|
|
|
|
{ MAP_REQUEST, "Map Request" },
|
|
|
|
{ PEER_REQUEST, "Peer Request" },
|
|
|
|
{ ANNOUNCE_RESPONSE, "Announce Response" },
|
|
|
|
{ MAP_RESPONSE, "Map Response" },
|
|
|
|
{ PEER_RESPONSE, "Peer Response" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string pcp_result_vals[] = {
|
|
|
|
{ 0, "Success" },
|
|
|
|
{ 1, "Unsupported Version" },
|
|
|
|
{ 2, "Not Authorized/Refused" },
|
|
|
|
{ 3, "Malformed Request" },
|
|
|
|
{ 4, "Unsupported opcode" },
|
|
|
|
{ 5, "Unsupported option" },
|
|
|
|
{ 6, "Malformed option" },
|
|
|
|
{ 7, "Network failure" },
|
|
|
|
{ 8, "No resources" },
|
|
|
|
{ 9, "Unsupported protocol" },
|
|
|
|
{ 10, "User exceeds quota" },
|
|
|
|
{ 11, "Cannot provide external port" },
|
|
|
|
{ 12, "Address mismatch" },
|
|
|
|
{ 13, "Excessive remote peers" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string pcp_option_vals[] = {
|
|
|
|
{ 0, "Reserved" },
|
|
|
|
{ OPT_THIRD_PARTY, "Third Party" },
|
|
|
|
{ OPT_PREFER_FAILURE, "Prefer Failure" },
|
|
|
|
{ OPT_FILTER, "Filter" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
dissect_nat_pmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
2009-02-25 00:22:47 +00:00
|
|
|
{
|
2009-02-25 00:36:31 +00:00
|
|
|
proto_tree *nat_pmp_tree;
|
2009-02-25 00:22:47 +00:00
|
|
|
proto_item *ti, *op_ti;
|
2013-02-01 01:29:58 +00:00
|
|
|
gint start_offset, offset = 0;
|
2009-02-25 00:22:47 +00:00
|
|
|
guint8 opcode;
|
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
col_set_str (pinfo->cinfo, COL_PROTOCOL, "NAT-PMP");
|
2009-07-07 10:07:42 +00:00
|
|
|
col_clear (pinfo->cinfo, COL_INFO);
|
2009-02-25 00:22:47 +00:00
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
start_offset = offset;
|
2011-07-19 18:48:31 +00:00
|
|
|
ti = proto_tree_add_item (tree, proto_nat_pmp, tvb, offset, -1, ENC_NA);
|
2009-02-25 00:36:31 +00:00
|
|
|
nat_pmp_tree = proto_item_add_subtree (ti, ett_nat_pmp);
|
2009-02-25 00:22:47 +00:00
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_version, tvb, offset, 1, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset++;
|
|
|
|
|
|
|
|
opcode = tvb_get_guint8 (tvb, offset);
|
|
|
|
proto_item_append_text (ti, ", %s", val_to_str (opcode, opcode_vals, "Unknown opcode: %d"));
|
2011-07-19 10:51:12 +00:00
|
|
|
op_ti = proto_tree_add_item (nat_pmp_tree, hf_opcode, tvb, offset, 1, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset++;
|
|
|
|
|
2009-09-06 05:29:51 +00:00
|
|
|
col_add_str (pinfo->cinfo, COL_INFO, val_to_str (opcode, opcode_vals, "Unknown opcode: %d"));
|
2009-02-25 00:22:47 +00:00
|
|
|
|
|
|
|
switch (opcode) {
|
|
|
|
|
|
|
|
case EXTERNAL_ADDRESS_REQUEST:
|
|
|
|
/* No more data */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EXTERNAL_ADDRESS_RESPONSE:
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_result_code, tvb, offset, 2, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 2;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_sssoe, tvb, offset, 4, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 4;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_external_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MAP_UDP_REQUEST:
|
|
|
|
case MAP_TCP_REQUEST:
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 2;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_internal_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 2;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_external_port_requested, tvb, offset, 2, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 2;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_rpmlis, tvb, offset, 4, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MAP_UDP_RESPONSE:
|
|
|
|
case MAP_TCP_RESPONSE:
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_result_code, tvb, offset, 2, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 2;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_sssoe, tvb, offset, 4, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 4;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_internal_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 2;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_external_port_mapped, tvb, offset, 2, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 2;
|
|
|
|
|
2011-07-19 10:51:12 +00:00
|
|
|
proto_tree_add_item (nat_pmp_tree, hf_pmlis, tvb, offset, 4, ENC_BIG_ENDIAN);
|
2009-02-25 00:22:47 +00:00
|
|
|
offset += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Unknown OP */
|
|
|
|
expert_add_info_format (pinfo, op_ti, PI_RESPONSE_CODE, PI_WARN, "Unknown opcode: %d", opcode);
|
|
|
|
break;
|
|
|
|
}
|
2013-02-01 01:29:58 +00:00
|
|
|
|
|
|
|
return (offset-start_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dissect_portcontrol_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint8 version)
|
|
|
|
{
|
|
|
|
proto_tree *pcp_tree, *opcode_tree = NULL, *option_tree, *option_sub_tree;
|
|
|
|
proto_item *ti, *opcode_ti, *option_ti, *suboption_ti;
|
|
|
|
gint offset = 0, start_offset, start_opcode_offset, start_option_offset;
|
|
|
|
guint8 opcode, option;
|
|
|
|
guint16 option_length;
|
|
|
|
gboolean is_response;
|
|
|
|
const gchar* op_str;
|
|
|
|
|
|
|
|
if (version == 1)
|
|
|
|
col_set_str (pinfo->cinfo, COL_PROTOCOL, "PCP v1");
|
|
|
|
else
|
|
|
|
col_set_str (pinfo->cinfo, COL_PROTOCOL, "PCP v2");
|
|
|
|
col_clear (pinfo->cinfo, COL_INFO);
|
|
|
|
|
|
|
|
start_offset = offset;
|
|
|
|
ti = proto_tree_add_item (tree, proto_pcp, tvb, offset, -1, ENC_NA);
|
|
|
|
pcp_tree = proto_item_add_subtree (ti, ett_pcp);
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_pcp_version, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
opcode = tvb_get_guint8 (tvb, offset);
|
|
|
|
is_response = opcode & 0x80;
|
|
|
|
op_str = val_to_str (opcode, opcode_vals, "Unknown opcode: %d");
|
|
|
|
proto_item_append_text (ti, ", %s", op_str);
|
|
|
|
opcode_ti = proto_tree_add_item (pcp_tree, hf_pcp_opcode, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
col_add_str (pinfo->cinfo, COL_INFO, op_str);
|
|
|
|
|
|
|
|
if (!is_response)
|
|
|
|
{
|
|
|
|
ti = proto_tree_add_boolean(pcp_tree, hf_request, tvb, offset-1, 1, is_response == FALSE);
|
|
|
|
PROTO_ITEM_SET_HIDDEN(ti);
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_reserved2, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_req_lifetime, tvb, offset, 4, ENC_BIG_ENDIAN);
|
|
|
|
offset+=4;
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_client_ip, tvb, offset, 16, ENC_NA);
|
|
|
|
offset+=16;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ti = proto_tree_add_boolean(pcp_tree, hf_response, tvb, offset-1, 1, is_response == TRUE);
|
|
|
|
PROTO_ITEM_SET_HIDDEN(ti);
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_reserved1, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_pcp_result_code, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_rsp_lifetime, tvb, offset, 4, ENC_BIG_ENDIAN);
|
|
|
|
offset+=4;
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_epoch_time, tvb, offset, 4, ENC_BIG_ENDIAN);
|
|
|
|
offset+=4;
|
|
|
|
|
|
|
|
proto_tree_add_item (pcp_tree, hf_reserved8, tvb, offset, 8, ENC_NA);
|
|
|
|
offset+=8;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_opcode_offset = offset;
|
|
|
|
if (match_strval(opcode, opcode_vals) != NULL)
|
|
|
|
{
|
2013-02-01 02:19:17 +00:00
|
|
|
opcode_ti = proto_tree_add_text(pcp_tree, tvb, offset, 0, "%s", op_str);
|
2013-02-01 01:29:58 +00:00
|
|
|
opcode_tree = proto_item_add_subtree (opcode_ti, ett_opcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(opcode) {
|
|
|
|
|
|
|
|
case ANNOUNCE_REQUEST:
|
|
|
|
case ANNOUNCE_RESPONSE:
|
|
|
|
/* No data */
|
|
|
|
break;
|
|
|
|
case MAP_REQUEST:
|
|
|
|
case MAP_RESPONSE:
|
|
|
|
if (version > 1)
|
|
|
|
{
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_nonce, tvb, offset, 12, ENC_NA);
|
|
|
|
offset+=12;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_reserved1, tvb, offset, 3, ENC_BIG_ENDIAN);
|
|
|
|
offset+=3;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_internal_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
if (opcode == MAP_REQUEST)
|
|
|
|
{
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_req_sug_external_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_req_sug_ext_ip, tvb, offset, 16, ENC_NA);
|
|
|
|
offset+=16;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_rsp_assigned_external_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_map_rsp_assigned_ext_ip, tvb, offset, 16, ENC_NA);
|
|
|
|
offset+=16;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PEER_REQUEST:
|
|
|
|
case PEER_RESPONSE:
|
|
|
|
if (version > 1)
|
|
|
|
{
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_nonce, tvb, offset, 12, ENC_NA);
|
|
|
|
offset+=12;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_reserved1, tvb, offset, 3, ENC_BIG_ENDIAN);
|
|
|
|
offset+=3;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_internal_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
if (opcode == PEER_REQUEST)
|
|
|
|
{
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_req_sug_external_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_req_sug_ext_ip, tvb, offset, 16, ENC_NA);
|
|
|
|
offset+=16;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_rsp_assigned_external_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_rsp_assigned_ext_ip, tvb, offset, 16, ENC_NA);
|
|
|
|
offset+=16;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_remote_peer_port, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_reserved2, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
proto_tree_add_item (opcode_tree, hf_peer_remote_peer_ip, tvb, offset, 16, ENC_NA);
|
|
|
|
offset+=16;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Unknown OP */
|
|
|
|
expert_add_info_format (pinfo, opcode_ti, PI_RESPONSE_CODE, PI_WARN, "Unknown opcode: %d", opcode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now see if there are any options for the supported opcodes */
|
|
|
|
if ((tvb_reported_length_remaining(tvb, offset) > 0) &&
|
|
|
|
(match_strval(opcode, opcode_vals) != NULL))
|
|
|
|
{
|
|
|
|
start_option_offset = offset;
|
|
|
|
option_ti = proto_tree_add_text(opcode_tree, tvb, offset, 0, "Options");
|
|
|
|
option_tree = proto_item_add_subtree (option_ti, ett_option);
|
|
|
|
|
|
|
|
while (tvb_reported_length_remaining(tvb, offset) > 0)
|
|
|
|
{
|
|
|
|
option = tvb_get_guint8(tvb, offset);
|
|
|
|
suboption_ti = proto_tree_add_item (option_tree, hf_option_code, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
option_sub_tree = proto_item_add_subtree (suboption_ti, ett_suboption);
|
|
|
|
offset++;
|
|
|
|
proto_tree_add_item (option_sub_tree, hf_option_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
option_length = tvb_get_ntohs(tvb, offset);
|
|
|
|
proto_tree_add_item (option_sub_tree, hf_option_length, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
|
|
offset+=2;
|
|
|
|
proto_item_set_len(suboption_ti, option_length+4);
|
|
|
|
|
|
|
|
if (option_length > 0)
|
|
|
|
{
|
|
|
|
switch(option) {
|
|
|
|
|
|
|
|
case OPT_THIRD_PARTY:
|
|
|
|
proto_tree_add_item (option_sub_tree, hf_option_third_party_internal_ip, tvb, offset, 16, ENC_NA);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_PREFER_FAILURE:
|
|
|
|
/* No data */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_FILTER:
|
|
|
|
proto_tree_add_item (option_sub_tree, hf_option_filter_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
proto_tree_add_item (option_sub_tree, hf_option_filter_prefix_length, tvb, offset+1, 1, ENC_BIG_ENDIAN);
|
|
|
|
proto_tree_add_item (option_sub_tree, hf_option_filter_remote_peer_port, tvb, offset+2, 2, ENC_BIG_ENDIAN);
|
|
|
|
proto_tree_add_item (option_sub_tree, hf_option_filter_remote_peer_ip, tvb, offset+4, 16, ENC_BIG_ENDIAN);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Unknown option */
|
|
|
|
expert_add_info_format (pinfo, option_ti, PI_RESPONSE_CODE, PI_WARN, "Unknown option: %d", option);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
offset+=option_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_item_set_len(option_ti, offset-start_option_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_item_set_len(opcode_ti, offset-start_opcode_offset);
|
|
|
|
|
|
|
|
return (offset-start_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dissect_portcontrol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
|
|
|
|
{
|
|
|
|
guint8 version = tvb_get_guint8 (tvb, 0);
|
|
|
|
|
|
|
|
switch(version)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
/* NAT-PMP protocol */
|
|
|
|
return dissect_nat_pmp(tvb, pinfo, tree, data);
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
return dissect_portcontrol_pdu(tvb, pinfo, tree, version);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2009-02-25 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
2009-02-25 00:36:31 +00:00
|
|
|
void proto_register_nat_pmp (void)
|
2009-02-25 00:22:47 +00:00
|
|
|
{
|
|
|
|
static hf_register_info hf[] = {
|
|
|
|
{ &hf_version,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Version", "nat-pmp.version", FT_UINT8, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_opcode,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Opcode", "nat-pmp.opcode", FT_UINT8, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
VALS(opcode_vals), 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_result_code,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Result Code", "nat-pmp.result_code", FT_UINT16, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
VALS(result_vals), 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_sssoe,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Seconds Since Start of Epoch", "nat-pmp.sssoe", FT_UINT32, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_external_ip,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "External IP Address", "nat-pmp.external_ip", FT_IPv4, BASE_NONE,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_reserved,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Reserved", "nat-pmp.reserved", FT_UINT16, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, "Reserved (must be zero)", HFILL } },
|
|
|
|
{ &hf_internal_port,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Internal Port", "nat-pmp.internal_port", FT_UINT16, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_external_port_requested,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Requested External Port", "nat-pmp.external_port", FT_UINT16, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_external_port_mapped,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Mapped External Port", "nat-pmp.external_port", FT_UINT16, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_rpmlis,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Requested Port Mapping Lifetime", "nat-pmp.pml", FT_UINT32, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, "Requested Port Mapping Lifetime in Seconds", HFILL } },
|
|
|
|
{ &hf_pmlis,
|
2009-02-25 00:36:31 +00:00
|
|
|
{ "Port Mapping Lifetime", "nat-pmp.pml", FT_UINT32, BASE_DEC,
|
2009-02-25 00:22:47 +00:00
|
|
|
NULL, 0x0, "Port Mapping Lifetime in Seconds", HFILL } },
|
|
|
|
};
|
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
static hf_register_info pcp_hf[] = {
|
|
|
|
{ &hf_pcp_version,
|
|
|
|
{ "Version", "portcontrol.version", FT_UINT8, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_request,
|
|
|
|
{ "Request", "portcontrol.request", FT_BOOLEAN, 8,
|
|
|
|
NULL, 0x01, NULL, HFILL } },
|
|
|
|
{ &hf_response,
|
|
|
|
{ "Response", "portcontrol.response", FT_BOOLEAN, 8,
|
|
|
|
NULL, 0x01, NULL, HFILL } },
|
|
|
|
{ &hf_pcp_opcode,
|
|
|
|
{ "Opcode", "portcontrol.opcode", FT_UINT8, BASE_DEC,
|
|
|
|
VALS(pcp_opcode_vals), 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_pcp_result_code,
|
|
|
|
{ "Result Code", "portcontrol.result_code", FT_UINT16, BASE_DEC,
|
|
|
|
VALS(pcp_result_vals), 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_reserved1,
|
|
|
|
{ "Reserved", "portcontrol.reserved", FT_UINT8, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_reserved2,
|
|
|
|
{ "Reserved", "portcontrol.reserved", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_reserved8,
|
|
|
|
{ "Reserved", "portcontrol.rsp_reserved", FT_BYTES, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_req_lifetime,
|
|
|
|
{ "Requested Lifetime", "portcontrol.lifetime_req", FT_UINT32, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_rsp_lifetime,
|
|
|
|
{ "Lifetime", "portcontrol.lifetime_rsp", FT_UINT32, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_client_ip,
|
|
|
|
{ "Client IP Address", "portcontrol.client_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_epoch_time,
|
|
|
|
{ "Epoch Time", "portcontrol.epoch_time", FT_UINT32, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_nonce,
|
|
|
|
{ "Mapping Nonce", "portcontrol.map.nonce", FT_BYTES, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_protocol,
|
|
|
|
{ "Protocol", "portcontrol.map.protocol", FT_UINT8, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_reserved1,
|
|
|
|
{ "Reserved", "portcontrol.map.reserved", FT_UINT24, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_internal_port,
|
|
|
|
{ "Internal Port", "portcontrol.map.internal_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_req_sug_external_port,
|
|
|
|
{ "Suggested External Port", "portcontrol.map.req_sug_external_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_req_sug_ext_ip,
|
|
|
|
{ "Suggested External IP Address", "portcontrol.map.req_sug_external_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_rsp_assigned_external_port,
|
|
|
|
{ "Assigned External Port", "portcontrol.map.rsp_assigned_external_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_map_rsp_assigned_ext_ip,
|
|
|
|
{ "Assigned External IP Address", "portcontrol.map.rsp_assigned_ext_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_nonce,
|
|
|
|
{ "Mapping Nonce", "portcontrol.peer.nonce", FT_BYTES, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_protocol,
|
|
|
|
{ "Protocol", "portcontrol.peer.protocol", FT_UINT8, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_reserved1,
|
|
|
|
{ "Reserved", "portcontrol.peer.reserved", FT_UINT24, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_internal_port,
|
|
|
|
{ "Internal Port", "portcontrol.peer.internal_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_req_sug_external_port,
|
|
|
|
{ "Suggested External Port", "portcontrol.peer.req_sug_external_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_req_sug_ext_ip,
|
|
|
|
{ "Suggested External IP Address", "portcontrol.peer.req_sug_external_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_remote_peer_port,
|
|
|
|
{ "Remote Peer Port", "portcontrol.peer.remote_peer_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_reserved2,
|
|
|
|
{ "Reserved", "portcontrol.peer.reserved", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_remote_peer_ip,
|
|
|
|
{ "Remote Peer IP Address", "portcontrol.peer.remote_peer_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_rsp_assigned_external_port,
|
|
|
|
{ "Assigned External Port", "portcontrol.peer.rsp_assigned_external_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_peer_rsp_assigned_ext_ip,
|
|
|
|
{ "Assigned External IP Address", "portcontrol.peer.rsp_assigned_ext_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_code,
|
|
|
|
{ "Option", "portcontrol.option", FT_UINT8, BASE_DEC,
|
|
|
|
VALS(pcp_option_vals), 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_reserved,
|
|
|
|
{ "Reserved", "portcontrol.option.reserved", FT_UINT8, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_length,
|
|
|
|
{ "Option Length", "portcontrol.option.length", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_third_party_internal_ip,
|
|
|
|
{ "Internal IP Address", "portcontrol.option.third_party.internal_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_filter_reserved,
|
|
|
|
{ "Reserved", "portcontrol.option.filter.reserved", FT_UINT8, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_filter_prefix_length,
|
|
|
|
{ "Prefix Length", "portcontrol.option.filter.prefix_length", FT_UINT8, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_filter_remote_peer_port,
|
|
|
|
{ "Remote Peer Port", "portcontrol.option.filter.remote_peer_port", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
{ &hf_option_filter_remote_peer_ip,
|
|
|
|
{ "Remote Peer IP Address", "portcontrol.option.filter.remote_peer_ip", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0x0, NULL, HFILL } },
|
|
|
|
};
|
|
|
|
|
|
|
|
static gint *pcp_ett[] = {
|
|
|
|
&ett_pcp,
|
|
|
|
&ett_opcode,
|
|
|
|
&ett_option,
|
|
|
|
&ett_suboption
|
|
|
|
};
|
|
|
|
|
2009-02-25 00:22:47 +00:00
|
|
|
static gint *ett[] = {
|
2009-02-25 00:36:31 +00:00
|
|
|
&ett_nat_pmp,
|
2009-02-25 00:22:47 +00:00
|
|
|
};
|
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
proto_nat_pmp = proto_register_protocol ("NAT Port Mapping Protocol", "NAT-PMP", "nat-pmp");
|
2009-02-25 00:22:47 +00:00
|
|
|
|
2009-02-25 00:36:31 +00:00
|
|
|
proto_register_field_array (proto_nat_pmp, hf, array_length (hf));
|
2009-02-25 00:22:47 +00:00
|
|
|
proto_register_subtree_array (ett, array_length (ett));
|
2013-02-01 01:29:58 +00:00
|
|
|
|
|
|
|
proto_pcp = proto_register_protocol ("Port Control Protocol", "Port Control", "portcontrol");
|
|
|
|
|
|
|
|
proto_register_field_array (proto_pcp, pcp_hf, array_length (pcp_hf));
|
|
|
|
proto_register_subtree_array (pcp_ett, array_length (pcp_ett));
|
|
|
|
|
2009-02-25 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
2009-02-25 00:36:31 +00:00
|
|
|
void proto_reg_handoff_nat_pmp (void)
|
2009-02-25 00:22:47 +00:00
|
|
|
{
|
2009-03-03 22:02:17 +00:00
|
|
|
dissector_handle_t nat_pmp_handle;
|
2013-02-01 01:29:58 +00:00
|
|
|
dissector_handle_t pcp_handle;
|
|
|
|
|
|
|
|
|
|
|
|
pcp_handle = new_create_dissector_handle(dissect_portcontrol, proto_pcp);
|
|
|
|
dissector_add_uint ("udp.port", PCP_STATUS_PORT, pcp_handle);
|
|
|
|
dissector_add_uint ("udp.port", PCP_PORT, pcp_handle);
|
2009-03-03 22:02:17 +00:00
|
|
|
|
2013-02-01 01:29:58 +00:00
|
|
|
nat_pmp_handle = new_create_dissector_handle(dissect_nat_pmp, proto_nat_pmp);
|
|
|
|
/* Port Control Protocol (packet-portcontrol.c) shares the same UDP ports as
|
|
|
|
NAT-PMP, but it backwards compatible. However, still let NAT-PMP
|
|
|
|
use Decode As
|
|
|
|
*/
|
|
|
|
dissector_add_handle("udp.port", nat_pmp_handle);
|
2009-02-25 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Editor modelines
|
|
|
|
*
|
|
|
|
* Local Variables:
|
|
|
|
* c-basic-offset: 2
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
2011-09-21 17:49:11 +00:00
|
|
|
* ex: set shiftwidth=2 tabstop=8 expandtab:
|
2009-07-23 17:46:09 +00:00
|
|
|
* :indentSize=2:tabSize=8:noTabs=true:
|
2009-02-25 00:22:47 +00:00
|
|
|
*/
|