2 * Erlang Distribution Protocol
3 * http://www.erlang.org/doc/apps/erts/erl_dist_protocol.html
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
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.
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.
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.
34 #include <epan/packet.h>
35 #include <epan/strutil.h>
36 #include <epan/emem.h>
38 #include <epan/dissectors/packet-tcp.h>
39 #include <epan/dissectors/packet-epmd.h>
41 #define ERL_PASS_THROUGH 'p'
43 #define VERSION_MAGIC 131 /* 130 in erlang 4.2 */
45 #define SMALL_INTEGER_EXT 'a'
46 #define INTEGER_EXT 'b'
49 #define SMALL_ATOM_EXT 's'
50 #define REFERENCE_EXT 'e'
51 #define NEW_REFERENCE_EXT 'r'
53 #define NEW_FLOAT_EXT 'F'
55 #define SMALL_TUPLE_EXT 'h'
56 #define LARGE_TUPLE_EXT 'i'
58 #define STRING_EXT 'k'
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'
68 #define DIST_HEADER 'D'
69 #define ATOM_CACHE_REF 'R'
70 #define COMPRESSED 'P'
72 #define PNAME "Erlang Distribution Protocol"
73 #define PSNAME "ErlDP"
74 #define PFNAME "erldp"
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" },
105 static const value_string erldp_ctlmsg_vals[] = {
112 { 7, "GROUP_LEADER" },
116 { 16, "REG_SEND_TT" },
119 { 20, "DEMONITOR_P" },
120 { 21, "MONITOR_P_EXIT" },
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;
137 static int hf_etf_tag = -1;
139 /* Initialize the subtree pointers */
140 static gint ett_erldp = -1;
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;
149 static gboolean erldp_desegment = TRUE;
152 static dissector_handle_t erldp_handle = NULL;
155 dissector_handle_t data_handle;
157 /*--- External Term Format ---*/
159 static gint dissect_etf_type(const gchar *label, packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree);
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;
170 num = tvb_get_guint8(tvb, offset);
171 proto_tree_add_text(tree, tvb, offset, 1, "NumberOfAtomCacheRefs: %d", num);
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)));
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,
195 long_atom = flg & (0x01 << 4*(num%2));
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));
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);
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);
222 proto_item_set_len(ti_acrs, offset - acrs_offset);
227 static gint dissect_etf_tuple_content(gboolean large, packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree, gchar **value_str _U_) {
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);
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;
245 offset = dissect_etf_dist_header(pinfo, tvb, offset, tree);
249 int_val = tvb_get_guint8(tvb, offset);
250 proto_tree_add_text(tree, tvb, offset, 1, "AtomCacheReferenceIndex: %d", int_val);
253 *value_str = ep_strdup_printf("%d", int_val);
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);
261 *value_str = ep_strdup_printf("%d", int_val);
265 int_val = tvb_get_ntohl(tvb, offset);
266 proto_tree_add_text(tree, tvb, offset, 4, "Int: %d", int_val);
269 *value_str = ep_strdup_printf("%d", int_val);
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));
276 proto_tree_add_text(tree, tvb, offset, 4, "Serial: %u", tvb_get_ntohl(tvb, offset));
278 proto_tree_add_text(tree, tvb, offset, 1, "Creation: %u", tvb_get_guint8(tvb, offset));
282 case SMALL_TUPLE_EXT:
283 offset = dissect_etf_tuple_content(FALSE, pinfo, tvb, offset, tree, value_str);
286 case LARGE_TUPLE_EXT:
287 offset = dissect_etf_tuple_content(TRUE, pinfo, tvb, offset, tree, value_str);
294 len = tvb_get_ntohl(tvb, offset);
295 proto_tree_add_text(tree, tvb, offset, 4, "Len: %d", len);
297 for (i=0; i<len; i++) {
298 offset = dissect_etf_type(NULL, pinfo, tvb, offset, tree);
300 offset = dissect_etf_type("Tail", pinfo, tvb, offset, tree);
303 case NEW_REFERENCE_EXT:
304 len = tvb_get_ntohs(tvb, offset);
305 proto_tree_add_text(tree, tvb, offset, 2, "Len: %d", len);
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));
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));
320 static gint dissect_etf_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const gchar *label) {
324 proto_tree *etf_tree;
326 mag = tvb_get_guint8(tvb, offset);
327 if (mag != VERSION_MAGIC) {
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);
334 proto_tree_add_text(etf_tree, tvb, offset, 1, "VERSION_MAGIC: %d", mag);
337 tag = tvb_get_guint8(tvb, offset);
338 proto_tree_add_item(etf_tree, hf_etf_tag, tvb, offset, 1, ENC_BIG_ENDIAN);
342 proto_item_set_text(ti, "%s", val_to_str(tag, VALS(etf_tag_vals), "unknown tag (%d)"));
344 offset = dissect_etf_type_content(tag, pinfo, tvb, offset, etf_tree, NULL);
346 proto_item_set_len(ti, offset);
351 static gint dissect_etf_type(const gchar *label, packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree) {
355 proto_tree *etf_tree;
356 gchar *value_str = NULL;
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);
361 tag = tvb_get_guint8(tvb, offset);
362 proto_tree_add_item(etf_tree, hf_etf_tag, tvb, offset, 1, ENC_BIG_ENDIAN);
366 proto_item_set_text(ti, "%s", val_to_str(tag, VALS(etf_tag_vals), "unknown tag (%d)"));
368 offset = dissect_etf_type_content(tag, pinfo, tvb, offset, etf_tree, &value_str);
370 proto_item_append_text(ti, ": %s", value_str);
372 proto_item_set_len(ti, offset - begin);
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)));
383 /*--- dissect_erldp_handshake -------------------------------------------------*/
384 static void dissect_erldp_handshake(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
388 gboolean is_challenge = FALSE;
392 proto_tree_add_item(tree, hf_erldp_length_2, tvb, offset, 2, ENC_BIG_ENDIAN);
394 tag = tvb_get_guint8(tvb, offset);
395 proto_tree_add_item(tree, hf_erldp_tag, tvb, offset, 1, ENC_ASCII|ENC_NA);
400 proto_tree_add_item(tree, hf_erldp_version, tvb, offset, 2, ENC_BIG_ENDIAN);
402 proto_tree_add_item(tree, hf_erldp_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
404 if (tvb_bytes_exist(tvb, offset, 4)) {
406 if(!g_ascii_isprint(tvb_get_guint8(tvb, offset + i))) {
412 proto_tree_add_item(tree, hf_erldp_challenge, tvb, offset, 4, ENC_BIG_ENDIAN);
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);
422 proto_tree_add_item(tree, hf_erldp_challenge, tvb, offset, 4, ENC_BIG_ENDIAN);
424 proto_tree_add_item(tree, hf_erldp_digest, tvb, offset, 16, ENC_NA);
426 col_add_str(pinfo->cinfo, COL_INFO, "SEND_CHALLENGE_REPLY");
430 proto_tree_add_item(tree, hf_erldp_digest, tvb, offset, 16, ENC_NA);
432 col_add_str(pinfo->cinfo, COL_INFO, "SEND_CHALLENGE_ACK");
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);
444 /*--- dissect_erldp_pdu -------------------------------------------------*/
445 static void dissect_erldp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
449 proto_tree *erldp_tree;
451 tvbuff_t *next_tvb = NULL;
453 col_set_str(pinfo->cinfo, COL_PROTOCOL, PSNAME);
455 ti = proto_tree_add_item(tree, proto_erldp, tvb, 0, -1, ENC_NA);
456 erldp_tree = proto_item_add_subtree(ti, ett_erldp);
458 if (is_handshake(tvb, 0)) {
459 dissect_erldp_handshake(tvb, pinfo, erldp_tree);
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);
470 col_add_str(pinfo->cinfo, COL_INFO, "KEEP_ALIVE");
474 type = tvb_get_guint8(tvb, offset);
476 case ERL_PASS_THROUGH:
477 proto_tree_add_item(erldp_tree, hf_erldp_type, tvb, offset, 1, ENC_BIG_ENDIAN);
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)"));
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);
494 proto_tree_add_item(erldp_tree, hf_erldp_type, tvb, offset, 1, ENC_BIG_ENDIAN);
496 col_add_str(pinfo->cinfo, COL_INFO, "unknown header format");
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));
506 return(4 + tvb_get_ntohl(tvb, offset));
509 /*--- dissect_erldp -------------------------------------------------*/
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 */
519 /*--- proto_register_erldp ----------------------------------------------*/
520 void proto_register_erldp(void) {
521 /* module_t *erldp_module; */
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,
532 { &hf_erldp_type, { "Type", "erldp.type",
533 FT_UINT8, BASE_DEC, NULL, 0x0,
535 { &hf_erldp_version, { "Version", "erldp.version",
536 FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
538 { &hf_erldp_flags, { "Flags", "erldp.flags",
539 FT_UINT32, BASE_HEX, NULL, 0x0,
541 { &hf_erldp_challenge, { "Challenge", "erldp.challenge",
542 FT_UINT32, BASE_HEX, NULL, 0x0,
544 { &hf_erldp_digest, { "Digest", "erldp.digest",
545 FT_BYTES, BASE_NONE, NULL, 0x0,
547 { &hf_erldp_name, { "Name", "erldp.name",
548 FT_STRING, BASE_NONE, NULL, 0x0,
550 { &hf_erldp_status, { "Status", "erldp.status",
551 FT_STRING, BASE_NONE, NULL, 0x0,
554 { &hf_erldp_length_4, { "Length", "erldp.len",
555 FT_UINT32, BASE_DEC, NULL, 0x0,
556 "Message Length", HFILL}},
559 { &hf_etf_tag, { "Tag", "erldp.etf_tag",
560 FT_UINT8, BASE_DEC, VALS(etf_tag_vals), 0x0,
564 /* List of subtrees */
565 static gint *ett[] = {
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);
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));
585 /*--- proto_reg_handoff_erldp -------------------------------------------*/
586 void proto_reg_handoff_erldp(void) {
588 dissector_add_handle("tcp.port", erldp_handle);
590 data_handle = find_dissector("data");