1 /* packet-nwp.c
2  * Routines for NWP dissection
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  * Neighborhood Watch Protocol (NWP) is an XIA protocol for resolving network
11  * addresses to link-layer addresses. Hosts on a LAN send NWP Announcement
12  * packets with their host identifiers (HIDs), and neighbors in the LAN
13  * respond with NWP Neighbor List packets containing their HIDs and associated
14  * link-layer addresses.
15  */
16 
17 #include "config.h"
18 #include <epan/packet.h>
19 #include <epan/expert.h>
20 
21 void proto_register_nwp(void);
22 void proto_reg_handoff_nwp(void);
23 
24 static gint proto_nwp		= -1;
25 
26 /* Header fields for all NWP headers. */
27 static gint hf_nwp_version	= -1;
28 static gint hf_nwp_type		= -1;
29 static gint hf_nwp_hid_count	= -1;
30 static gint hf_nwp_haddr_len	= -1;
31 
32 /* Header fields for NWP Announcement packets. */
33 static gint hf_nwp_ann_haddr	= -1;
34 static gint hf_nwp_ann_hids	= -1;
35 static gint hf_nwp_ann_hid	= -1;
36 
37 /* Header fields for NWP Neighbor List packets. */
38 static gint hf_nwp_neigh_list	= -1;
39 static gint hf_nwp_neigh	= -1;
40 static gint hf_nwp_neigh_hid	= -1;
41 static gint hf_nwp_neigh_num	= -1;
42 static gint hf_nwp_neigh_haddr	= -1;
43 
44 static gint ett_nwp_tree		= -1;
45 static gint ett_nwp_ann_hid_tree	= -1;
46 static gint ett_nwp_neigh_list_tree	= -1;
47 static gint ett_nwp_neigh_tree		= -1;
48 
49 static expert_field ei_nwp_bad_type = EI_INIT;
50 
51 static dissector_handle_t nwp_handle;
52 
53 #define NWP_XID_CHUNK_LEN	4
54 #define NWP_XID_LEN		20
55 /* Two characters for every byte + 4 for "hid-" + 1 for "\0" */
56 #define NWP_HID_STR_LEN		((NWP_XID_LEN * 2) + 5)
57 
58 /* Room for a string of hardware addresses of the form:
59  *
60  *  XX:...:XX; XX:...:XX; ...
61  *
62  * (2 * LEN) bytes for each hardware address, plus (LEN - 1) bytes for colons,
63  * plus 2 bytes for "; ". Multiply that by the COUNT of hardware addresses
64  * that are present.
65  */
66 #define NWP_HADDRS_STR_LEN(LEN, COUNT)	(((2 * LEN) + (LEN - 1) + 2) * COUNT)
67 
68 #define NWPH_MIN_LEN		4
69 #define ETHERTYPE_NWP		0xC0DF
70 #define NWP_VERSION		0x01
71 
72 #define NWP_TYPE_ANNOUNCEMENT	0x01
73 #define NWP_TYPE_NEIGH_LIST	0x02
74 
75 /* Offsets of fields in NWP Announcements/Neighbor Lists. */
76 #define NWPH_VERS		0
77 #define NWPH_TYPE		1
78 #define NWPH_HIDC		2
79 #define NWPH_HLEN		3
80 
81 #define NWPH_NLST		4
82 #define NWPH_HWAD		4
83 
84 static const value_string nwp_type_vals[] = {
85 	{ NWP_TYPE_ANNOUNCEMENT,	"NWP Announcement" },
86 	{ NWP_TYPE_NEIGH_LIST,		"NWP Neighbor List" },
87 	{ 0,				NULL }
88 };
89 
90 static void
add_hid_to_strbuf(tvbuff_t * tvb,wmem_strbuf_t * hid_buf,guint8 offset)91 add_hid_to_strbuf(tvbuff_t *tvb, wmem_strbuf_t *hid_buf, guint8 offset)
92 {
93 	int i;
94 	for (i = 0; i < NWP_XID_LEN / NWP_XID_CHUNK_LEN; i++) {
95 		wmem_strbuf_append_printf(hid_buf, "%08x",
96 			tvb_get_ntohl(tvb, offset));
97 		offset += NWP_XID_CHUNK_LEN;
98 	}
99 }
100 
101 static void
dissect_nwp_ann(tvbuff_t * tvb,proto_tree * nwp_tree,guint8 hid_count,guint8 ha_len)102 dissect_nwp_ann(tvbuff_t *tvb, proto_tree *nwp_tree, guint8 hid_count,
103 	guint8 ha_len)
104 {
105 	proto_tree *hid_tree = NULL;
106 	proto_item *ti = NULL;
107 
108 	wmem_strbuf_t *buf;
109 	guint i;
110 	guint8 offset;
111 
112 	/* Add hardware address. */
113 	proto_tree_add_item(nwp_tree, hf_nwp_ann_haddr, tvb, NWPH_HWAD,
114 		ha_len, ENC_NA);
115 
116 	/* Add tree for HIDs. */
117 	ti = proto_tree_add_item(nwp_tree, hf_nwp_ann_hids, tvb,
118 		NWPH_HWAD + ha_len, hid_count * NWP_XID_LEN, ENC_NA);
119 	hid_tree = proto_item_add_subtree(ti, ett_nwp_ann_hid_tree);
120 
121 	buf = wmem_strbuf_sized_new(wmem_packet_scope(),
122 		NWP_HID_STR_LEN, NWP_HID_STR_LEN);
123 
124 	/* Add HIDs. */
125 	offset = NWPH_HWAD + ha_len;
126 	for (i = 0; i < hid_count; i++) {
127 		const gchar *hid_str;
128 
129 		wmem_strbuf_append(buf, "hid-");
130 		add_hid_to_strbuf(tvb, buf, offset);
131 		hid_str = wmem_strbuf_get_str(buf);
132 
133 		proto_tree_add_string_format(hid_tree, hf_nwp_ann_hid, tvb,
134 			offset, NWP_XID_LEN, hid_str, "%s", hid_str);
135 		wmem_strbuf_truncate(buf, 0);
136 
137 		offset += NWP_XID_LEN;
138 	}
139 }
140 
141 /* Neighbor list is of the form:
142  *      HID_1 NUM_1 HA_11 HA_12 ... HA_1NUM_1
143  *      HID_2 NUM_2 HA_21 HA_22 ... HA_2NUM_2
144  *      ...
145  *      HID_count NUM_count HA_count1 HA_count2 ... HA_countNUM_count
146  *
147  *      count == hid_count.
148  */
149 static void
dissect_nwp_nl(tvbuff_t * tvb,proto_tree * nwp_tree,guint8 hid_count,guint8 ha_len)150 dissect_nwp_nl(tvbuff_t *tvb, proto_tree *nwp_tree, guint8 hid_count,
151 	guint8 ha_len)
152 {
153 	proto_tree *neigh_list_tree = NULL;
154 	proto_tree *neigh_tree = NULL;
155 	proto_item *pi = NULL;
156 
157 	guint i;
158 	guint8 offset = NWPH_NLST;
159 
160 	wmem_strbuf_t *hid_buf = wmem_strbuf_sized_new(wmem_packet_scope(),
161 		NWP_HID_STR_LEN, NWP_HID_STR_LEN);
162 
163 	/* Set up tree for neighbor list. */
164 	pi = proto_tree_add_item(nwp_tree, hf_nwp_neigh_list,
165 		tvb, NWPH_NLST, -1, ENC_NA);
166 	neigh_list_tree = proto_item_add_subtree(pi, ett_nwp_neigh_list_tree);
167 
168 	for (i = 0; i < hid_count; i++) {
169 		const gchar *hid_str;
170 		guint j;
171 		guint8 ha_count = tvb_get_guint8(tvb, offset + NWP_XID_LEN);
172 
173 		/* Set up tree for this individual neighbor. */
174 		pi = proto_tree_add_none_format(neigh_list_tree, hf_nwp_neigh,
175 			tvb, offset, NWP_XID_LEN + 1 + ha_len * ha_count,
176 			"Neighbor %d", i + 1);
177 		neigh_tree = proto_item_add_subtree(pi, ett_nwp_neigh_tree);
178 
179 		/* Add HID for this neighbor. */
180 		wmem_strbuf_append(hid_buf, "hid-");
181 		add_hid_to_strbuf(tvb, hid_buf, offset);
182 		hid_str = wmem_strbuf_get_str(hid_buf);
183 		proto_tree_add_string(neigh_tree, hf_nwp_neigh_hid, tvb,
184 			offset, NWP_XID_LEN, hid_str);
185 		wmem_strbuf_truncate(hid_buf, 0);
186 		offset += NWP_XID_LEN;
187 
188 		/* Add number of devices this neighbor has. */
189 		proto_tree_add_item(neigh_tree, hf_nwp_neigh_num, tvb,
190 			offset, 1, ENC_BIG_ENDIAN);
191 		offset++;
192 
193 		/* Add hardware addresses for the neighbor's devices. */
194 		for (j = 0; j < ha_count; j++)
195 			proto_tree_add_item(neigh_tree, hf_nwp_neigh_haddr,
196 				tvb, offset + (j * ha_len), ha_len, ENC_NA);
197 
198 		offset += ha_len * ha_count;
199 	}
200 }
201 
202 static gint
dissect_nwp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)203 dissect_nwp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
204 	void *data _U_)
205 {
206 	proto_tree *nwp_tree = NULL;
207 
208 	proto_item *ti = NULL;
209 	proto_item *type_ti = NULL;
210 
211 	const gchar *type_str;
212 	guint8 type, hid_count, ha_len;
213 
214 	if (tvb_reported_length(tvb) < NWPH_MIN_LEN)
215 		return 0;
216 
217 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "NWP");
218 
219 	col_clear(pinfo->cinfo, COL_INFO);
220 	type = tvb_get_guint8(tvb, NWPH_TYPE);
221 	type_str = val_to_str(type, nwp_type_vals,
222 		"Unknown NWP packet type (0x%02x)");
223 	col_add_str(pinfo->cinfo, COL_INFO, type_str);
224 
225 	/* Construct protocol tree. */
226 	ti = proto_tree_add_item(tree, proto_nwp, tvb, 0, -1, ENC_NA);
227 	nwp_tree = proto_item_add_subtree(ti, ett_nwp_tree);
228 
229 	/* Add NWP version. */
230 	proto_tree_add_item(nwp_tree, hf_nwp_version, tvb,
231 		NWPH_VERS, 1, ENC_BIG_ENDIAN);
232 
233 	/* Add NWP type. */
234 	type_ti = proto_tree_add_item(nwp_tree, hf_nwp_type, tvb,
235 		NWPH_TYPE, 1, ENC_BIG_ENDIAN);
236 	if (!try_val_to_str(type, nwp_type_vals))
237 		expert_add_info_format(pinfo, type_ti, &ei_nwp_bad_type,
238 			"%s", type_str);
239 
240 	/* Get # of HIDs represented in this packet to use later and add it. */
241 	hid_count = tvb_get_guint8(tvb, NWPH_HIDC);
242 	proto_tree_add_item(nwp_tree, hf_nwp_hid_count, tvb,
243 		NWPH_HIDC, 1, ENC_BIG_ENDIAN);
244 
245 	/* Get hardware address length to use later and add it. */
246 	ha_len = tvb_get_guint8(tvb, NWPH_HLEN);
247 	proto_tree_add_item(nwp_tree, hf_nwp_haddr_len, tvb,
248 		NWPH_HLEN, 1, ENC_BIG_ENDIAN);
249 
250 	switch (type) {
251 	case NWP_TYPE_ANNOUNCEMENT:
252 		dissect_nwp_ann(tvb, nwp_tree, hid_count, ha_len);
253 		break;
254 	case NWP_TYPE_NEIGH_LIST:
255 		dissect_nwp_nl(tvb, nwp_tree, hid_count, ha_len);
256 		break;
257 	default:
258 		break;
259 	}
260 
261 	return tvb_captured_length(tvb);
262 }
263 
264 void
proto_register_nwp(void)265 proto_register_nwp(void)
266 {
267 	static hf_register_info hf[] = {
268 
269 		{ &hf_nwp_version,
270 		{ "Version", "nwp.version", FT_UINT8,
271 		   BASE_DEC, NULL, 0x0,	NULL, HFILL }},
272 
273 		{ &hf_nwp_type,
274 		{ "Type", "nwp.type", FT_UINT8,
275 		   BASE_HEX, VALS(nwp_type_vals), 0x0, NULL, HFILL }},
276 
277 		{ &hf_nwp_hid_count,
278 		{ "HID Count", "nwp.hid_count", FT_UINT8,
279 		   BASE_DEC, NULL, 0x0,	NULL, HFILL }},
280 
281 		{ &hf_nwp_haddr_len,
282 		{ "Hardware Address Length", "nwp.haddr_len", FT_UINT8,
283 		   BASE_DEC, NULL, 0x0,	NULL, HFILL }},
284 
285 		{ &hf_nwp_ann_haddr,
286 		{ "Hardware Address", "nwp.ann_haddr", FT_BYTES,
287 		   SEP_COLON, NULL, 0x0, NULL, HFILL }},
288 
289 		{ &hf_nwp_ann_hids,
290 		{ "HIDs", "nwp.ann_hids", FT_NONE,
291 		   BASE_NONE, NULL, 0x0, NULL, HFILL }},
292 
293 		{ &hf_nwp_ann_hid,
294 		{ "HID", "nwp.ann_hid", FT_STRING,
295 		   BASE_NONE, NULL, 0x0, NULL, HFILL }},
296 
297 		{ &hf_nwp_neigh_list,
298 		{ "Neighbor List", "nwp.neigh_list", FT_NONE,
299 		   BASE_NONE, NULL, 0x0, NULL, HFILL }},
300 
301 		{ &hf_nwp_neigh,
302 		{ "Neighbor", "nwp.neigh", FT_NONE,
303 		   BASE_NONE, NULL, 0x0, NULL, HFILL }},
304 
305 		{ &hf_nwp_neigh_hid,
306 		{ "HID", "nwp.neigh_hid", FT_STRING,
307 		   BASE_NONE, NULL, 0x0, NULL, HFILL }},
308 
309 		{ &hf_nwp_neigh_num,
310 		{ "Number of Devices", "nwp.neigh_num", FT_UINT8,
311 		   BASE_DEC, NULL, 0x0, NULL, HFILL }},
312 
313 		{ &hf_nwp_neigh_haddr,
314 		{ "Hardware Address", "nwp.neigh_haddr", FT_BYTES,
315 		   SEP_COLON, NULL, 0x0, NULL, HFILL }}
316 	};
317 
318 	static gint *ett[] = {
319 		&ett_nwp_tree,
320 		&ett_nwp_ann_hid_tree,
321 		&ett_nwp_neigh_list_tree,
322 		&ett_nwp_neigh_tree
323 	};
324 
325 	static ei_register_info ei[] = {
326 		{ &ei_nwp_bad_type,
327 		{ "nwp.bad_type", PI_MALFORMED, PI_ERROR,
328 		  "Invalid type", EXPFILL }}
329 	};
330 
331 	expert_module_t *expert_nwp;
332 
333 	proto_nwp = proto_register_protocol(
334 		"Neighborhood Watch Protocol",
335 		"NWP",
336 	        "nwp");
337 
338 	nwp_handle = register_dissector("nwp", dissect_nwp, proto_nwp);
339 	proto_register_field_array(proto_nwp, hf, array_length(hf));
340 	proto_register_subtree_array(ett, array_length(ett));
341 
342 	expert_nwp = expert_register_protocol(proto_nwp);
343 	expert_register_field_array(expert_nwp, ei, array_length(ei));
344 }
345 
346 void
proto_reg_handoff_nwp(void)347 proto_reg_handoff_nwp(void)
348 {
349 	dissector_add_uint("ethertype", ETHERTYPE_NWP, nwp_handle);
350 }
351 
352 /*
353  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
354  *
355  * Local variables:
356  * c-basic-offset: 8
357  * tab-width: 8
358  * indent-tabs-mode: t
359  * End:
360  *
361  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
362  * :indentSize=8:tabSize=8:noTabs=false:
363  */
364