Set the svn:eol-style property on all text files to "native", so that
[obnox/wireshark/wip.git] / packet-mpls-echo.c
1 /* packet-mpls-echo.c
2  * Routines for Multiprotocol Label Switching Echo dissection
3  * Copyright 2004, Carlos Pignataro <cpignata@cisco.com>
4  *
5  * $Id$
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  * 
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.
20  * 
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <glib.h>
34 #include <epan/packet.h>
35 #include "prefs.h"
36 #include "packet-ntp.h"
37 #include "packet-ldp.h"
38
39 #define UDP_PORT_MPLS_ECHO 3503
40
41 void proto_reg_handoff_mpls_echo(void);
42
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;
69
70 static gint ett_mpls_echo = -1;
71 static gint ett_mpls_echo_tlv = -1;
72 static gint ett_mpls_echo_tlv_fec = -1;
73
74 static int mpls_echo_udp_port = 0;
75
76 static guint32 global_mpls_echo_udp_port = UDP_PORT_MPLS_ECHO;
77
78 static const value_string mpls_echo_msgtype[] = {
79   {1, "MPLS Echo Request"},
80   {2, "MPLS Echo Reply"},
81   {0, NULL}
82 };
83
84 static const value_string mpls_echo_replymode[] = {
85   {1, "Do not reply"},
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"},
89   {0, NULL}
90 };
91
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"},
98   {5, "Reserved"},
99   {6, "Reserved"},
100   {7, "Reserved"},
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"},
106   {0, NULL}
107 };
108
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
114
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" },
119   { TLV_PAD,                       "Pad" },
120   { TLV_ERROR_CODE,                "Error Code" },
121   { TLV_VENDOR_CODE,               "Vendor Enterprise Code" },
122   { 0, NULL}
123 };
124
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
134
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"},
146   { 0, NULL}
147 };
148
149 static const value_string mpls_echo_tlv_pad[] = {
150   { 1, "Drop Pad TLV from reply" },
151   { 2, "Copy Pad TLV to reply" },
152   { 0, NULL}
153 };
154
155 /*
156  * Dissector for FEC sub-TLVs
157  */
158 static void
159 dissect_mpls_echo_tlv_fec(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
160 {
161         proto_tree *ti = NULL, *tlv_fec_tree = NULL;
162         guint16 index = 1, type;
163         int length;
164
165         if (tree){
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;
174
175             /* FEC sub-TLV Type and Length */
176             proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_type, tvb, offset,
177                 2, FALSE);
178             proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_len, tvb, offset + 2,
179                 2, FALSE);
180
181             /* FEC sub-TLV Value */
182             switch (type) {
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);
188                 if (length == 9)
189                     proto_tree_add_text(tlv_fec_tree, tvb, offset + 6, 3, "Padding");
190                 break;
191             case TLV_FEC_STACK_L2_CID:
192                 if (length != 16){
193                     if(tree)
194                         proto_tree_add_text(tree, tvb, offset, rem,
195                             "Error processing sub-TLV: length is %d, should be 16", length);
196                     return;
197                 }
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);
208                 break;
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:
216             default:
217                 proto_tree_add_item(tlv_fec_tree, hf_mpls_echo_tlv_fec_value, tvb, offset + 4,
218                     length, FALSE);
219                 break;
220             }
221             rem -= 4 + length;
222             offset += 4 + length;
223             index++;
224           }
225         }
226 }
227
228 /*
229  * Dissector for MPLS Echo TLVs and return bytes consumed
230  */
231 static int
232 dissect_mpls_echo_tlv(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
233 {
234         guint16 type;
235         int length;
236         proto_tree *ti = NULL, *mpls_echo_tlv_tree = NULL;
237
238         length = tvb_reported_length_remaining(tvb, offset);
239         rem = MIN(rem, length);
240
241         if( rem < 4 ) { /* Type Length */
242                 if(tree)
243                     proto_tree_add_text(tree, tvb, offset, rem,
244                         "Error processing TLV: length is %d, should be >= 4",
245                         rem);
246                 return rem;
247         }
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);
252
253         if (tree) {
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;
258
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);
264
265                 /* MPLS Echo TLV Value */
266                 switch (type) {
267                 case TLV_TARGET_FEC_STACK:
268                         dissect_mpls_echo_tlv_fec(tvb, offset + 4, mpls_echo_tlv_tree, length);
269                         break;
270                 case TLV_PAD:
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);
275                         break;
276                 case TLV_DOWNSTREAM_MAPPING:
277                 case TLV_ERROR_CODE:
278                 case TLV_VENDOR_CODE:
279                 default:
280                         proto_tree_add_item(mpls_echo_tlv_tree, hf_mpls_echo_tlv_value, tvb,
281                             offset + 4, length, FALSE);
282                         break;
283                 }
284         }
285         return length + 4;  /* Length of the Value field + Type Length */
286 }
287
288 /*
289  * Dissector for MPLS Echo (LSP PING) packets
290  */
291 static void
292 dissect_mpls_echo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
293 {
294         int offset = 0, rem = 0, len;
295         proto_item *ti = NULL;
296         proto_tree *mpls_echo_tree = NULL;
297         guint8 msgtype;
298         const guint8 *ts_sent, *ts_rec;
299         gchar buff[NTP_TS_SIZE];
300
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. */
304         }
305         if (tvb_get_ntohs(tvb, 0) != 1) {
306                 return; /* Not version 1. */
307         }
308
309         if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
310                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MPLS ECHO");
311     
312         rem = tvb_reported_length_remaining(tvb, offset);
313
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");
317                 if(tree)
318                         proto_tree_add_text(tree, tvb, offset, rem,
319                             "Error processing Message: length is %d, should be >= 32",
320                             rem);
321                 return;
322         }
323
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)"));
329
330
331         if (tree) {
332
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);
336
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);
353
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));
361
362         }
363
364         offset += 32;
365         rem -= 32;
366
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);
370                 offset += len;
371                 rem -= len;
372         }
373
374 }
375
376
377 /* Register the protocol with Ethereal */
378
379 void
380 proto_register_mpls_echo(void)
381 {                 
382
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}
387                 },
388                 { &hf_mpls_echo_mbz,
389                         { "MBZ", "mpls_echo.mbz",
390                         FT_UINT16, BASE_DEC, NULL, 0x0, "MPLS ECHO Must Be Zero", HFILL}
391                 },
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}
395                 },
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}
399                 },
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}
403                 },
404                 { &hf_mpls_echo_returnsubcode,
405                         { "Return Subcode", "mpls_echo.return_subcode",
406                         FT_UINT8, BASE_DEC, NULL, 0x0, "MPLS ECHO Return Subcode", HFILL}
407                 },
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}
411                 },
412                 { &hf_mpls_echo_sequence,
413                         { "Sequence Number", "mpls_echo.sequence",
414                         FT_UINT32, BASE_DEC, NULL, 0x0, "MPLS ECHO Sequence Number", HFILL}
415                 },
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}
419                 },
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}
423                 },
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}
428                 },
429                 { &hf_mpls_echo_tlv_len,
430                         { "Length", "mpls_echo.tlv.len",
431                         FT_UINT16, BASE_DEC, NULL, 0x0, "MPLS ECHO TLV Length", HFILL}
432                 },
433                 { &hf_mpls_echo_tlv_value,
434                         { "Value", "mpls_echo.tlv.value",
435                         FT_BYTES, BASE_NONE, NULL, 0x0, "MPLS ECHO TLV Value", HFILL}
436                 },
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}
441                 },
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}
445                 },
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}
449                 },
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}
453                 },
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}
457                 },
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}
461                 },
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}
465                 },
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}
469                 },
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}
473                 },
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}
477                 },
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}
481                 },
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}
485                 }
486         };
487
488         static gint *ett[] = {
489                 &ett_mpls_echo,
490                 &ett_mpls_echo_tlv,
491                 &ett_mpls_echo_tlv_fec,
492         };
493
494         module_t *mpls_echo_module;
495
496         proto_mpls_echo = proto_register_protocol("Multiprotocol Label Switching Echo",
497             "MPLS Echo", "mpls-echo");
498
499         proto_register_field_array(proto_mpls_echo, hf, array_length(hf));
500         proto_register_subtree_array(ett, array_length(ett));
501
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);
507 }
508
509
510 void
511 proto_reg_handoff_mpls_echo(void)
512 {
513         static gboolean mpls_echo_prefs_initialized = FALSE;
514         static dissector_handle_t mpls_echo_handle;
515
516         if(!mpls_echo_prefs_initialized) {
517             mpls_echo_handle = create_dissector_handle(dissect_mpls_echo,
518                 proto_mpls_echo);
519             mpls_echo_prefs_initialized = TRUE;
520         } else {
521             dissector_delete("udp.port", mpls_echo_udp_port, mpls_echo_handle);
522         }
523
524         mpls_echo_udp_port = global_mpls_echo_udp_port;
525         dissector_add("udp.port", global_mpls_echo_udp_port, mpls_echo_handle);
526 }