2 * Routines for the disassembly of the Mikrotik Neighbor Discovery Protocol
4 * Copyright 2011 Joerg Mayer (see AUTHORS file)
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 http://wiki.mikrotik.com/wiki/Manual:IP/Neighbor_discovery
16 - Find out about first 4 bytes (are the first 2 simply part of the sequence number?)
17 - Find out about additional TLVs
18 - Find out about unpack values
23 #include <epan/packet.h>
24 void proto_register_mndp(void);
25 void proto_reg_handoff_mndp(void);
27 static dissector_handle_t mndp_handle
;
29 /* protocol handles */
30 static int proto_mndp
;
34 static int ett_mndp_tlv_header
;
38 static int hf_mndp_tlv_type
;
39 static int hf_mndp_tlv_length
;
40 static int hf_mndp_tlv_data
;
42 static int hf_mndp_header_unknown
;
43 static int hf_mndp_header_seqno
;
45 static int hf_mndp_mac
;
46 static int hf_mndp_softwareid
;
47 static int hf_mndp_version
;
48 static int hf_mndp_identity
;
49 static int hf_mndp_uptime
;
50 static int hf_mndp_platform
;
51 static int hf_mndp_board
;
52 static int hf_mndp_unpack
;
53 static int hf_mndp_ipv6address
;
54 static int hf_mndp_interfacename
;
55 static int hf_mndp_ipv4address
;
57 #define PROTO_SHORT_NAME "MNDP"
58 #define PROTO_LONG_NAME "Mikrotik Neighbor Discovery Protocol"
60 #define PORT_MNDP 5678 /* Not IANA registered */
62 /* ============= copy/paste/modify from value_string.[hc] ============== */
63 typedef struct _ext_value_string
{
67 int (*specialfunction
)(tvbuff_t
*, packet_info
*, proto_tree
*, uint32_t,
68 uint32_t, const struct _ext_value_string
*);
69 const struct _ext_value_string
*evs
;
74 match_strextval_idx(uint32_t val
, const ext_value_string
*vs
, int *idx
) {
78 while (vs
[i
].strptr
) {
79 if (vs
[i
].value
== val
) {
94 extval_to_str_idx(wmem_allocator_t
*pool
, uint32_t val
, const ext_value_string
*vs
, int *idx
, const char *fmt
) {
100 ret
= match_strextval_idx(val
, vs
, idx
);
104 return wmem_strdup_printf(pool
, fmt
, val
);
106 /* ============= end copy/paste/modify ============== */
108 /* Forward decls needed by mndp_tunnel_tlv_vals et al */
109 static int dissect_tlv(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*mndp_tree
,
110 uint32_t offset
, uint32_t length
, const ext_value_string
*value_array
);
112 static const ext_value_string mndp_body_tlv_vals
[] = {
113 { 1, "MAC-Address", &hf_mndp_mac
, NULL
, NULL
},
114 { 5, "Identity", &hf_mndp_identity
, NULL
, NULL
},
115 { 7, "Version", &hf_mndp_version
, NULL
, NULL
},
116 { 8, "Platform", &hf_mndp_platform
, NULL
, NULL
},
117 { 10, "Uptime", &hf_mndp_uptime
, NULL
, (ext_value_string
*)true },
118 { 11, "Software-ID", &hf_mndp_softwareid
, NULL
, NULL
},
119 { 12, "Board", &hf_mndp_board
, NULL
, NULL
},
120 { 14, "Unpack", &hf_mndp_unpack
, NULL
, NULL
},
121 { 15, "IPv6-Address", &hf_mndp_ipv6address
, NULL
, NULL
},
122 { 16, "Interface name", &hf_mndp_interfacename
, NULL
, NULL
},
123 { 17, "IPv4-Address", &hf_mndp_ipv4address
, NULL
, NULL
},
125 { 0, NULL
, NULL
, NULL
, NULL
}
128 static const value_string mndp_unpack_vals
[] = {
129 /* none|simple|uncompressed-headers|uncompressed-all */
135 dissect_tlv(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*mndp_tree
,
136 uint32_t offset
, uint32_t length _U_
, const ext_value_string
*value_array
)
140 proto_item
*tlv_tree
;
141 proto_item
*type_item
;
144 unsigned encoding_info
;
146 tlv_type
= tvb_get_ntohs(tvb
, offset
);
147 tlv_length
= tvb_get_ntohs(tvb
, offset
+ 2);
148 tlv_tree
= proto_tree_add_subtree_format(mndp_tree
, tvb
,
149 offset
, tlv_length
+4, ett_mndp_tlv_header
, NULL
,
153 extval_to_str_idx(pinfo
->pool
, tlv_type
, value_array
, NULL
, "Unknown"));
155 type_item
= proto_tree_add_item(tlv_tree
, hf_mndp_tlv_type
,
156 tvb
, offset
, 2, ENC_BIG_ENDIAN
);
157 proto_item_append_text(type_item
, " = %s",
158 extval_to_str_idx(pinfo
->pool
, tlv_type
, value_array
,
159 &type_index
, "Unknown"));
161 proto_tree_add_item(tlv_tree
, hf_mndp_tlv_length
,
162 tvb
, offset
, 2, ENC_BIG_ENDIAN
);
168 tlv_end
= offset
+ tlv_length
;
170 /* Make hf_ handling independent of specialfunction */
171 /* FIXME: Properly handle encoding info */
172 if ( type_index
!= -1
173 && !value_array
[type_index
].specialfunction
174 && value_array
[type_index
].evs
!= NULL
176 encoding_info
= value_array
[type_index
].evs
? true : false;
178 encoding_info
= false;
180 if ( type_index
!= -1 && value_array
[type_index
].hf_element
) {
181 proto_tree_add_item(tlv_tree
,
182 *(value_array
[type_index
].hf_element
),
183 tvb
, offset
, tlv_length
, encoding_info
);
185 proto_tree_add_item(tlv_tree
, hf_mndp_tlv_data
,
186 tvb
, offset
, tlv_length
, ENC_NA
);
188 if ( type_index
!= -1 && value_array
[type_index
].specialfunction
) {
191 while (offset
< tlv_end
) {
192 newoffset
= value_array
[type_index
].specialfunction (
193 tvb
, pinfo
, tlv_tree
, offset
, tlv_length
,
194 value_array
[type_index
].evs
);
195 DISSECTOR_ASSERT(newoffset
> offset
);
203 dissect_mndp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
206 proto_tree
*mndp_tree
;
208 uint32_t packet_length
;
210 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PROTO_SHORT_NAME
);
212 packet_length
= tvb_reported_length(tvb
);
214 /* Header dissection */
215 ti
= proto_tree_add_item(tree
, proto_mndp
, tvb
, offset
, -1,
217 mndp_tree
= proto_item_add_subtree(ti
, ett_mndp
);
219 proto_tree_add_item(mndp_tree
, hf_mndp_header_unknown
, tvb
, offset
, 2,
222 proto_tree_add_item(mndp_tree
, hf_mndp_header_seqno
, tvb
, offset
, 2,
226 while (offset
< packet_length
) {
227 offset
= dissect_tlv(tvb
, pinfo
, mndp_tree
,
228 offset
, 0, mndp_body_tlv_vals
);
235 test_mndp(tvbuff_t
*tvb
)
237 /* Observed captures of MNDP always seem to have port 5678 as both
238 * the source and destination port, and have a broadcast destination IP
239 * and destination MAC address (if we have those layers.)
240 * The TLVs are also transmitted in increasing type order.
241 * TLV type 1 (MAC-Address) appears to be mandatory (and thus first),
242 * and always has length 6.
243 * We could also step through all the TLVs to see if the types and
244 * lengths are reasonable.
245 * Any of these could be used to strengthen the heuristic further.
249 /* Minimum of 8 bytes, 4 Bytes header + 1 TLV-header */
250 if ( tvb_captured_length(tvb
) < 8) {
254 type
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
259 length
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
264 /* Length does *not* include the type and length fields. */
265 if (tvb_reported_length_remaining(tvb
, offset
) < length
) {
269 /* If there's more data left, it should be another TLV. */
270 if (tvb_reported_length_remaining(tvb
, offset
) > 0 &&
271 tvb_reported_length_remaining(tvb
, offset
) < 4) {
278 dissect_mndp_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
280 if ( !test_mndp(tvb
) ) {
283 dissect_mndp(tvb
, pinfo
, tree
);
288 dissect_mndp_static(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
290 if ( !test_mndp(tvb
) ) {
293 return dissect_mndp(tvb
, pinfo
, tree
);
297 proto_register_mndp(void)
299 static hf_register_info hf
[] = {
303 { "TlvType", "mndp.tlv.type", FT_UINT16
, BASE_DEC
, NULL
,
306 { &hf_mndp_tlv_length
,
307 { "TlvLength", "mndp.tlv.length", FT_UINT16
, BASE_DEC
, NULL
,
311 { "TlvData", "mndp.tlv.data", FT_BYTES
, BASE_NONE
, NULL
,
314 /* MNDP tunnel header */
315 { &hf_mndp_header_unknown
,
316 { "Header Unknown", "mndp.header.unknown", FT_BYTES
, BASE_NONE
, NULL
,
319 { &hf_mndp_header_seqno
,
320 { "SeqNo", "mndp.header.seqno", FT_UINT16
, BASE_DEC
, NULL
,
323 /* MNDP tunnel data */
325 { "MAC-Address", "mndp.mac", FT_ETHER
, BASE_NONE
, NULL
,
328 { &hf_mndp_softwareid
,
329 { "Software-ID", "mndp.softwareid", FT_STRING
, BASE_NONE
, NULL
,
333 { "Version", "mndp.version", FT_STRING
, BASE_NONE
, NULL
,
337 { "Identity", "mndp.identity", FT_STRING
, BASE_NONE
, NULL
,
341 { "Uptime", "mndp.uptime", FT_RELATIVE_TIME
, BASE_NONE
, NULL
,
345 { "Platform", "mndp.platform", FT_STRING
, BASE_NONE
, NULL
,
349 { "Board", "mndp.board", FT_STRING
, BASE_NONE
, NULL
,
353 { "Unpack", "mndp.unpack", FT_UINT8
, BASE_DEC
, VALS(mndp_unpack_vals
),
356 { &hf_mndp_ipv6address
,
357 { "IPv6-Address", "mndp.ipv6address", FT_IPv6
, BASE_NONE
, NULL
,
360 { &hf_mndp_interfacename
,
361 { "Interface name", "mndp.interfacename", FT_STRING
, BASE_NONE
, NULL
,
364 { &hf_mndp_ipv4address
,
365 { "IPv4-Address", "mndp.ipv4address", FT_IPv4
, BASE_NONE
, NULL
,
369 static int *ett
[] = {
371 &ett_mndp_tlv_header
,
374 proto_mndp
= proto_register_protocol(PROTO_LONG_NAME
, PROTO_SHORT_NAME
, "mndp");
375 proto_register_field_array(proto_mndp
, hf
, array_length(hf
));
376 proto_register_subtree_array(ett
, array_length(ett
));
378 mndp_handle
= register_dissector("mndp", dissect_mndp_static
, proto_mndp
);
382 proto_reg_handoff_mndp(void)
384 dissector_add_uint_with_preference("udp.port", PORT_MNDP
, mndp_handle
);
385 heur_dissector_add("udp", dissect_mndp_heur
, "MNDP over UDP", "mndp_udp", proto_mndp
, HEURISTIC_DISABLE
);
389 * Editor modelines - https://www.wireshark.org/tools/modelines.html
394 * indent-tabs-mode: t
397 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
398 * :indentSize=8:tabSize=8:noTabs=false: