/src/wireshark/epan/dissectors/packet-ehdlc.c
Line | Count | Source |
1 | | /* packet-ehdlc.c |
2 | | * Routines for packet dissection of Ericsson HDLC as used in A-bis over IP |
3 | | * Copyright 2010-2012, 2016 by Harald Welte <laforge@gnumonks.org> |
4 | | * |
5 | | * This code is based on pure educational guesses while looking at protocol |
6 | | * traces, as there is no publicly available protocol description by Ericsson. |
7 | | * Even the name is a guess, since it looks quite a bit like HDLC and is used |
8 | | * by Ericsson, I called it EHDLC. |
9 | | * |
10 | | * Wireshark - Network traffic analyzer |
11 | | * By Gerald Combs <gerald@wireshark.org> |
12 | | * Copyright 1998 Gerald Combs |
13 | | * |
14 | | * SPDX-License-Identifier: GPL-2.0-or-later |
15 | | */ |
16 | | |
17 | | #include "config.h" |
18 | | |
19 | | #include <epan/packet.h> |
20 | | #include <epan/tfs.h> |
21 | | #include "packet-xdlc.h" |
22 | | |
23 | | void proto_register_ehdlc(void); |
24 | | void proto_reg_handoff_ehdlc(void); |
25 | | |
26 | | /* Initialize the protocol and registered fields */ |
27 | | static int proto_ehdlc; |
28 | | |
29 | | static int hf_ehdlc_data_len; |
30 | | static int hf_ehdlc_csapi; |
31 | | static int hf_ehdlc_ctei; |
32 | | |
33 | | static int hf_ehdlc_sapi; |
34 | | static int hf_ehdlc_tei; |
35 | | static int hf_ehdlc_c_r; |
36 | | |
37 | | static int hf_ehdlc_xid_payload; |
38 | | static int hf_ehdlc_xid_win_tx; |
39 | | static int hf_ehdlc_xid_win_rx; |
40 | | static int hf_ehdlc_xid_ack_tmr_ms; |
41 | | static int hf_ehdlc_xid_format_id; |
42 | | static int hf_ehdlc_xid_group_id; |
43 | | static int hf_ehdlc_xid_len; |
44 | | static int hf_ehdlc_control; |
45 | | |
46 | | static int hf_ehdlc_p; |
47 | | static int hf_ehdlc_f; |
48 | | static int hf_ehdlc_u_modifier_cmd; |
49 | | static int hf_ehdlc_u_modifier_resp; |
50 | | static int hf_ehdlc_ftype_s_u; |
51 | | |
52 | | static int hf_ehdlc_n_r; |
53 | | static int hf_ehdlc_n_s; |
54 | | static int hf_ehdlc_p_ext; |
55 | | static int hf_ehdlc_f_ext; |
56 | | static int hf_ehdlc_s_ftype; |
57 | | static int hf_ehdlc_ftype_i; |
58 | | static int hf_ehdlc_ftype_s_u_ext; |
59 | | |
60 | | static dissector_handle_t ehdlc_handle; |
61 | | |
62 | | /* Used only for U frames */ |
63 | | static const xdlc_cf_items ehdlc_cf_items = { |
64 | | NULL, |
65 | | NULL, |
66 | | &hf_ehdlc_p, |
67 | | &hf_ehdlc_f, |
68 | | NULL, |
69 | | &hf_ehdlc_u_modifier_cmd, |
70 | | &hf_ehdlc_u_modifier_resp, |
71 | | NULL, |
72 | | &hf_ehdlc_ftype_s_u |
73 | | }; |
74 | | |
75 | | /* Used only for I and S frames */ |
76 | | static const xdlc_cf_items ehdlc_cf_items_ext = { |
77 | | &hf_ehdlc_n_r, |
78 | | &hf_ehdlc_n_s, |
79 | | &hf_ehdlc_p_ext, |
80 | | &hf_ehdlc_f_ext, |
81 | | &hf_ehdlc_s_ftype, |
82 | | NULL, |
83 | | NULL, |
84 | | &hf_ehdlc_ftype_i, |
85 | | &hf_ehdlc_ftype_s_u_ext, |
86 | | }; |
87 | | |
88 | | /* Initialize the subtree pointers */ |
89 | | static int ett_ehdlc; |
90 | | static int ett_ehdlc_xid; |
91 | | static int ett_ehdlc_control; |
92 | | |
93 | | enum { |
94 | | SUB_RSL, |
95 | | SUB_OML, |
96 | | SUB_TFP, |
97 | | SUB_PGSL, |
98 | | SUB_DATA, |
99 | | |
100 | | SUB_MAX |
101 | | }; |
102 | | |
103 | | /* Determine TEI from Compressed TEI */ |
104 | | static uint8_t tei_from_ctei(uint8_t ctei) |
105 | 0 | { |
106 | 0 | if (ctei < 12) |
107 | 0 | return ctei; |
108 | 0 | else |
109 | 0 | return 60 + (ctei - 12); |
110 | 0 | } |
111 | | |
112 | | static uint8_t c_r_from_csapi(uint8_t csapi) |
113 | 0 | { |
114 | 0 | switch (csapi) { |
115 | 0 | case 1: |
116 | 0 | case 6: |
117 | 0 | return 1; |
118 | 0 | default: |
119 | 0 | return 0; |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | static uint8_t sapi_from_csapi(uint8_t csapi) |
124 | 0 | { |
125 | 0 | switch (csapi) { |
126 | 0 | case 0: |
127 | 0 | case 1: /* RSL */ |
128 | 0 | return 0; |
129 | 0 | case 2: /* TFP */ |
130 | 0 | return 10; |
131 | 0 | case 3: /* TFP */ |
132 | 0 | return 11; |
133 | 0 | case 4: /* P-GSL */ |
134 | 0 | return 12; |
135 | 0 | case 5: |
136 | 0 | case 6: /* OML */ |
137 | 0 | return 62; |
138 | 0 | case 7: |
139 | 0 | default: |
140 | | /* error! */ |
141 | 0 | return 0; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | static dissector_handle_t sub_handles[SUB_MAX]; |
146 | | |
147 | | static int |
148 | | dissect_ehdlc_xid(proto_tree *tree, tvbuff_t *tvb, unsigned base_offset, unsigned len) |
149 | 0 | { |
150 | 0 | unsigned offset = base_offset; |
151 | 0 | proto_item *ti; |
152 | 0 | proto_tree *xid_tree; |
153 | | |
154 | | /* XID is formatted like ISO 8885, typically we see |
155 | | * something like |
156 | | * 82 format identifier |
157 | | * 80 group identifier |
158 | | * 00 09 length |
159 | | * 07 01 05 Window Size Tx |
160 | | * 09 01 04 Ack Timer (msec) |
161 | | * 08 01 05 Window Size Rx */ |
162 | 0 | ti = proto_tree_add_item(tree, hf_ehdlc_xid_payload, |
163 | 0 | tvb, offset, len, ENC_NA); |
164 | 0 | xid_tree = proto_item_add_subtree(ti, ett_ehdlc_xid); |
165 | |
|
166 | 0 | proto_tree_add_item(xid_tree, hf_ehdlc_xid_format_id, tvb, offset++, 1, ENC_NA); |
167 | 0 | proto_tree_add_item(xid_tree, hf_ehdlc_xid_group_id, tvb, offset++, 1, ENC_NA); |
168 | 0 | proto_tree_add_item(xid_tree, hf_ehdlc_xid_len, tvb, offset, 2, ENC_BIG_ENDIAN); |
169 | 0 | offset += 2; |
170 | |
|
171 | 0 | while (tvb_reported_length_remaining(tvb, offset) >= 2) { |
172 | 0 | uint8_t iei = tvb_get_uint8(tvb, offset++); |
173 | 0 | uint8_t ie_len = tvb_get_uint8(tvb, offset++); |
174 | |
|
175 | 0 | switch (iei) { |
176 | 0 | case 0x07: |
177 | 0 | proto_tree_add_item(xid_tree, hf_ehdlc_xid_win_tx, tvb, |
178 | 0 | offset, ie_len, ENC_NA); |
179 | 0 | break; |
180 | 0 | case 0x08: |
181 | 0 | proto_tree_add_item(xid_tree, hf_ehdlc_xid_win_rx, tvb, |
182 | 0 | offset, ie_len, ENC_NA); |
183 | 0 | break; |
184 | 0 | case 0x09: |
185 | 0 | proto_tree_add_item(xid_tree, hf_ehdlc_xid_ack_tmr_ms, tvb, |
186 | 0 | offset, ie_len, ENC_NA); |
187 | 0 | break; |
188 | 0 | } |
189 | 0 | offset += ie_len; |
190 | 0 | } |
191 | | |
192 | 0 | return offset - base_offset; |
193 | 0 | } |
194 | | |
195 | | /* Code to actually dissect the packets */ |
196 | | static int |
197 | | dissect_ehdlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
198 | 0 | { |
199 | 0 | int offset = 4; |
200 | |
|
201 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "EHDLC"); |
202 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
203 | |
|
204 | 0 | while (tvb_reported_length_remaining(tvb, offset) > 0) { |
205 | 0 | proto_item *ti = NULL; |
206 | 0 | proto_tree *ehdlc_tree = NULL; |
207 | 0 | uint16_t len, hdr2; |
208 | 0 | uint8_t csapi, ctei, sapi, tei, c_r; |
209 | 0 | tvbuff_t *next_tvb; |
210 | 0 | uint16_t control; |
211 | 0 | bool is_response = false, is_extended = true; |
212 | 0 | int header_length = 2; /* Address + Length field */ |
213 | |
|
214 | 0 | hdr2 = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
215 | 0 | len = hdr2 & 0x1FF; |
216 | 0 | csapi = hdr2 >> 13; |
217 | 0 | sapi = sapi_from_csapi(csapi); |
218 | 0 | c_r = c_r_from_csapi(csapi); |
219 | 0 | ctei = (hdr2 >> 9) & 0xF; |
220 | 0 | tei = tei_from_ctei(ctei); |
221 | | |
222 | | /* Add TEI to INFO column */ |
223 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " | TEI:%02u | ", tei); |
224 | 0 | col_set_fence(pinfo->cinfo, COL_INFO); |
225 | |
|
226 | 0 | if (tree) { |
227 | | /* Use MIN(...,...) in the following to prevent a premature */ |
228 | | /* exception before we try to dissect whatever is available. */ |
229 | 0 | ti = proto_tree_add_protocol_format(tree, proto_ehdlc, |
230 | 0 | tvb, offset, MIN(len, tvb_captured_length_remaining(tvb,offset)), |
231 | 0 | "Ericsson HDLC protocol"); |
232 | 0 | ehdlc_tree = proto_item_add_subtree(ti, ett_ehdlc); |
233 | |
|
234 | 0 | proto_tree_add_item(ehdlc_tree, hf_ehdlc_csapi, |
235 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
236 | 0 | proto_tree_add_item(ehdlc_tree, hf_ehdlc_ctei, |
237 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
238 | 0 | ti = proto_tree_add_uint(ehdlc_tree, hf_ehdlc_c_r, |
239 | 0 | tvb, offset, 1, c_r); |
240 | 0 | proto_item_set_generated(ti); |
241 | 0 | ti = proto_tree_add_uint(ehdlc_tree, hf_ehdlc_sapi, |
242 | 0 | tvb, offset, 1, sapi); |
243 | 0 | proto_item_set_generated(ti); |
244 | 0 | ti = proto_tree_add_uint(ehdlc_tree, hf_ehdlc_tei, |
245 | 0 | tvb, offset, 1, tei); |
246 | 0 | proto_item_set_generated(ti); |
247 | 0 | proto_tree_add_item(ehdlc_tree, hf_ehdlc_data_len, |
248 | 0 | tvb, offset, 2, ENC_BIG_ENDIAN); |
249 | 0 | } |
250 | |
|
251 | 0 | if (sapi == 10 || sapi == 11) { |
252 | | /* Voice TRAU */ |
253 | 0 | next_tvb = tvb_new_subset_length(tvb, offset+2, len-2); |
254 | 0 | call_dissector(sub_handles[SUB_TFP], next_tvb, pinfo, tree); |
255 | 0 | offset += len; |
256 | 0 | continue; |
257 | 0 | } else if (sapi == 12) { |
258 | | /* GPRS TRAU */ |
259 | 0 | next_tvb = tvb_new_subset_length(tvb, offset+2, len-2); |
260 | 0 | call_dissector(sub_handles[SUB_PGSL], next_tvb, pinfo, tree); |
261 | 0 | offset += len; |
262 | 0 | continue; |
263 | 0 | } |
264 | | |
265 | 0 | control = dissect_xdlc_control(tvb, offset+2, pinfo, ehdlc_tree, hf_ehdlc_control, |
266 | 0 | ett_ehdlc_control, &ehdlc_cf_items, &ehdlc_cf_items_ext, |
267 | 0 | NULL, NULL, is_response, is_extended, false); |
268 | 0 | header_length += XDLC_CONTROL_LEN(control, is_extended); |
269 | |
|
270 | 0 | if (XDLC_IS_INFORMATION(control)) { |
271 | 0 | next_tvb = tvb_new_subset_length(tvb, offset+header_length, |
272 | 0 | len-header_length); |
273 | |
|
274 | 0 | switch (sapi) { |
275 | 0 | case 0: |
276 | | /* len == 4 seems to be some kind of ACK */ |
277 | 0 | if (len <= 4) |
278 | 0 | break; |
279 | 0 | call_dissector(sub_handles[SUB_RSL], next_tvb, pinfo, tree); |
280 | 0 | break; |
281 | 0 | case 62: |
282 | | /* len == 4 seems to be some kind of ACK */ |
283 | 0 | if (len <= 4) |
284 | 0 | break; |
285 | 0 | call_dissector(sub_handles[SUB_OML], next_tvb, pinfo, tree); |
286 | 0 | break; |
287 | 0 | default: |
288 | 0 | call_dissector(sub_handles[SUB_DATA], next_tvb, pinfo, tree); |
289 | 0 | break; |
290 | 0 | } |
291 | 0 | } else if (control == (XDLC_U | XDLC_XID)) { |
292 | 0 | dissect_ehdlc_xid(ehdlc_tree, tvb, offset+header_length, |
293 | 0 | len-header_length); |
294 | 0 | } |
295 | | |
296 | 0 | if (len == 0) |
297 | 0 | len = 1; |
298 | 0 | offset += len; |
299 | 0 | } |
300 | 0 | return tvb_captured_length(tvb); |
301 | 0 | } |
302 | | |
303 | | void |
304 | | proto_register_ehdlc(void) |
305 | 14 | { |
306 | 14 | static hf_register_info hf[] = { |
307 | 14 | { &hf_ehdlc_data_len, |
308 | 14 | { "DataLen", "ehdlc.data_len", |
309 | 14 | FT_UINT16, BASE_DEC, NULL, 0x01FF, |
310 | 14 | "The length of the data (in bytes)", HFILL } |
311 | 14 | }, |
312 | 14 | { &hf_ehdlc_csapi, |
313 | 14 | { "Compressed SAPI", "ehdlc.csapi", |
314 | 14 | FT_UINT8, BASE_DEC, NULL, 0xE0, |
315 | 14 | NULL, HFILL} |
316 | 14 | }, |
317 | 14 | { &hf_ehdlc_ctei, |
318 | 14 | { "Compressed TEI", "ehdlc.ctei", |
319 | 14 | FT_UINT8, BASE_DEC, NULL, 0x1E, |
320 | 14 | NULL, HFILL} |
321 | 14 | }, |
322 | 14 | { &hf_ehdlc_sapi, |
323 | 14 | { "SAPI", "ehdlc.sapi", |
324 | 14 | FT_UINT8, BASE_DEC, NULL, 0, |
325 | 14 | NULL, HFILL } |
326 | 14 | }, |
327 | 14 | { &hf_ehdlc_tei, |
328 | 14 | { "TEI", "ehdlc.tei", |
329 | 14 | FT_UINT8, BASE_DEC, NULL, 0, |
330 | 14 | NULL, HFILL } |
331 | 14 | }, |
332 | 14 | { &hf_ehdlc_c_r, |
333 | 14 | { "C/R", "ehdlc.c_r", |
334 | 14 | FT_UINT8, BASE_DEC, NULL, 0, |
335 | 14 | NULL, HFILL } |
336 | 14 | }, |
337 | | |
338 | 14 | { &hf_ehdlc_xid_payload, |
339 | 14 | { "XID Payload", "ehdlc.xid_payload", |
340 | 14 | FT_BYTES, BASE_NONE, NULL, 0, |
341 | 14 | NULL, HFILL } |
342 | 14 | }, |
343 | 14 | { &hf_ehdlc_xid_win_tx, |
344 | 14 | { "Transmit Window", "ehdlc.xid.win_tx", |
345 | 14 | FT_UINT8, BASE_DEC, NULL, 0, |
346 | 14 | NULL, HFILL } |
347 | 14 | }, |
348 | 14 | { &hf_ehdlc_xid_win_rx, |
349 | 14 | { "Receive Window", "ehdlc.xid.win_rx", |
350 | 14 | FT_UINT8, BASE_DEC, NULL, 0, |
351 | 14 | NULL, HFILL } |
352 | 14 | }, |
353 | 14 | { &hf_ehdlc_xid_ack_tmr_ms, |
354 | 14 | { "Timer (ms)", "ehdlc.xid.ack_tmr_ms", |
355 | 14 | FT_UINT8, BASE_DEC, NULL, 0, |
356 | 14 | NULL, HFILL } |
357 | 14 | }, |
358 | 14 | { &hf_ehdlc_xid_format_id, |
359 | 14 | { "Format Identifier", "ehdlc.xid.format_id", |
360 | 14 | FT_UINT8, BASE_HEX, NULL, 0, |
361 | 14 | NULL, HFILL } |
362 | 14 | }, |
363 | 14 | { &hf_ehdlc_xid_group_id, |
364 | 14 | { "Group Identifier", "ehdlc.xid.group_id", |
365 | 14 | FT_UINT8, BASE_HEX, NULL, 0, |
366 | 14 | NULL, HFILL } |
367 | 14 | }, |
368 | 14 | { &hf_ehdlc_xid_len, |
369 | 14 | { "XID Length", "ehdlc.xid.len", |
370 | 14 | FT_UINT16, BASE_DEC, NULL, 0, |
371 | 14 | NULL, HFILL } |
372 | 14 | }, |
373 | 14 | { &hf_ehdlc_control, |
374 | 14 | { "Control Field", "ehdlc.control", |
375 | 14 | FT_UINT16, BASE_HEX, NULL, 0, |
376 | 14 | NULL, HFILL } |
377 | 14 | }, |
378 | 14 | { &hf_ehdlc_n_r, |
379 | 14 | { "N(R)", "ehdlc.control.n_r", |
380 | 14 | FT_UINT16, BASE_DEC, NULL, XDLC_N_R_EXT_MASK, |
381 | 14 | NULL, HFILL } |
382 | 14 | }, |
383 | 14 | { &hf_ehdlc_n_s, |
384 | 14 | { "N(S)", "ehdlc.control.n_s", |
385 | 14 | FT_UINT16, BASE_DEC, NULL, XDLC_N_S_EXT_MASK, |
386 | 14 | NULL, HFILL } |
387 | 14 | }, |
388 | 14 | { &hf_ehdlc_p, |
389 | 14 | { "Poll", "ehdlc.control.p", |
390 | 14 | FT_BOOLEAN, 8, TFS(&tfs_set_notset), XDLC_P_F, |
391 | 14 | NULL, HFILL } |
392 | 14 | }, |
393 | 14 | { &hf_ehdlc_p_ext, |
394 | 14 | { "Poll", "ehdlc.control.p", |
395 | 14 | FT_BOOLEAN, 16, TFS(&tfs_set_notset), XDLC_P_F_EXT, |
396 | 14 | NULL, HFILL } |
397 | 14 | }, |
398 | 14 | { &hf_ehdlc_f, |
399 | 14 | { "Final", "ehdlc.control.f", |
400 | 14 | FT_BOOLEAN, 8, TFS(&tfs_set_notset), XDLC_P_F, |
401 | 14 | NULL, HFILL } |
402 | 14 | }, |
403 | 14 | { &hf_ehdlc_f_ext, |
404 | 14 | { "Final", "ehdlc.control.f", |
405 | 14 | FT_BOOLEAN, 16, TFS(&tfs_set_notset), XDLC_P_F_EXT, |
406 | 14 | NULL, HFILL } |
407 | 14 | }, |
408 | 14 | { &hf_ehdlc_s_ftype, |
409 | 14 | { "Supervisory frame type", "ehdlc.control.s_ftype", |
410 | 14 | FT_UINT16, BASE_HEX, VALS(stype_vals), XDLC_S_FTYPE_MASK, |
411 | 14 | NULL, HFILL } |
412 | 14 | }, |
413 | 14 | { &hf_ehdlc_u_modifier_cmd, |
414 | 14 | { "Command", "ehdlc.control.u_modifier_cmd", |
415 | 14 | FT_UINT8, BASE_HEX, VALS(modifier_vals_cmd), XDLC_U_MODIFIER_MASK, |
416 | 14 | NULL, HFILL } |
417 | 14 | }, |
418 | 14 | { &hf_ehdlc_u_modifier_resp, |
419 | 14 | { "Response", "ehdlc.control.u_modifier_resp", |
420 | 14 | FT_UINT8, BASE_HEX, VALS(modifier_vals_resp), XDLC_U_MODIFIER_MASK, |
421 | 14 | NULL, HFILL } |
422 | 14 | }, |
423 | 14 | { &hf_ehdlc_ftype_i, |
424 | 14 | { "Frame Type", "ehdlc.control.ftype", |
425 | 14 | FT_UINT16, BASE_HEX, VALS(ftype_vals), XDLC_I_MASK, |
426 | 14 | NULL, HFILL } |
427 | 14 | }, |
428 | 14 | { &hf_ehdlc_ftype_s_u, |
429 | 14 | { "Frame Type", "ehdlc.control.ftype", |
430 | 14 | FT_UINT8, BASE_HEX, VALS(ftype_vals), XDLC_S_U_MASK, |
431 | 14 | NULL, HFILL } |
432 | 14 | }, |
433 | 14 | { &hf_ehdlc_ftype_s_u_ext, |
434 | 14 | { "Frame Type", "ehdlc.control.ftype", |
435 | 14 | FT_UINT16, BASE_HEX, VALS(ftype_vals), XDLC_S_U_MASK, |
436 | 14 | NULL, HFILL } |
437 | 14 | }, |
438 | 14 | }; |
439 | | |
440 | 14 | static int *ett[] = { |
441 | 14 | &ett_ehdlc, |
442 | 14 | &ett_ehdlc_xid, |
443 | 14 | &ett_ehdlc_control, |
444 | 14 | }; |
445 | | |
446 | 14 | proto_ehdlc = |
447 | 14 | proto_register_protocol("Ericsson HDLC", |
448 | 14 | "Ericsson HDLC as used in A-bis over IP", "ehdlc"); |
449 | | |
450 | 14 | proto_register_field_array(proto_ehdlc, hf, array_length(hf)); |
451 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
452 | | |
453 | 14 | ehdlc_handle = register_dissector("ehdlc", dissect_ehdlc, proto_ehdlc); |
454 | 14 | } |
455 | | |
456 | | void |
457 | | proto_reg_handoff_ehdlc(void) |
458 | 14 | { |
459 | 14 | sub_handles[SUB_RSL] = find_dissector_add_dependency("gsm_abis_rsl", proto_ehdlc); |
460 | 14 | sub_handles[SUB_OML] = find_dissector_add_dependency("gsm_abis_oml", proto_ehdlc); |
461 | 14 | sub_handles[SUB_TFP] = find_dissector_add_dependency("gsm_abis_tfp", proto_ehdlc); |
462 | 14 | sub_handles[SUB_PGSL] = find_dissector_add_dependency("gsm_abis_pgsl", proto_ehdlc); |
463 | 14 | sub_handles[SUB_DATA] = find_dissector("data"); |
464 | | |
465 | 14 | dissector_add_for_decode_as("l2tp.pw_type", ehdlc_handle); |
466 | 14 | } |
467 | | |
468 | | /* |
469 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
470 | | * |
471 | | * Local variables: |
472 | | * c-basic-offset: 8 |
473 | | * tab-width: 8 |
474 | | * indent-tabs-mode: t |
475 | | * End: |
476 | | * |
477 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
478 | | * :indentSize=8:tabSize=8:noTabs=false: |
479 | | */ |