For proto_tree_add_item(..., proto_xxx, ...)use ENC_NA as the encoding arg.
[obnox/wireshark/wip.git] / epan / dissectors / packet-erldp.c
1 /* packet-erldp.c
2  * Erlang Distribution Protocol
3  * http://www.erlang.org/doc/apps/erts/erl_dist_protocol.html
4  *
5  * 2010  Tomas Kukosa
6  *
7  * $Id$
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <string.h>
33
34 #include <epan/packet.h>
35 #include <epan/strutil.h>
36 #include <epan/emem.h>
37
38 #include <epan/dissectors/packet-tcp.h>
39 #include <epan/dissectors/packet-epmd.h>
40
41 #define ERL_PASS_THROUGH      'p'
42
43 #define VERSION_MAGIC 131   /* 130 in erlang 4.2 */
44
45 #define SMALL_INTEGER_EXT 'a'
46 #define INTEGER_EXT       'b'
47 #define FLOAT_EXT         'c'
48 #define ATOM_EXT          'd'
49 #define SMALL_ATOM_EXT    's'
50 #define REFERENCE_EXT     'e'
51 #define NEW_REFERENCE_EXT 'r'
52 #define PORT_EXT          'f'
53 #define NEW_FLOAT_EXT     'F'
54 #define PID_EXT           'g'
55 #define SMALL_TUPLE_EXT   'h'
56 #define LARGE_TUPLE_EXT   'i'
57 #define NIL_EXT           'j'
58 #define STRING_EXT        'k'
59 #define LIST_EXT          'l'
60 #define BINARY_EXT        'm'
61 #define BIT_BINARY_EXT    'M'
62 #define SMALL_BIG_EXT     'n'
63 #define LARGE_BIG_EXT     'o'
64 #define NEW_FUN_EXT       'p'
65 #define EXPORT_EXT        'q'
66 #define FUN_EXT           'u'
67
68 #define DIST_HEADER       'D'
69 #define ATOM_CACHE_REF    'R'
70 #define COMPRESSED        'P'
71
72 #define PNAME  "Erlang Distribution Protocol"
73 #define PSNAME "ErlDP"
74 #define PFNAME "erldp"
75
76 static const value_string etf_tag_vals[] = {
77   { SMALL_INTEGER_EXT , "SMALL_INTEGER_EXT" },
78   { INTEGER_EXT       , "INTEGER_EXT" },
79   { FLOAT_EXT         , "FLOAT_EXT" },
80   { ATOM_EXT          , "ATOM_EXT" },
81   { SMALL_ATOM_EXT    , "SMALL_ATOM_EXT" },
82   { REFERENCE_EXT     , "REFERENCE_EXT" },
83   { NEW_REFERENCE_EXT , "NEW_REFERENCE_EXT" },
84   { PORT_EXT          , "PORT_EXT" },
85   { NEW_FLOAT_EXT     , "NEW_FLOAT_EXT" },
86   { PID_EXT           , "PID_EXT" },
87   { SMALL_TUPLE_EXT   , "SMALL_TUPLE_EXT" },
88   { LARGE_TUPLE_EXT   , "LARGE_TUPLE_EXT" },
89   { NIL_EXT           , "NIL_EXT" },
90   { STRING_EXT        , "STRING_EXT" },
91   { LIST_EXT          , "LIST_EXT" },
92   { BINARY_EXT        , "BINARY_EXT" },
93   { BIT_BINARY_EXT    , "BIT_BINARY_EXT" },
94   { SMALL_BIG_EXT     , "SMALL_BIG_EXT" },
95   { LARGE_BIG_EXT     , "LARGE_BIG_EXT" },
96   { NEW_FUN_EXT       , "NEW_FUN_EXT" },
97   { EXPORT_EXT        , "EXPORT_EXT" },
98   { FUN_EXT           , "FUN_EXT" },
99   { DIST_HEADER       , "DIST_HEADER" },
100   { ATOM_CACHE_REF    , "ATOM_CACHE_REF" },
101   { COMPRESSED        , "COMPRESSED" },
102   {  0, NULL }
103 };
104
105 static const value_string erldp_ctlmsg_vals[] = {
106   {  1, "LINK" },
107   {  2, "SEND" },
108   {  3, "EXIT" },
109   {  4, "UNLINK" },
110   {  5, "NODE_LINK" },
111   {  6, "REG_SEND" },
112   {  7, "GROUP_LEADER" },
113   {  8, "EXIT2" },
114   { 12, "SEND_TT" },
115   { 13, "EXIT_TT" },
116   { 16, "REG_SEND_TT" },
117   { 18, "EXIT2_TT" },
118   { 19, "MONITOR_P" },
119   { 20, "DEMONITOR_P" },
120   { 21, "MONITOR_P_EXIT" },
121   {  0, NULL }
122 };
123
124 /* Initialize the protocol and registered fields */
125 int proto_erldp = -1;
126 static int hf_erldp_length_2 = -1;
127 static int hf_erldp_length_4 = -1;
128 static int hf_erldp_tag = -1;
129 static int hf_erldp_type = -1;
130 static int hf_erldp_version = -1;
131 static int hf_erldp_flags = -1;
132 static int hf_erldp_challenge = -1;
133 static int hf_erldp_digest = -1;
134 static int hf_erldp_name = -1;
135 static int hf_erldp_status = -1;
136
137 static int hf_etf_tag = -1;
138
139 /* Initialize the subtree pointers */
140 static gint ett_erldp = -1;
141
142 static gint ett_etf = -1;
143 static gint ett_etf_flags = -1;
144 static gint ett_etf_acrs = -1;
145 static gint ett_etf_acr = -1;
146 static gint ett_etf_tmp = -1;
147
148 /* Preferences */
149 static gboolean erldp_desegment = TRUE;
150
151 /* Dissectors */
152 static dissector_handle_t erldp_handle = NULL;
153
154 /* Subdissectors */
155 dissector_handle_t data_handle;
156
157 /*--- External Term Format ---*/
158
159 static gint dissect_etf_type(const gchar *label, packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree);
160
161 static gint dissect_etf_dist_header(packet_info *pinfo _U_, tvbuff_t *tvb, gint offset, proto_tree *tree) {
162   guint8 num, flen, i, flg, isi;
163   gint flg_offset, acrs_offset, acr_offset;
164   guint32 atom_txt_len;
165   gboolean new_entry, long_atom;
166   proto_item *ti_acrs, *ti_acr, *ti_tmp;
167   proto_tree *flags_tree, *acrs_tree, *acr_tree;
168   const gchar *str;
169
170   num = tvb_get_guint8(tvb, offset);
171   proto_tree_add_text(tree, tvb, offset, 1, "NumberOfAtomCacheRefs: %d", num);
172   offset++;
173
174   if (num == 0)
175     return offset;
176
177   flg_offset = offset;
178   flen = num / 2 + 1;
179   ti_tmp = proto_tree_add_text(tree, tvb, offset, flen, "Flags: %s", tvb_bytes_to_str(tvb, offset, flen));
180   flags_tree = proto_item_add_subtree(ti_tmp, ett_etf_flags);
181   for (i=0; i<num; i++) {
182     flg = tvb_get_guint8(tvb, offset + i / 2);
183     proto_tree_add_text(flags_tree, tvb, offset + i / 2, 1, "%s",
184             decode_boolean_bitfield(flg, 0x08 << 4*(i%2), 8,
185                         ep_strdup_printf("NewCacheEntryFlag[%2d]: SET", i),
186                         ep_strdup_printf("NewCacheEntryFlag[%2d]: ---", i)));
187     proto_tree_add_text(flags_tree, tvb, offset + i / 2, 1, "%s",
188             decode_numeric_bitfield(flg, 0x07 << 4*(i%2), 8, ep_strdup_printf("SegmentIndex     [%2d]: %%u", i)));
189   }
190   flg = tvb_get_guint8(tvb, offset + num / 2);
191   proto_tree_add_text(flags_tree, tvb, offset + num / 2, 1, "%s",
192           decode_boolean_bitfield(flg, 0x01 << 4*(num%2), 8,
193                       "LongAtoms: YES",
194                       "LongAtoms: NO"));
195   long_atom = flg & (0x01 << 4*(num%2));
196   offset += flen;
197
198   acrs_offset = offset;
199   ti_acrs = proto_tree_add_text(tree, tvb, offset, 0, "AtomCacheRefs");
200   acrs_tree = proto_item_add_subtree(ti_acrs, ett_etf_acrs);
201   for (i=0; i<num; i++) {
202     flg = tvb_get_guint8(tvb, flg_offset + i / 2);
203     new_entry = flg & (0x08 << 4*(i%2));
204     acr_offset = offset;
205     ti_acr = proto_tree_add_text(acrs_tree, tvb, offset, 0, "AtomCacheRef[%2d]:", i);
206     acr_tree = proto_item_add_subtree(ti_acr, ett_etf_acr);
207     isi = tvb_get_guint8(tvb, offset);
208     proto_tree_add_text(acr_tree, tvb, offset, 1, "InternalSegmentIndex: %d", isi);
209     proto_item_append_text(ti_acr, " %3d", isi);
210     offset++;
211     if (!new_entry)
212       continue;
213     atom_txt_len = (long_atom) ? tvb_get_ntohs(tvb, offset) : tvb_get_guint8(tvb, offset);
214     proto_tree_add_text(acr_tree, tvb, offset, (long_atom) ? 2 : 1, "Length: %d", atom_txt_len);
215     offset += (long_atom) ? 2 : 1;
216     str = tvb_get_ephemeral_string(tvb, offset, atom_txt_len);
217     proto_tree_add_text(acr_tree, tvb, offset, atom_txt_len, "AtomText: %s", str);
218     proto_item_append_text(ti_acr, " - '%s'", str);
219     offset += atom_txt_len;
220     proto_item_set_len(ti_acr, offset - acr_offset);
221   }
222   proto_item_set_len(ti_acrs, offset - acrs_offset);
223
224   return offset;
225 }
226
227 static gint dissect_etf_tuple_content(gboolean large, packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree, gchar **value_str _U_) {
228   guint32 arity, i;
229
230   arity = (large) ? tvb_get_ntohl(tvb, offset) : tvb_get_guint8(tvb, offset);
231   proto_tree_add_text(tree, tvb, offset, (large) ? 4 : 1, "Arity: %u", arity);
232   offset += (large) ? 4 : 1;
233   for (i=0; i<arity; i++) {
234     offset = dissect_etf_type(NULL, pinfo, tvb, offset, tree);
235   }
236
237   return offset;
238 }
239
240 static gint dissect_etf_type_content(guint8 tag, packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree, gchar **value_str) {
241   gint32 len, int_val, i;
242
243   switch (tag) {
244     case DIST_HEADER:
245       offset = dissect_etf_dist_header(pinfo, tvb, offset, tree);
246       break;
247
248     case ATOM_CACHE_REF:
249       int_val = tvb_get_guint8(tvb, offset);
250       proto_tree_add_text(tree, tvb, offset, 1, "AtomCacheReferenceIndex: %d", int_val);
251       offset += 1;
252       if (value_str)
253         *value_str = ep_strdup_printf("%d", int_val);
254       break;
255
256     case SMALL_INTEGER_EXT:
257       int_val = tvb_get_guint8(tvb, offset);
258       proto_tree_add_text(tree, tvb, offset, 1, "Int: %d", int_val);
259       offset += 1;
260       if (value_str)
261         *value_str = ep_strdup_printf("%d", int_val);
262       break;
263
264     case INTEGER_EXT:
265       int_val = tvb_get_ntohl(tvb, offset);
266       proto_tree_add_text(tree, tvb, offset, 4, "Int: %d", int_val);
267       offset += 4;
268       if (value_str)
269         *value_str = ep_strdup_printf("%d", int_val);
270       break;
271
272     case PID_EXT:
273       offset = dissect_etf_type("Node", pinfo, tvb, offset, tree);
274       proto_tree_add_text(tree, tvb, offset, 4, "ID: 0x%08X", tvb_get_ntohl(tvb, offset));
275       offset += 4;
276       proto_tree_add_text(tree, tvb, offset, 4, "Serial: %u", tvb_get_ntohl(tvb, offset));
277       offset += 4;
278       proto_tree_add_text(tree, tvb, offset, 1, "Creation: %u", tvb_get_guint8(tvb, offset));
279       offset++;
280       break;
281
282     case SMALL_TUPLE_EXT:
283       offset = dissect_etf_tuple_content(FALSE, pinfo, tvb, offset, tree, value_str);
284       break;
285
286     case LARGE_TUPLE_EXT:
287       offset = dissect_etf_tuple_content(TRUE, pinfo, tvb, offset, tree, value_str);
288       break;
289
290     case NIL_EXT:
291       break;
292
293     case LIST_EXT:
294       len = tvb_get_ntohl(tvb, offset);
295       proto_tree_add_text(tree, tvb, offset, 4, "Len: %d", len);
296       offset += 4;
297       for (i=0; i<len; i++) {
298         offset = dissect_etf_type(NULL, pinfo, tvb, offset, tree);
299       }
300       offset = dissect_etf_type("Tail", pinfo, tvb, offset, tree);
301       break;
302
303     case NEW_REFERENCE_EXT:
304       len = tvb_get_ntohs(tvb, offset);
305       proto_tree_add_text(tree, tvb, offset, 2, "Len: %d", len);
306       offset += 2;
307       offset = dissect_etf_type("Node", pinfo, tvb, offset, tree);
308       proto_tree_add_text(tree, tvb, offset, 1, "Creation: %u", tvb_get_guint8(tvb, offset));
309       offset++;
310       for (i=0; i<len; i++) {
311         proto_tree_add_text(tree, tvb, offset, 4, "ID[%d]: 0x%08X", i, tvb_get_ntohl(tvb, offset));
312         offset += 4;
313       }
314       break;
315   }
316
317   return offset;
318 }
319
320 static gint dissect_etf_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const gchar *label) {
321   gint offset = 0;
322   guint8 mag, tag;
323   proto_item *ti;
324   proto_tree *etf_tree;
325
326   mag = tvb_get_guint8(tvb, offset);
327   if (mag != VERSION_MAGIC) {
328     return 0;
329   }
330
331   ti = proto_tree_add_text(tree, tvb, offset, -1, "%s", (label) ? label : "External Term Format");
332   etf_tree = proto_item_add_subtree(ti, ett_etf);
333
334   proto_tree_add_text(etf_tree, tvb, offset, 1, "VERSION_MAGIC: %d", mag);
335   offset++;
336
337   tag = tvb_get_guint8(tvb, offset);
338   proto_tree_add_item(etf_tree, hf_etf_tag, tvb, offset, 1, ENC_BIG_ENDIAN);
339   offset++;
340
341   if (!label)
342     proto_item_set_text(ti, "%s", val_to_str(tag, VALS(etf_tag_vals), "unknown tag (%d)"));
343
344   offset = dissect_etf_type_content(tag, pinfo, tvb, offset, etf_tree, NULL);
345
346   proto_item_set_len(ti, offset);
347
348   return offset;
349 }
350
351 static gint dissect_etf_type(const gchar *label, packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree) {
352   gint begin = offset;
353   guint8 tag;
354   proto_item *ti;
355   proto_tree *etf_tree;
356   gchar *value_str = NULL;
357
358   ti = proto_tree_add_text(tree, tvb, offset, -1, "%s", (label) ? label : "External Term Format");
359   etf_tree = proto_item_add_subtree(ti, ett_etf);
360
361   tag = tvb_get_guint8(tvb, offset);
362   proto_tree_add_item(etf_tree, hf_etf_tag, tvb, offset, 1, ENC_BIG_ENDIAN);
363   offset++;
364
365   if (!label)
366     proto_item_set_text(ti, "%s", val_to_str(tag, VALS(etf_tag_vals), "unknown tag (%d)"));
367
368   offset = dissect_etf_type_content(tag, pinfo, tvb, offset, etf_tree, &value_str);
369   if (value_str)
370     proto_item_append_text(ti, ": %s", value_str);
371
372   proto_item_set_len(ti, offset - begin);
373
374   return offset;
375 }
376
377 static gboolean is_handshake(tvbuff_t *tvb, int offset) {
378   guint32 len = tvb_get_ntohs(tvb, offset);
379   guint8 tag = tvb_get_guint8(tvb, offset + 2);
380   return ((len > 0) && strchr("nras", tag) && (len == (guint32)tvb_length_remaining(tvb, offset + 2)));
381 }
382
383 /*--- dissect_erldp_handshake -------------------------------------------------*/
384 static void dissect_erldp_handshake(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
385   gint offset = 0;
386   guint8 tag;
387   gint i;
388   gboolean is_challenge = FALSE;
389   guint32 str_len;
390   const gchar *str;
391
392   proto_tree_add_item(tree, hf_erldp_length_2, tvb, offset, 2, ENC_BIG_ENDIAN);
393   offset += 2;
394   tag = tvb_get_guint8(tvb, offset);
395   proto_tree_add_item(tree, hf_erldp_tag, tvb, offset, 1, ENC_ASCII|ENC_NA);
396   offset++;
397
398   switch (tag) {
399     case 'n' :
400       proto_tree_add_item(tree, hf_erldp_version, tvb, offset, 2, ENC_BIG_ENDIAN);
401       offset += 2;
402       proto_tree_add_item(tree, hf_erldp_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
403       offset += 4;
404       if (tvb_bytes_exist(tvb, offset, 4)) {
405         for (i=0; i<4; i++)
406           if(!g_ascii_isprint(tvb_get_guint8(tvb, offset + i))) {
407             is_challenge = TRUE;
408             break;
409           }
410       }
411       if (is_challenge) {
412         proto_tree_add_item(tree, hf_erldp_challenge, tvb, offset, 4, ENC_BIG_ENDIAN);
413         offset += 4;
414       }
415       str_len = tvb_length_remaining(tvb, offset);
416       str = tvb_get_ephemeral_string(tvb, offset, str_len);
417       proto_tree_add_item(tree, hf_erldp_name, tvb, offset, str_len, ENC_ASCII|ENC_NA);
418       col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s", (is_challenge) ? "SEND_CHALLENGE" : "SEND_NAME", str);
419       break;
420
421     case 'r' :
422       proto_tree_add_item(tree, hf_erldp_challenge, tvb, offset, 4, ENC_BIG_ENDIAN);
423       offset += 4;
424       proto_tree_add_item(tree, hf_erldp_digest, tvb, offset, 16, ENC_NA);
425       offset += 16;
426       col_add_str(pinfo->cinfo, COL_INFO, "SEND_CHALLENGE_REPLY");
427       break;
428
429     case 'a' :
430       proto_tree_add_item(tree, hf_erldp_digest, tvb, offset, 16, ENC_NA);
431       offset += 16;
432       col_add_str(pinfo->cinfo, COL_INFO, "SEND_CHALLENGE_ACK");
433       break;
434
435     case 's' :
436       str_len = tvb_length_remaining(tvb, offset);
437       str = tvb_get_ephemeral_string(tvb, offset, str_len);
438       proto_tree_add_item(tree, hf_erldp_status, tvb, offset, str_len, ENC_ASCII|ENC_NA);
439       col_add_fstr(pinfo->cinfo, COL_INFO, "SEND_STATUS %s", str);
440       break;
441   }
442 }
443
444 /*--- dissect_erldp_pdu -------------------------------------------------*/
445 static void dissect_erldp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
446   gint offset;
447   guint32 msg_len;
448   guint8 type, ctl_op;
449   proto_tree *erldp_tree;
450   proto_item *ti;
451   tvbuff_t *next_tvb = NULL;
452
453   col_set_str(pinfo->cinfo, COL_PROTOCOL, PSNAME);
454
455   ti = proto_tree_add_item(tree, proto_erldp, tvb, 0, -1, ENC_NA);
456   erldp_tree = proto_item_add_subtree(ti, ett_erldp);
457
458   if (is_handshake(tvb, 0)) {
459     dissect_erldp_handshake(tvb, pinfo, erldp_tree);
460     return;
461   }
462
463   offset = 0;
464
465   msg_len = tvb_get_ntohl(tvb, offset);
466   proto_tree_add_item(erldp_tree, hf_erldp_length_4, tvb, offset, 4, ENC_BIG_ENDIAN);
467   offset += 4;
468
469   if (msg_len == 0) {
470     col_add_str(pinfo->cinfo, COL_INFO, "KEEP_ALIVE");
471     return;
472   }
473
474   type = tvb_get_guint8(tvb, offset);
475   switch (type) {
476     case ERL_PASS_THROUGH:
477       proto_tree_add_item(erldp_tree, hf_erldp_type, tvb, offset, 1, ENC_BIG_ENDIAN);
478       offset++;
479       break;
480
481     case VERSION_MAGIC:
482       next_tvb = tvb_new_subset(tvb, offset, -1, 4 + msg_len - offset);
483       offset += dissect_etf_pdu(next_tvb, pinfo, erldp_tree, "DistributionHeader");
484       if ((tvb_get_guint8(tvb, offset) == SMALL_TUPLE_EXT) && (tvb_get_guint8(tvb, offset + 2) == SMALL_INTEGER_EXT)) {
485         ctl_op = tvb_get_guint8(tvb, offset + 3);
486         col_add_str(pinfo->cinfo, COL_INFO, val_to_str(ctl_op, VALS(erldp_ctlmsg_vals), "unknown ControlMessage operation (%d)"));
487       }
488       offset = dissect_etf_type("ControlMessage", pinfo, tvb, offset, erldp_tree);
489       if (tvb_length_remaining(tvb, offset) > 0)
490         offset = dissect_etf_type("Message", pinfo, tvb, offset, erldp_tree);
491       break;
492
493     default:
494       proto_tree_add_item(erldp_tree, hf_erldp_type, tvb, offset, 1, ENC_BIG_ENDIAN);
495       offset++;
496       col_add_str(pinfo->cinfo, COL_INFO, "unknown header format");
497       return;
498   }
499 }
500
501 /*--- get_erldp_pdu_len -------------------------------------------------*/
502 static guint get_erldp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset) {
503   if (is_handshake(tvb, offset))
504     return(2 + tvb_get_ntohs(tvb, offset));
505   else
506     return(4 + tvb_get_ntohl(tvb, offset));
507 }
508
509 /*--- dissect_erldp -------------------------------------------------*/
510 static void
511 dissect_erldp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
512   tcp_dissect_pdus(tvb, pinfo, tree,
513                    erldp_desegment,    /* desegment or not   */
514                     4,               /* fixed-length part of the PDU */
515                    get_erldp_pdu_len,  /* routine to get the length of the PDU */
516                    dissect_erldp_pdu); /* routine to dissect a PDU */
517 }
518
519 /*--- proto_register_erldp ----------------------------------------------*/
520 void proto_register_erldp(void) {
521   /* module_t *erldp_module; */
522
523   /* List of fields */
524   static hf_register_info hf[] = {
525     /*--- Handshake fields ---*/
526     { &hf_erldp_length_2, { "Length", "erldp.len",
527                         FT_UINT16, BASE_DEC, NULL, 0x0,
528                         "Message Length", HFILL}},
529     { &hf_erldp_tag,  { "Tag", "erldp.tag",
530                         FT_STRING, BASE_NONE, NULL, 0x0,
531                         NULL, HFILL}},
532     { &hf_erldp_type, { "Type", "erldp.type",
533                         FT_UINT8, BASE_DEC, NULL, 0x0,
534                         NULL, HFILL}},
535     { &hf_erldp_version, { "Version", "erldp.version",
536                         FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
537                         NULL, HFILL}},
538     { &hf_erldp_flags,  { "Flags", "erldp.flags",
539                         FT_UINT32, BASE_HEX, NULL, 0x0,
540                         NULL, HFILL}},
541     { &hf_erldp_challenge, { "Challenge", "erldp.challenge",
542                         FT_UINT32, BASE_HEX, NULL, 0x0,
543                         NULL, HFILL}},
544     { &hf_erldp_digest, { "Digest", "erldp.digest",
545                         FT_BYTES, BASE_NONE, NULL, 0x0,
546                         NULL, HFILL}},
547     { &hf_erldp_name, { "Name", "erldp.name",
548                         FT_STRING, BASE_NONE, NULL, 0x0,
549                         NULL, HFILL}},
550     { &hf_erldp_status, { "Status", "erldp.status",
551                         FT_STRING, BASE_NONE, NULL, 0x0,
552                         NULL, HFILL}},
553     /*---  ---*/
554     { &hf_erldp_length_4, { "Length", "erldp.len",
555                         FT_UINT32, BASE_DEC, NULL, 0x0,
556                         "Message Length", HFILL}},
557
558     /*--- ETF  ---*/
559     { &hf_etf_tag,    { "Tag", "erldp.etf_tag",
560                         FT_UINT8, BASE_DEC, VALS(etf_tag_vals), 0x0,
561                         NULL, HFILL}},
562   };
563
564   /* List of subtrees */
565   static gint *ett[] = {
566     &ett_erldp,
567     &ett_etf,
568     &ett_etf_flags,
569     &ett_etf_acrs,
570     &ett_etf_acr,
571     &ett_etf_tmp,
572   };
573
574   /* Register protocol and dissector */
575   proto_erldp = proto_register_protocol(PNAME, PSNAME, PFNAME);
576   register_dissector(PFNAME, dissect_erldp, proto_erldp);
577   erldp_handle = find_dissector(PFNAME);
578
579   /* Register fields and subtrees */
580   proto_register_field_array(proto_erldp, hf, array_length(hf));
581   proto_register_subtree_array(ett, array_length(ett));
582
583 }
584
585 /*--- proto_reg_handoff_erldp -------------------------------------------*/
586 void proto_reg_handoff_erldp(void) {
587
588   dissector_add_handle("tcp.port", erldp_handle);
589
590   data_handle = find_dissector("data");
591 }