summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-nwp.c
blob: d21070bbc574b41ce6bdacb4786aa60e027345e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/* packet-nwp.c
 * Routines for NWP dissection
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Neighborhood Watch Protocol (NWP) is an XIA protocol for resolving network
 * addresses to link-layer addresses. Hosts on a LAN send NWP Announcement
 * packets with their host identifiers (HIDs), and neighbors in the LAN
 * respond with NWP Neighbor List packets containing their HIDs and associated
 * link-layer addresses.
 */

#include "config.h"
#include <epan/packet.h>
#include <epan/expert.h>

void proto_register_nwp(void);
void proto_reg_handoff_nwp(void);

static int proto_nwp;

/* Header fields for all NWP headers. */
static int hf_nwp_version;
static int hf_nwp_type;
static int hf_nwp_hid_count;
static int hf_nwp_haddr_len;

/* Header fields for NWP Announcement packets. */
static int hf_nwp_ann_haddr;
static int hf_nwp_ann_hids;
static int hf_nwp_ann_hid;

/* Header fields for NWP Neighbor List packets. */
static int hf_nwp_neigh_list;
static int hf_nwp_neigh;
static int hf_nwp_neigh_hid;
static int hf_nwp_neigh_num;
static int hf_nwp_neigh_haddr;

static int ett_nwp_tree;
static int ett_nwp_ann_hid_tree;
static int ett_nwp_neigh_list_tree;
static int ett_nwp_neigh_tree;

static expert_field ei_nwp_bad_type;

static dissector_handle_t nwp_handle;

#define NWP_XID_CHUNK_LEN	4
#define NWP_XID_LEN		20
/* Two characters for every byte + 4 for "hid-" + 1 for "\0" */
#define NWP_HID_STR_LEN		((NWP_XID_LEN * 2) + 5)

/* Room for a string of hardware addresses of the form:
 *
 *  XX:...:XX; XX:...:XX; ...
 *
 * (2 * LEN) bytes for each hardware address, plus (LEN - 1) bytes for colons,
 * plus 2 bytes for "; ". Multiply that by the COUNT of hardware addresses
 * that are present.
 */
#define NWP_HADDRS_STR_LEN(LEN, COUNT)	(((2 * LEN) + (LEN - 1) + 2) * COUNT)

#define NWPH_MIN_LEN		4
#define ETHERTYPE_NWP		0xC0DF
#define NWP_VERSION		0x01

#define NWP_TYPE_ANNOUNCEMENT	0x01
#define NWP_TYPE_NEIGH_LIST	0x02

/* Offsets of fields in NWP Announcements/Neighbor Lists. */
#define NWPH_VERS		0
#define NWPH_TYPE		1
#define NWPH_HIDC		2
#define NWPH_HLEN		3

#define NWPH_NLST		4
#define NWPH_HWAD		4

static const value_string nwp_type_vals[] = {
	{ NWP_TYPE_ANNOUNCEMENT,	"NWP Announcement" },
	{ NWP_TYPE_NEIGH_LIST,		"NWP Neighbor List" },
	{ 0,				NULL }
};

static void
add_hid_to_strbuf(tvbuff_t *tvb, wmem_strbuf_t *hid_buf, int offset)
{
	int i;
	for (i = 0; i < NWP_XID_LEN / NWP_XID_CHUNK_LEN; i++) {
		wmem_strbuf_append_printf(hid_buf, "%08x",
			tvb_get_ntohl(tvb, offset));
		offset += NWP_XID_CHUNK_LEN;
	}
}

static void
dissect_nwp_ann(tvbuff_t *tvb, proto_tree *nwp_tree, uint8_t hid_count,
	uint8_t ha_len)
{
	proto_tree *hid_tree = NULL;
	proto_item *ti = NULL;

	wmem_strbuf_t *buf;
	unsigned i;
	int offset;

	/* Add hardware address. */
	proto_tree_add_item(nwp_tree, hf_nwp_ann_haddr, tvb, NWPH_HWAD,
		ha_len, ENC_NA);

	/* Add tree for HIDs. */
	ti = proto_tree_add_item(nwp_tree, hf_nwp_ann_hids, tvb,
		NWPH_HWAD + ha_len, hid_count * NWP_XID_LEN, ENC_NA);
	hid_tree = proto_item_add_subtree(ti, ett_nwp_ann_hid_tree);

	buf = wmem_strbuf_new_sized(wmem_packet_scope(), NWP_HID_STR_LEN);

	/* Add HIDs. */
	offset = NWPH_HWAD + ha_len;
	for (i = 0; i < hid_count; i++) {
		const char *hid_str;

		wmem_strbuf_append(buf, "hid-");
		add_hid_to_strbuf(tvb, buf, offset);
		hid_str = wmem_strbuf_get_str(buf);

		proto_tree_add_string_format(hid_tree, hf_nwp_ann_hid, tvb,
			offset, NWP_XID_LEN, hid_str, "%s", hid_str);
		wmem_strbuf_truncate(buf, 0);

		offset += NWP_XID_LEN;
	}
}

/* Neighbor list is of the form:
 *      HID_1 NUM_1 HA_11 HA_12 ... HA_1NUM_1
 *      HID_2 NUM_2 HA_21 HA_22 ... HA_2NUM_2
 *      ...
 *      HID_count NUM_count HA_count1 HA_count2 ... HA_countNUM_count
 *
 *      count == hid_count.
 */
static void
dissect_nwp_nl(tvbuff_t *tvb, proto_tree *nwp_tree, uint8_t hid_count,
	uint8_t ha_len)
{
	proto_tree *neigh_list_tree = NULL;
	proto_tree *neigh_tree = NULL;
	proto_item *pi = NULL;

	unsigned i;
	int   offset = NWPH_NLST;

	wmem_strbuf_t *hid_buf = wmem_strbuf_new_sized(wmem_packet_scope(),
		NWP_HID_STR_LEN);

	/* Set up tree for neighbor list. */
	pi = proto_tree_add_item(nwp_tree, hf_nwp_neigh_list,
		tvb, NWPH_NLST, -1, ENC_NA);
	neigh_list_tree = proto_item_add_subtree(pi, ett_nwp_neigh_list_tree);

	for (i = 0; i < hid_count; i++) {
		const char *hid_str;
		unsigned j;
		uint8_t ha_count = tvb_get_uint8(tvb, offset + NWP_XID_LEN);

		/* Set up tree for this individual neighbor. */
		pi = proto_tree_add_none_format(neigh_list_tree, hf_nwp_neigh,
			tvb, offset, NWP_XID_LEN + 1 + ha_len * ha_count,
			"Neighbor %d", i + 1);
		neigh_tree = proto_item_add_subtree(pi, ett_nwp_neigh_tree);

		/* Add HID for this neighbor. */
		wmem_strbuf_append(hid_buf, "hid-");
		add_hid_to_strbuf(tvb, hid_buf, offset);
		hid_str = wmem_strbuf_get_str(hid_buf);
		proto_tree_add_string(neigh_tree, hf_nwp_neigh_hid, tvb,
			offset, NWP_XID_LEN, hid_str);
		wmem_strbuf_truncate(hid_buf, 0);
		offset += NWP_XID_LEN;

		/* Add number of devices this neighbor has. */
		proto_tree_add_item(neigh_tree, hf_nwp_neigh_num, tvb,
			offset, 1, ENC_BIG_ENDIAN);
		offset++;

		/* Add hardware addresses for the neighbor's devices. */
		for (j = 0; j < ha_count; j++)
			proto_tree_add_item(neigh_tree, hf_nwp_neigh_haddr,
				tvb, offset + (j * ha_len), ha_len, ENC_NA);

		offset += (ha_len * ha_count);
	}
}

static int
dissect_nwp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
	void *data _U_)
{
	proto_tree *nwp_tree = NULL;

	proto_item *ti = NULL;
	proto_item *type_ti = NULL;

	const char *type_str;
	uint8_t type, hid_count, ha_len;

	if (tvb_reported_length(tvb) < NWPH_MIN_LEN)
		return 0;

	col_set_str(pinfo->cinfo, COL_PROTOCOL, "NWP");

	col_clear(pinfo->cinfo, COL_INFO);
	type = tvb_get_uint8(tvb, NWPH_TYPE);
	type_str = val_to_str(type, nwp_type_vals,
		"Unknown NWP packet type (0x%02x)");
	col_add_str(pinfo->cinfo, COL_INFO, type_str);

	/* Construct protocol tree. */
	ti = proto_tree_add_item(tree, proto_nwp, tvb, 0, -1, ENC_NA);
	nwp_tree = proto_item_add_subtree(ti, ett_nwp_tree);

	/* Add NWP version. */
	proto_tree_add_item(nwp_tree, hf_nwp_version, tvb,
		NWPH_VERS, 1, ENC_BIG_ENDIAN);

	/* Add NWP type. */
	type_ti = proto_tree_add_item(nwp_tree, hf_nwp_type, tvb,
		NWPH_TYPE, 1, ENC_BIG_ENDIAN);
	if (!try_val_to_str(type, nwp_type_vals))
		expert_add_info_format(pinfo, type_ti, &ei_nwp_bad_type,
			"%s", type_str);

	/* Get # of HIDs represented in this packet to use later and add it. */
	hid_count = tvb_get_uint8(tvb, NWPH_HIDC);
	proto_tree_add_item(nwp_tree, hf_nwp_hid_count, tvb,
		NWPH_HIDC, 1, ENC_BIG_ENDIAN);

	/* Get hardware address length to use later and add it. */
	ha_len = tvb_get_uint8(tvb, NWPH_HLEN);
	proto_tree_add_item(nwp_tree, hf_nwp_haddr_len, tvb,
		NWPH_HLEN, 1, ENC_BIG_ENDIAN);

	switch (type) {
	case NWP_TYPE_ANNOUNCEMENT:
		dissect_nwp_ann(tvb, nwp_tree, hid_count, ha_len);
		break;
	case NWP_TYPE_NEIGH_LIST:
		dissect_nwp_nl(tvb, nwp_tree, hid_count, ha_len);
		break;
	default:
		break;
	}

	return tvb_captured_length(tvb);
}

void
proto_register_nwp(void)
{
	static hf_register_info hf[] = {

		{ &hf_nwp_version,
		{ "Version", "nwp.version", FT_UINT8,
		   BASE_DEC, NULL, 0x0,	NULL, HFILL }},

		{ &hf_nwp_type,
		{ "Type", "nwp.type", FT_UINT8,
		   BASE_HEX, VALS(nwp_type_vals), 0x0, NULL, HFILL }},

		{ &hf_nwp_hid_count,
		{ "HID Count", "nwp.hid_count", FT_UINT8,
		   BASE_DEC, NULL, 0x0,	NULL, HFILL }},

		{ &hf_nwp_haddr_len,
		{ "Hardware Address Length", "nwp.haddr_len", FT_UINT8,
		   BASE_DEC, NULL, 0x0,	NULL, HFILL }},

		{ &hf_nwp_ann_haddr,
		{ "Hardware Address", "nwp.ann_haddr", FT_BYTES,
		   SEP_COLON, NULL, 0x0, NULL, HFILL }},

		{ &hf_nwp_ann_hids,
		{ "HIDs", "nwp.ann_hids", FT_NONE,
		   BASE_NONE, NULL, 0x0, NULL, HFILL }},

		{ &hf_nwp_ann_hid,
		{ "HID", "nwp.ann_hid", FT_STRING,
		   BASE_NONE, NULL, 0x0, NULL, HFILL }},

		{ &hf_nwp_neigh_list,
		{ "Neighbor List", "nwp.neigh_list", FT_NONE,
		   BASE_NONE, NULL, 0x0, NULL, HFILL }},

		{ &hf_nwp_neigh,
		{ "Neighbor", "nwp.neigh", FT_NONE,
		   BASE_NONE, NULL, 0x0, NULL, HFILL }},

		{ &hf_nwp_neigh_hid,
		{ "HID", "nwp.neigh_hid", FT_STRING,
		   BASE_NONE, NULL, 0x0, NULL, HFILL }},

		{ &hf_nwp_neigh_num,
		{ "Number of Devices", "nwp.neigh_num", FT_UINT8,
		   BASE_DEC, NULL, 0x0, NULL, HFILL }},

		{ &hf_nwp_neigh_haddr,
		{ "Hardware Address", "nwp.neigh_haddr", FT_BYTES,
		   SEP_COLON, NULL, 0x0, NULL, HFILL }}
	};

	static int *ett[] = {
		&ett_nwp_tree,
		&ett_nwp_ann_hid_tree,
		&ett_nwp_neigh_list_tree,
		&ett_nwp_neigh_tree
	};

	static ei_register_info ei[] = {
		{ &ei_nwp_bad_type,
		{ "nwp.bad_type", PI_MALFORMED, PI_ERROR,
		  "Invalid type", EXPFILL }}
	};

	expert_module_t *expert_nwp;

	proto_nwp = proto_register_protocol("Neighborhood Watch Protocol", "NWP", "nwp");

	nwp_handle = register_dissector("nwp", dissect_nwp, proto_nwp);
	proto_register_field_array(proto_nwp, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));

	expert_nwp = expert_register_protocol(proto_nwp);
	expert_register_field_array(expert_nwp, ei, array_length(ei));
}

void
proto_reg_handoff_nwp(void)
{
	dissector_add_uint("ethertype", ETHERTYPE_NWP, nwp_handle);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */