summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-gsm_l2rcop.c
blob: b9c6fcce3b7086c11d528913d84d2b2dd9b81bea (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
/* packet-gsm_l2rcop.c
 * Routines for GSM L2RCOP (3GPP TS 27.002) dissection
 * (C) 2023 Harald Welte <laforge@osmocom.org>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/xdlc.h>
#include <epan/reassemble.h>
#include <epan/conversation.h>

void proto_register_gsm_l2rcop(void);

static int proto_l2rcop = -1;

static int hf_l2rcop_sa = -1;
static int hf_l2rcop_sb = -1;
static int hf_l2rcop_x = -1;
static int hf_l2rcop_addr = -1;
static int hf_l2rcop_break = -1;
static int hf_l2rcop_break_ack = -1;

static int ett_l2rcop = -1;

static const value_string addr_vals[] = {
	{ 31, "last status change, remainder empty" },
	{ 30, "last status change, remainder full of characters" },
	{ 29, "destructive break signal, remainder empty" },
	{ 28, "destructive break acknowledge, remainder empty" },
	{ 27, "extended address in ext octet" },
	{ 0, NULL }
};

static void
add_characters(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, guint offset, guint len)
{
	tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, len);
	call_data_dissector(next_tvb, pinfo, tree);
}

/* Dissect a L2RCOP message as described in 3GPP TS 27.002 */
static int
dissect_l2rcop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
	int reported_len = tvb_reported_length(tvb);
	guint cur;

	/* we currently support RLP v0 + v1 (first octet is always status octet) */

	for (cur = 0; cur < (guint)reported_len; ) {
		guint8 oct = tvb_get_guint8(tvb, cur);
		guint8 addr = oct & 0x1f;
		proto_tree *l2rcop_tree;
		proto_item *ti;
		const gchar *addr_str = val_to_str(addr, addr_vals, "%u characters");

		ti = proto_tree_add_protocol_format(tree, proto_l2rcop, tvb, 0, reported_len,
						    "GSM L2RCOP Chunk Status=0x%02x (Addr: %s)", oct, addr_str);
		l2rcop_tree = proto_item_add_subtree(ti, ett_l2rcop);

		proto_tree_add_item(l2rcop_tree, hf_l2rcop_sa, tvb, cur, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(l2rcop_tree, hf_l2rcop_sb, tvb, cur, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(l2rcop_tree, hf_l2rcop_x, tvb, cur, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(l2rcop_tree, hf_l2rcop_addr, tvb, cur, 1, ENC_BIG_ENDIAN);

		switch (addr) {
		case 31: /* last status change, remainder empty */
			return reported_len;
		case 30: /* last status change, remainder full of characters */
			add_characters(l2rcop_tree, pinfo, tvb, cur+1, reported_len-cur-1);
			return reported_len;
		case 29: /* destructive break signal, remainder empty */
			proto_tree_add_item(l2rcop_tree, hf_l2rcop_break, tvb, cur, 1, ENC_BIG_ENDIAN);
			return reported_len;
		case 28: /* destructive break acknowledge, remainder empty */
			proto_tree_add_item(l2rcop_tree, hf_l2rcop_break_ack, tvb, cur, 1, ENC_BIG_ENDIAN);
			return reported_len;
		case 27: /* extended address in ext octet */
			cur++;
			addr = tvb_get_guint8(tvb, cur) & 0x3f;
			/* This "cannot happen"; let's abort processing right now. */
			if (addr == 0)
				return reported_len;
			proto_tree_add_uint(l2rcop_tree, hf_l2rcop_addr, tvb, cur, 1, addr);
			add_characters(l2rcop_tree, pinfo, tvb, cur+1, addr);
			cur += 1 + addr;
			break;
		case 0:
			/* This "cannot happen"; let's abort processing right now. */
			return reported_len;
		default:
			/* This "cannot happen"; let's abort processing right now. */
			if (addr == 0)
				return reported_len;
			add_characters(l2rcop_tree, pinfo, tvb, cur+1, addr);
			cur += 1 + addr;
			break;
		}
	}

	return reported_len;
}

static const true_false_string x_vals = {
	"flow control ACTIVE", "flow control inactive"
};

static const true_false_string sab_vals = {
	"OFF", "ON"
};


void
proto_register_gsm_l2rcop(void)
{
	static hf_register_info hf[] = {
		{ &hf_l2rcop_sa,
		  { "SA", "gsm_l2rcop.sa", FT_BOOLEAN, 8, TFS(&sab_vals), 0x80,
		    NULL, HFILL }},
		{ &hf_l2rcop_sb,
		  { "SB", "gsm_l2rcop.sb", FT_BOOLEAN, 8, TFS(&sab_vals), 0x40,
		    NULL, HFILL }},
		{ &hf_l2rcop_x,
		  { "X", "gsm_l2rcop.x", FT_BOOLEAN, 8, TFS(&x_vals), 0x20,
		    NULL, HFILL }},
		{ &hf_l2rcop_addr,
		  { "Address", "gsm_l2rcop.addr", FT_UINT8, BASE_DEC|BASE_SPECIAL_VALS, VALS(addr_vals), 0x1f,
		    NULL, HFILL }},
		{ &hf_l2rcop_break,
		  { "Break", "gsm_l2rcop.break", FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }},
		{ &hf_l2rcop_break_ack,
		  { "Break Ack", "gsm_l2rcop.break_ack", FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }},
	};
	static gint *ett[] = {
		&ett_l2rcop,
	};

	proto_l2rcop = proto_register_protocol("GSM L2R Character Oriented Protocol (L2RCOP)", "GSM-L2RCOP",
						"gsm_l2rcop");
	proto_register_field_array(proto_l2rcop, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
	register_dissector("gsm_l2rcop", dissect_l2rcop, proto_l2rcop);
}


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