2 * Routines for Multiprotocol Label Switching Echo dissection
3 * Copyright 2004, Carlos Pignataro <cpignata@cisco.com>
5 * $Id: packet-mpls-echo.c,v 1.1 2004/05/13 20:20:34 gerald Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #include <epan/packet.h>
36 #include "packet-ntp.h"
37 #include "packet-ldp.h"
39 #define UDP_PORT_MPLS_ECHO 3503
41 void proto_reg_handoff_mpls_echo(void);
43 static int proto_mpls_echo = -1;
44 static int hf_mpls_echo_version = -1;
45 static int hf_mpls_echo_mbz = -1;
46 static int hf_mpls_echo_msgtype = -1;
47 static int hf_mpls_echo_replymode = -1;
48 static int hf_mpls_echo_returncode = -1;
49 static int hf_mpls_echo_returnsubcode = -1;
50 static int hf_mpls_echo_handle = -1;
51 static int hf_mpls_echo_sequence = -1;
52 static int hf_mpls_echo_ts_sent = -1;
53 static int hf_mpls_echo_ts_rec = -1;
54 static int hf_mpls_echo_tlv_type = -1;
55 static int hf_mpls_echo_tlv_len = -1;
56 static int hf_mpls_echo_tlv_value = -1;
57 static int hf_mpls_echo_tlv_fec_type = -1;
58 static int hf_mpls_echo_tlv_fec_len = -1;
59 static int hf_mpls_echo_tlv_fec_value = -1;
60 static int hf_mpls_echo_tlv_fec_ldp_ipv4 = -1;
61 static int hf_mpls_echo_tlv_fec_ldp_ipv4_mask = -1;
62 static int hf_mpls_echo_tlv_fec_l2cid_sender = -1;
63 static int hf_mpls_echo_tlv_fec_l2cid_remote = -1;
64 static int hf_mpls_echo_tlv_fec_l2cid_vcid = -1;
65 static int hf_mpls_echo_tlv_fec_l2cid_encap = -1;
66 static int hf_mpls_echo_tlv_fec_l2cid_mbz = -1;
67 static int hf_mpls_echo_tlv_padaction = -1;
68 static int hf_mpls_echo_tlv_padding = -1;
70 static gint ett_mpls_echo = -1;
71 static gint ett_mpls_echo_tlv = -1;
72 static gint ett_mpls_echo_tlv_fec = -1;
74 static int mpls_echo_udp_port = 0;
76 static guint32 global_mpls_echo_udp_port = UDP_PORT_MPLS_ECHO;
78 static const value_string mpls_echo_msgtype[] = {
79 {1, "MPLS Echo Request"},
80 {2, "MPLS Echo Reply"},
84 static const value_string mpls_echo_replymode[] = {
86 {2, "Reply via an IPv4/IPv6 UDP packet"},
87 {3, "Reply via an IPv4/IPv6 UDP packet with Router Alert"},
88 {4, "Reply via application level control channel"},
92 static const value_string mpls_echo_returncode[] = {
93 {0, "No return code"},
94 {1, "Malformed echo request received"},
95 {2, "One or more of the TLVs was not understood"},
96 {3, "Replying router is an egress for the FEC at stack depth RSC"},
97 {4, "Replying router has no mapping for the FEC at stack depth RSC"},
101 {8, "Label switched at stack-depth RSC"},
102 {9, "Label switched but no MPLS forwarding at stack-depth RSC"},
103 {10, "Mapping for this FEC is not the given label at stack depth RSC"},
104 {11, "No label entry at stack-depth RSC"},
105 {12, "Protocol not associated with interface at FEC stack depth RSC"},
109 #define TLV_TARGET_FEC_STACK 0x0001
110 #define TLV_DOWNSTREAM_MAPPING 0x0002
111 #define TLV_PAD 0x0003
112 #define TLV_ERROR_CODE 0x0004
113 #define TLV_VENDOR_CODE 0x0005
115 /* MPLS Echo TLV Type names */
116 static const value_string mpls_echo_tlv_type_names[] = {
117 { TLV_TARGET_FEC_STACK, "Target FEC Stack" },
118 { TLV_DOWNSTREAM_MAPPING, "Downstream Mapping" },
120 { TLV_ERROR_CODE, "Error Code" },
121 { TLV_VENDOR_CODE, "Vendor Enterprise Code" },
125 #define TLV_FEC_STACK_LDP_IPv4 1
126 #define TLV_FEC_STACK_LDP_IPv6 2
127 #define TLV_FEC_STACK_RSVP_IPv4 3
128 #define TLV_FEC_STACK_RSVP_IPv6 4
129 #define TLV_FEC_STACK_RES 5
130 #define TLV_FEC_STACK_VPN_IPv4 6
131 #define TLV_FEC_STACK_VPN_IPv6 7
132 #define TLV_FEC_STACK_L2_VPN 8
133 #define TLV_FEC_STACK_L2_CID 9
135 /* FEC sub-TLV Type names */
136 static const value_string mpls_echo_tlv_fec_names[] = {
137 { TLV_FEC_STACK_LDP_IPv4, "LDP IPv4 prefix"},
138 { TLV_FEC_STACK_LDP_IPv6, "LDP IPv6 prefix"},
139 { TLV_FEC_STACK_RSVP_IPv4, "RSVP IPv4 Session Query"},
140 { TLV_FEC_STACK_RSVP_IPv6, "RSVP IPv6 Session Query"},
141 { TLV_FEC_STACK_RES, "Reserved"},
142 { TLV_FEC_STACK_VPN_IPv4, "VPN IPv4 prefix"},
143 { TLV_FEC_STACK_VPN_IPv6, "VPN IPv6 prefix"},
144 { TLV_FEC_STACK_L2_VPN, "L2 VPN endpoint"},
145 { TLV_FEC_STACK_L2_CID, "L2 cirtuit ID"},
149 static const value_string mpls_echo_tlv_pad[] = {
150 { 1, "Drop Pad TLV from reply" },
151 { 2, "Copy Pad TLV to reply" },
156 * Dissector for FEC sub-TLVs
159 dissect_mpls_echo_tlv_fec(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
161 proto_tree *ti = NULL, *tlv_fec_tree = NULL;
162 guint16 index = 1, type;
166 while (rem >= 4){ /* Type, Length */
167 type = tvb_get_ntohs(tvb, offset);
168 length = tvb_get_ntohs(tvb, offset + 2);
169 ti = proto_tree_add_text(tree, tvb, offset, length + 4, "FEC Element %u: %s",
170 index, val_to_str(type, mpls_echo_tlv_fec_names,
171 "Unknown FEC type (0x%04X)"));
172 tlv_fec_tree = proto_item_add_subtree(ti, ett_mpls_echo_tlv_fec);
173 if(tlv_fec_tree == NULL) return;
175 /* FEC sub-TLV Type and Length */
176 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_type, tvb, offset,
178 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_len, tvb, offset + 2,
181 /* FEC sub-TLV Value */
183 case TLV_FEC_STACK_LDP_IPv4:
184 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_ldp_ipv4,
185 tvb, offset + 4, 4, FALSE);
186 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_ldp_ipv4_mask,
187 tvb, offset + 8, 1, FALSE);
189 proto_tree_add_text(tlv_fec_tree, tvb, offset + 6, 3, "Padding");
191 case TLV_FEC_STACK_L2_CID:
194 proto_tree_add_text(tree, tvb, offset, rem,
195 "Error processing sub-TLV: length is %d, should be 16", length);
198 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_l2cid_sender,
199 tvb, offset + 4, 4, FALSE);
200 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_l2cid_remote,
201 tvb, offset + 8, 4, FALSE);
202 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_l2cid_vcid,
203 tvb, offset + 12, 4, FALSE);
204 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_l2cid_encap,
205 tvb, offset + 16, 2, FALSE);
206 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_l2cid_mbz,
207 tvb, offset + 18, 2, FALSE);
209 case TLV_FEC_STACK_LDP_IPv6:
210 case TLV_FEC_STACK_RSVP_IPv4:
211 case TLV_FEC_STACK_RSVP_IPv6:
212 case TLV_FEC_STACK_RES:
213 case TLV_FEC_STACK_VPN_IPv4:
214 case TLV_FEC_STACK_VPN_IPv6:
215 case TLV_FEC_STACK_L2_VPN:
217 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_value, tvb, offset + 4,
222 offset += 4 + length;
229 * Dissector for MPLS Echo TLVs and return bytes consumed
232 dissect_mpls_echo_tlv(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
236 proto_tree *ti = NULL, *mpls_echo_tlv_tree = NULL;
238 length = tvb_reported_length_remaining(tvb, offset);
239 rem = MIN(rem, length);
241 if( rem < 4 ) { /* Type Length */
243 proto_tree_add_text(tree, tvb, offset, rem,
244 "Error processing TLV: length is %d, should be >= 4",
248 type = tvb_get_ntohs(tvb, offset);
249 length = tvb_get_ntohs(tvb, offset + 2),
250 rem -= 4; /* do not count Type Length */
251 length = MIN(length, rem);
254 ti = proto_tree_add_text(tree, tvb, offset, length + 4, "%s",
255 val_to_str(type, mpls_echo_tlv_type_names, "Unknown TLV type (0x%04X)"));
256 mpls_echo_tlv_tree = proto_item_add_subtree(ti, ett_mpls_echo_tlv);
257 if(mpls_echo_tlv_tree == NULL) return length+4;
259 /* MPLS Echo TLV Type and Length */
260 proto_tree_add_uint_format(mpls_echo_tlv_tree, hf_mpls_echo_tlv_type, tvb,
261 offset, 2, type, "Type: %s (%u)",
262 val_to_str(type, mpls_echo_tlv_type_names, "Unknown TLV type"), type );
263 proto_tree_add_item(mpls_echo_tlv_tree, hf_mpls_echo_tlv_len, tvb, offset + 2, 2, FALSE);
265 /* MPLS Echo TLV Value */
267 case TLV_TARGET_FEC_STACK:
268 dissect_mpls_echo_tlv_fec(tvb, offset + 4, mpls_echo_tlv_tree, length);
271 proto_tree_add_item(mpls_echo_tlv_tree, hf_mpls_echo_tlv_padaction, tvb,
272 offset + 4, 1, FALSE);
273 proto_tree_add_item(mpls_echo_tlv_tree, hf_mpls_echo_tlv_padding, tvb,
274 offset + 5, length - 1, FALSE);
276 case TLV_DOWNSTREAM_MAPPING:
278 case TLV_VENDOR_CODE:
280 proto_tree_add_item(mpls_echo_tlv_tree, hf_mpls_echo_tlv_value, tvb,
281 offset + 4, length, FALSE);
285 return length + 4; /* Length of the Value field + Type Length */
289 * Dissector for MPLS Echo (LSP PING) packets
292 dissect_mpls_echo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
294 int offset = 0, rem = 0, len;
295 proto_item *ti = NULL;
296 proto_tree *mpls_echo_tree = NULL;
298 const guint8 *ts_sent, *ts_rec;
299 gchar buff[NTP_TS_SIZE];
301 /* If version != 1 we assume it's not an mpls ping packet */
302 if (!tvb_bytes_exist(tvb, 0, 2)) {
303 return; /* Not enough information to tell. */
305 if (tvb_get_ntohs(tvb, 0) != 1) {
306 return; /* Not version 1. */
309 if (check_col(pinfo->cinfo, COL_PROTOCOL))
310 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MPLS ECHO");
312 rem = tvb_reported_length_remaining(tvb, offset);
314 if( rem < 32 ) { /* The fixed part of the packet is 32 Bytes */
315 if( check_col(pinfo->cinfo, COL_INFO) )
316 col_set_str(pinfo->cinfo, COL_INFO, "Malformed Message");
318 proto_tree_add_text(tree, tvb, offset, rem,
319 "Error processing Message: length is %d, should be >= 32",
324 /* Get the message type and fill in the Column info */
325 msgtype = tvb_get_guint8(tvb, offset + 4);
326 if (check_col(pinfo->cinfo, COL_INFO))
327 col_set_str(pinfo->cinfo, COL_INFO,
328 val_to_str(msgtype, mpls_echo_msgtype, "Unknown Message Type (0x%02X)"));
333 /* Add subtree and dissect the fixed part of the message */
334 ti = proto_tree_add_item(tree, proto_mpls_echo, tvb, 0, -1, FALSE);
335 mpls_echo_tree = proto_item_add_subtree(ti, ett_mpls_echo);
337 proto_tree_add_item(mpls_echo_tree,
338 hf_mpls_echo_version, tvb, offset, 2, FALSE);
339 proto_tree_add_item(mpls_echo_tree,
340 hf_mpls_echo_mbz, tvb, offset + 2, 2, FALSE);
341 proto_tree_add_item(mpls_echo_tree,
342 hf_mpls_echo_msgtype, tvb, offset + 4, 1, FALSE);
343 proto_tree_add_item(mpls_echo_tree,
344 hf_mpls_echo_replymode, tvb, offset + 5, 1, FALSE);
345 proto_tree_add_item(mpls_echo_tree,
346 hf_mpls_echo_returncode, tvb, offset + 6, 1, FALSE);
347 proto_tree_add_item(mpls_echo_tree,
348 hf_mpls_echo_returnsubcode, tvb, offset + 7, 1, FALSE);
349 proto_tree_add_item(mpls_echo_tree,
350 hf_mpls_echo_handle, tvb, offset + 8, 4, FALSE);
351 proto_tree_add_item(mpls_echo_tree,
352 hf_mpls_echo_sequence, tvb, offset + 12, 4, FALSE);
354 /* Using NTP routine to calculate the timestamp */
355 ts_sent = tvb_get_ptr(tvb, 16, 8);
356 proto_tree_add_bytes_format(mpls_echo_tree, hf_mpls_echo_ts_sent, tvb,
357 offset + 16, 8, ts_sent, "Timestamp Sent: %s", ntp_fmt_ts(ts_sent, buff));
358 ts_rec = tvb_get_ptr(tvb, 24, 8);
359 proto_tree_add_bytes_format(mpls_echo_tree, hf_mpls_echo_ts_rec, tvb,
360 offset + 24, 8, ts_rec, "Timestamp Received: %s", ntp_fmt_ts(ts_rec, buff));
367 /* Dissect all TLVs */
368 while(tvb_reported_length_remaining(tvb, offset) > 0 ) {
369 len = dissect_mpls_echo_tlv(tvb, offset, mpls_echo_tree, rem);
377 /* Register the protocol with Ethereal */
380 proto_register_mpls_echo(void)
383 static hf_register_info hf[] = {
384 { &hf_mpls_echo_version,
385 { "Version", "mpls_echo.version",
386 FT_UINT16, BASE_DEC, NULL, 0x0, "MPLS ECHO Version Number", HFILL}
389 { "MBZ", "mpls_echo.mbz",
390 FT_UINT16, BASE_DEC, NULL, 0x0, "MPLS ECHO Must Be Zero", HFILL}
392 { &hf_mpls_echo_msgtype,
393 { "Message Type", "mpls_echo.msg_type",
394 FT_UINT8, BASE_DEC, VALS(mpls_echo_msgtype), 0x0, "MPLS ECHO Message Type", HFILL}
396 { &hf_mpls_echo_replymode,
397 { "Reply Mode", "mpls_echo.reply_mode",
398 FT_UINT8, BASE_DEC, VALS(mpls_echo_replymode), 0x0, "MPLS ECHO Reply Mode", HFILL}
400 { &hf_mpls_echo_returncode,
401 { "Return Code", "mpls_echo.return_code",
402 FT_UINT8, BASE_DEC, VALS(mpls_echo_returncode), 0x0, "MPLS ECHO Return Code", HFILL}
404 { &hf_mpls_echo_returnsubcode,
405 { "Return Subcode", "mpls_echo.return_subcode",
406 FT_UINT8, BASE_DEC, NULL, 0x0, "MPLS ECHO Return Subcode", HFILL}
408 { &hf_mpls_echo_handle,
409 { "Sender's Handle", "mpls_echo.sender_handle",
410 FT_UINT32, BASE_HEX, NULL, 0x0, "MPLS ECHO Sender's Handle", HFILL}
412 { &hf_mpls_echo_sequence,
413 { "Sequence Number", "mpls_echo.sequence",
414 FT_UINT32, BASE_DEC, NULL, 0x0, "MPLS ECHO Sequence Number", HFILL}
416 { &hf_mpls_echo_ts_sent,
417 { "Timestamp Sent", "mpls_echo.timestamp_sent",
418 FT_BYTES, BASE_NONE, NULL, 0x0, "MPLS ECHO Timestamp Sent", HFILL}
420 { &hf_mpls_echo_ts_rec,
421 { "Timestamp Received", "mpls_echo.timestamp_rec",
422 FT_BYTES, BASE_NONE, NULL, 0x0, "MPLS ECHO Timestamp Received", HFILL}
424 { &hf_mpls_echo_tlv_type,
425 { "Type", "mpls_echo.tlv.type",
426 FT_UINT16, BASE_DEC, VALS(mpls_echo_tlv_type_names), 0x0,
427 "MPLS ECHO TLV Type", HFILL}
429 { &hf_mpls_echo_tlv_len,
430 { "Length", "mpls_echo.tlv.len",
431 FT_UINT16, BASE_DEC, NULL, 0x0, "MPLS ECHO TLV Length", HFILL}
433 { &hf_mpls_echo_tlv_value,
434 { "Value", "mpls_echo.tlv.value",
435 FT_BYTES, BASE_NONE, NULL, 0x0, "MPLS ECHO TLV Value", HFILL}
437 { &hf_mpls_echo_tlv_fec_type,
438 { "Type", "mpls_echo.tlv.fec.type",
439 FT_UINT16, BASE_DEC, VALS(mpls_echo_tlv_fec_names), 0x0,
440 "MPLS ECHO TLV FEC Stack Type", HFILL}
442 { &hf_mpls_echo_tlv_fec_len,
443 { "Length", "mpls_echo.tlv.fec.len",
444 FT_UINT16, BASE_DEC, NULL, 0x0, "MPLS ECHO TLV FEC Stack Length", HFILL}
446 { &hf_mpls_echo_tlv_fec_value,
447 { "Value", "mpls_echo.tlv.fec.value",
448 FT_BYTES, BASE_NONE, NULL, 0x0, "MPLS ECHO TLV FEC Stack Value", HFILL}
450 { &hf_mpls_echo_tlv_fec_ldp_ipv4,
451 { "IPv4 Prefix", "mpls_echo.tlv.fec.ldp_ipv4",
452 FT_IPv4, BASE_NONE, NULL, 0x0, "MPLS ECHO TLV FEC Stack IPv4", HFILL}
454 { &hf_mpls_echo_tlv_fec_ldp_ipv4_mask,
455 { "Prefix Length", "mpls_echo.tlv.fec.ldp_ipv4_mask",
456 FT_UINT8, BASE_DEC, NULL, 0x0, "MPLS ECHO TLV FEC Stack IPv4 Prefix Length", HFILL}
458 { &hf_mpls_echo_tlv_fec_l2cid_sender,
459 { "Sender's PE Address", "mpls_echo.tlv.fec.l2cid_sender",
460 FT_IPv4, BASE_NONE, NULL, 0x0, "MPLS ECHO TLV FEC Stack L2CID Sender", HFILL}
462 { &hf_mpls_echo_tlv_fec_l2cid_remote,
463 { "Remote PE Address", "mpls_echo.tlv.fec.l2cid_remote",
464 FT_IPv4, BASE_NONE, NULL, 0x0, "MPLS ECHO TLV FEC Stack L2CID Remote", HFILL}
466 { &hf_mpls_echo_tlv_fec_l2cid_vcid,
467 { "VC ID", "mpls_echo.tlv.fec.l2cid_vcid",
468 FT_UINT32, BASE_DEC, NULL, 0x0, "MPLS ECHO TLV FEC Stack L2CID VCID", HFILL}
470 { &hf_mpls_echo_tlv_fec_l2cid_encap,
471 { "Encapsulation", "mpls_echo.tlv.fec.l2cid_encap",
472 FT_UINT16, BASE_DEC, VALS(fec_vc_types_vals), 0x0, "MPLS ECHO TLV FEC Stack L2CID Encapsulation", HFILL}
474 { &hf_mpls_echo_tlv_fec_l2cid_mbz,
475 { "MBZ", "mpls_echo.tlv.fec.l2cid_mbz",
476 FT_UINT16, BASE_HEX, NULL, 0x0, "MPLS ECHO TLV FEC Stack L2CID MBZ", HFILL}
478 { &hf_mpls_echo_tlv_padaction,
479 { "Pad Action", "mpls_echo.tlv.pad_action",
480 FT_UINT8, BASE_DEC, VALS(mpls_echo_tlv_pad), 0x0, "MPLS ECHO Pad TLV Action", HFILL}
482 { &hf_mpls_echo_tlv_padding,
483 { "Padding", "mpls_echo.tlv.pad_padding",
484 FT_BYTES, BASE_NONE, NULL, 0x0, "MPLS ECHO Pad TLV Padding", HFILL}
488 static gint *ett[] = {
491 &ett_mpls_echo_tlv_fec,
494 module_t *mpls_echo_module;
496 proto_mpls_echo = proto_register_protocol("Multiprotocol Label Switching Echo",
497 "MPLS Echo", "mpls-echo");
499 proto_register_field_array(proto_mpls_echo, hf, array_length(hf));
500 proto_register_subtree_array(ett, array_length(ett));
502 mpls_echo_module = prefs_register_protocol(proto_mpls_echo, proto_reg_handoff_mpls_echo);
503 prefs_register_uint_preference(mpls_echo_module, "udp.port", "MPLS Echo UDP Port",
504 "Set the UDP port for messages (if other"
505 " than the default of 3503)",
506 10, &global_mpls_echo_udp_port);
511 proto_reg_handoff_mpls_echo(void)
513 static gboolean mpls_echo_prefs_initialized = FALSE;
514 static dissector_handle_t mpls_echo_handle;
516 if(!mpls_echo_prefs_initialized) {
517 mpls_echo_handle = create_dissector_handle(dissect_mpls_echo,
519 mpls_echo_prefs_initialized = TRUE;
521 dissector_delete("udp.port", mpls_echo_udp_port, mpls_echo_handle);
524 mpls_echo_udp_port = global_mpls_echo_udp_port;
525 dissector_add("udp.port", global_mpls_echo_udp_port, mpls_echo_handle);