3 * Routines for LWAPP encapsulated packet disassembly
6 * Copyright (c) 2003 by David Frascone <dave@frascone.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include <epan/packet.h>
18 #include <epan/prefs.h>
19 #include <epan/proto_data.h>
20 #include <epan/expert.h>
22 #include <wsutil/array.h>
24 void proto_register_lwapp(void);
25 void proto_reg_handoff_lwapp(void);
27 static dissector_handle_t lwapp_l3_handle
;
28 static dissector_handle_t lwapp_handle
;
30 #define LWAPP_8023_PORT 12220 /* Not IANA registered */
31 #define LWAPP_UDP_PORT_RANGE "12222-12223" /* Not IANA registered */
33 #define LWAPP_FLAGS_T 0x04
34 #define LWAPP_FLAGS_F 0x02
35 #define LWAPP_FLAGS_FT 0x01
37 static int proto_lwapp
;
38 static int proto_lwapp_l3
;
39 static int proto_lwapp_control
;
41 static int ett_lwapp_l3
;
42 static int ett_lwapp_flags
;
43 static int ett_lwapp_control
;
45 static int hf_lwapp_version
;
46 static int hf_lwapp_slotid
;
47 static int hf_lwapp_flags
;
48 static int hf_lwapp_flags_type
;
49 static int hf_lwapp_flags_fragment
;
50 static int hf_lwapp_flags_fragment_type
;
51 static int hf_lwapp_fragment_id
;
52 static int hf_lwapp_length
;
53 static int hf_lwapp_rssi
;
54 static int hf_lwapp_snr
;
55 /* static int hf_lwapp_control; */
56 static int hf_lwapp_control_mac
;
57 static int hf_lwapp_control_type
;
58 static int hf_lwapp_control_seq_no
;
59 static int hf_lwapp_control_length
;
61 #define LWAPP_MAX_NESTED_ENCAP 10
63 static expert_field ei_lwapp_too_many_encap
;
65 static dissector_handle_t eth_withoutfcs_handle
;
66 static dissector_handle_t wlan_handle
;
67 static dissector_handle_t wlan_bsfc_handle
;
69 /* Set by preferences */
70 static bool swap_frame_control
;
101 OPERATION_RATE_SET_PAYLOAD
,
102 MULTI_DOMAIN_CAPABILITY_PAYLOAD
,
103 MAC_OPERATION_PAYLOAD
,
104 PHY_TX_POWER_PAYLOAD
,
105 PHY_TX_POWER_LEVEL_PAYLOAD
,
108 SUPPORTED_RATES_PAYLOAD
,
111 RRM_NEIGHBOR_CTRL_PAYLOAD
,
112 RRM_NOISE_CTRL_PAYLOAD
,
113 RRM_NOISE_DATA_PAYLOAD
,
114 RRM_INTERFERENCE_CTRL_PAYLOAD
,
115 RRM_INTERFERENCE_DATA_PAYLOAD
,
116 RRM_LOAD_CTRL_PAYLOAD
,
117 RRM_LOAD_DATA_PAYLOAD
,
118 CHANGE_STATE_EVENT_PAYLOAD
,
122 DELETE_MOBILE_PAYLOAD
127 DISCOVERY_REQUEST
= 1,
139 CONFIGURE_COMMAND_RES
,
143 CHANGE_STATE_EVENT_RES
,
158 PRIMARY_DISCOVERY_REQ
,
159 PRIMARY_DISCOVERY_RES
,
162 RESET_REQ_CLEAR_CONFIG
165 static const value_string control_msg_vals
[] = {
166 {DISCOVERY_REQUEST
, "DISCOVERY_REQUEST"},
167 {DISCOVERY_REPLY
, "DISCOVERY_REPLY"},
168 {JOIN_REQUEST
, "JOIN_REQUEST"},
169 {JOIN_REPLY
, "JOIN_REPLY"},
170 {HANDOFF_REQUEST
, "HANDOFF_REQUEST"},
171 {HANDOFF_REPLY
, "HANDOFF_REPLY"},
172 {HANDOFF_COMMAND
, "HANDOFF_COMMAND"},
173 {HANDOFF_RESPONSE
, "HANDOFF_RESPONSE"},
174 {HANDOFF_CONFIRM
, "HANDOFF_CONFIRM"},
175 {CONFIGURE_REQUEST
, "CONFIGURE_REQUEST"},
176 {CONFIGURE_RESPONSE
, "CONFIGURE_RESPONSE"},
177 {CONFIGURE_COMMAND
, "CONFIGURE_COMMAND"},
178 {CONFIGURE_COMMAND_RES
, "CONFIGURE_COMMAND_RES"},
179 {STATISTICS_INFO
, "STATISTICS_INFO"},
180 {STATISTICS_INFO_RES
, "STATISTICS_INFO_RES"},
181 {CHANGE_STATE_EVENT
, "CHANGE_STATE_EVENT"},
182 {CHANGE_STATE_EVENT_RES
, "CHANGE_STATE_EVENT_RES"},
183 {RRM_CONTROL_REQ
, "RRM_CONTROL_REQ"},
184 {RRM_CONTROL_RES
, "RRM_CONTROL_RES"},
185 {RRM_DATA_REQ
, "RRM_DATA_REQ"},
186 {RRM_DATA_RES
, "RRM_DATA_RES"},
187 {ECHO_REQUEST
, "ECHO_REQUEST"},
188 {ECHO_RESPONSE
, "ECHO_RESPONSE"},
189 {IMAGE_DATA
, "IMAGE_DATA"},
190 {IMAGE_DATA_RES
, "IMAGE_DATA_RES"},
191 {RESET_REQ
, "RESET_REQ"},
192 {RESET_RES
, "RESET_RES"},
193 {I_AM_UP_REQ
, "I_AM_UP_REQ"},
194 {I_AM_UP_RES
, "I_AM_UP_RES"},
195 {KEY_UPDATE_REQ
, "KEY_UPDATE_REQ"},
196 {KEY_UPDATE_RES
, "KEY_UPDATE_RES"},
197 {PRIMARY_DISCOVERY_REQ
, "PRIMARY_DISCOVERY_REQ"},
198 {PRIMARY_DISCOVERY_RES
, "PRIMARY_DISCOVERY_RES"},
199 {DATA_TRANSFER
, "DATA_TRANSFER"},
200 {DATA_TRANSFER_RES
, "DATA_TRANSFER_RES"},
201 {RESET_REQ_CLEAR_CONFIG
, "RESET_REQ_CLEAR_CONFIG"},
205 static value_string_ext control_msg_vals_ext
= VALUE_STRING_EXT_INIT(control_msg_vals
);
208 static const value_string control_tag_vals
[] = {
210 {RESULT_CODE
, "RESULT_CODE"},
211 {MWAR_ADDR_PAYLOAD
, "MWAR_ADDR_PAYLOAD"},
212 {RAD_PAYLOAD
, "RAD_PAYLOAD"},
213 {RAD_SLOT_PAYLOAD
, "RAD_SLOT_PAYLOAD"},
214 {RAD_NAME_PAYLOAD
, "RAD_NAME_PAYLOAD"},
215 {MWAR_PAYLOAD
, "MWAR_PAYLOAD"},
216 {VAP_PAYLOAD
, "VAP_PAYLOAD"},
217 {STATION_CFG_PAYLOAD
, "STATION_CFG_PAYLOAD"},
218 {OPERATION_RATE_SET_PAYLOAD
, "OPERATION_RATE_SET_PAYLOAD"},
219 {MULTI_DOMAIN_CAPABILITY_PAYLOAD
, "MULTI_DOMAIN_CAPABILITY_PAYLOAD"},
220 {MAC_OPERATION_PAYLOAD
, "MAC_OPERATION_PAYLOAD"},
221 {PHY_TX_POWER_PAYLOAD
, "PHY_TX_POWER_PAYLOAD"},
222 {PHY_TX_POWER_LEVEL_PAYLOAD
, "PHY_TX_POWER_LEVEL_PAYLOAD"},
223 {PHY_DSSS_PAYLOAD
, "PHY_DSSS_PAYLOAD"},
224 {PHY_OFDM_PAYLOAD
, "PHY_OFDM_PAYLOAD"},
225 {SUPPORTED_RATES_PAYLOAD
, "SUPPORTED_RATES_PAYLOAD"},
226 {AUTH_PAYLOAD
, "AUTH_PAYLOAD"},
227 {TEST_PAYLOAD
, "TEST_PAYLOAD"},
228 {RRM_NEIGHBOR_CTRL_PAYLOAD
, "RRM_NEIGHBOR_CTRL_PAYLOAD"},
229 {RRM_NOISE_CTRL_PAYLOAD
, "RRM_NOISE_CTRL_PAYLOAD"},
230 {RRM_NOISE_DATA_PAYLOAD
, "RRM_NOISE_DATA_PAYLOAD"},
231 {RRM_INTERFERENCE_CTRL_PAYLOAD
, "RRM_INTERFERENCE_CTRL_PAYLOAD"},
232 {RRM_INTERFERENCE_DATA_PAYLOAD
, "RRM_INTERFERENCE_DATA_PAYLOAD"},
233 {RRM_LOAD_CTRL_PAYLOAD
, "RRM_LOAD_CTRL_PAYLOAD"},
234 {RRM_LOAD_DATA_PAYLOAD
, "RRM_LOAD_DATA_PAYLOAD"},
235 {CHANGE_STATE_EVENT_PAYLOAD
, "CHANGE_STATE_EVENT_PAYLOAD"},
236 {ADMIN_STATE_PAYLOAD
, "ADMIN_STATE_PAYLOAD"},
237 {DELETE_VAP_PAYLOAD
, "DELETE_VAP_PAYLOAD"},
238 {ADD_MOBILE_PAYLOAD
, "ADD_MOBILE_PAYLOAD"},
239 {DELETE_MOBILE_PAYLOAD
, "DELETE_MOBILE_PAYLOAD"},
242 static value_string_ext control_tag_vals_ext
= VALUE_STRING_EXT_INIT(control_tag_vals
);
245 static const true_false_string lwapp_flags_type
= {
246 "LWAPP Control Packet" ,
251 * dissect lwapp control packets. This is not fully implemented,
252 * but it's a good start.
255 dissect_control(tvbuff_t
*tvb
, packet_info
*pinfo
,
259 proto_tree
*control_tree
;
262 /* Set up structures needed to add the protocol subtree and manage it */
266 /* Make entries in Protocol column and Info column on summary display */
267 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "LWAPP");
268 col_set_str(pinfo
->cinfo
, COL_INFO
,
271 /* Copy our header */
272 tvb_memcpy(tvb
, (uint8_t*) &header
, offset
, sizeof(header
));
275 * Fix the length (network byte ordering), and set our version &
278 header
.length
= g_ntohs(header
.length
);
280 col_append_str(pinfo
->cinfo
, COL_INFO
,
281 val_to_str_ext(header
.type
, &control_msg_vals_ext
, "Bad Type: 0x%02x"));
283 /* In the interest of speed, if "tree" is NULL, don't do any work not
284 necessary to generate protocol tree items. */
286 /* create display subtree for the protocol */
287 ti
= proto_tree_add_item(tree
, proto_lwapp_control
, tvb
, offset
,
289 control_tree
= proto_item_add_subtree(ti
, ett_lwapp_control
);
291 proto_tree_add_uint(control_tree
, hf_lwapp_control_type
,
292 tvb
, offset
, 1, header
.type
);
295 proto_tree_add_uint(control_tree
, hf_lwapp_control_seq_no
,
296 tvb
, offset
, 1, header
.seqNo
);
299 proto_tree_add_uint(control_tree
, hf_lwapp_control_length
,
300 tvb
, offset
, 2, header
.length
);
303 /* Dissect rest of packet as data */
304 next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
305 call_data_dissector(next_tvb
, pinfo
, tree
);
308 } /* dissect_control */
311 * This lwapp dissector assumes that there is an 802.3 header at
312 * the start of the packet, so it simply re-calls the ethernet
313 * dissector on the packet.
316 dissect_lwapp_l3(tvbuff_t
*tvb
, packet_info
*pinfo
,
317 proto_tree
*tree
, void* data _U_
)
319 /* Set up structures needed to add the protocol subtree and manage it */
321 proto_tree
*lwapp_tree
;
323 tvbuff_t
*next_client
;
325 /* Make entries in Protocol column and Info column on summary display */
326 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "LWAPP-L3");
327 col_set_str(pinfo
->cinfo
, COL_INFO
, "802.3 Packets over Layer 3");
329 /* create display subtree for the protocol */
330 ti
= proto_tree_add_item(tree
, proto_lwapp_l3
, tvb
, offset
,
332 lwapp_tree
= proto_item_add_subtree(ti
, ett_lwapp_l3
);
334 /* Dissect as Ethernet */
335 next_client
= tvb_new_subset_remaining(tvb
, 0);
336 call_dissector(eth_withoutfcs_handle
, next_client
, pinfo
, lwapp_tree
);
338 return tvb_captured_length(tvb
);
340 } /* dissect_lwapp_l3*/
344 * This dissector dissects the lwapp protocol itself. It assumes an
345 * lwapp payload in the data, and doesn't care whether the data was
346 * from a UDP packet, or a Layer 2 one.
349 dissect_lwapp(tvbuff_t
*tvb
, packet_info
*pinfo
,
350 proto_tree
*tree
, void* data _U_
)
355 proto_tree
*lwapp_tree
;
356 tvbuff_t
*next_client
;
358 uint8_t have_destmac
=0;
359 static int * const flags
[] = {
360 &hf_lwapp_flags_type
,
361 &hf_lwapp_flags_fragment
,
362 &hf_lwapp_flags_fragment_type
,
365 unsigned encap_nested_count
;
367 /* Set up structures needed to add the protocol subtree and manage it */
371 /* Make entries in Protocol column and Info column on summary display */
372 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "LWAPP");
373 col_set_str(pinfo
->cinfo
, COL_INFO
,
374 "LWAPP IP or Layer 2");
376 /* First, set up our dest mac, if we're a control packet with a
377 * dest of port 12223 */
378 if (pinfo
->destport
== 12223 ) {
379 tvb_memcpy(tvb
, dest_mac
, offset
, 6);
382 /* Copy our header */
383 tvb_memcpy(tvb
, (uint8_t*) &header
, offset
+ 6, sizeof(header
));
386 /* Copy our header */
387 tvb_memcpy(tvb
, (uint8_t*) &header
, offset
, sizeof(header
));
392 * Fix the length (network byte ordering), and set our version &
395 header
.length
= g_ntohs(header
.length
);
396 version
= (header
.flags
& 0xc0) >> 6;
397 slotId
= (header
.flags
& 0x38) >> 3;
399 if ((header
.flags
& LWAPP_FLAGS_T
) != 0)
400 col_append_str(pinfo
->cinfo
, COL_INFO
,
403 col_append_str(pinfo
->cinfo
, COL_INFO
,
406 /* create display subtree for the protocol */
407 ti
= proto_tree_add_item(tree
, proto_lwapp
, tvb
, offset
, -1, ENC_NA
);
408 encap_nested_count
= GPOINTER_TO_UINT(p_get_proto_data(pinfo
->pool
, pinfo
, proto_lwapp
, 0));
409 if (++encap_nested_count
> LWAPP_MAX_NESTED_ENCAP
) {
410 expert_add_info(pinfo
, ti
, &ei_lwapp_too_many_encap
);
411 return tvb_captured_length(tvb
);
413 p_add_proto_data(pinfo
->pool
, pinfo
, proto_lwapp
, 0, GUINT_TO_POINTER(encap_nested_count
));
415 /* In the interest of speed, if "tree" is NULL, don't do any work not
416 necessary to generate protocol tree items. */
419 lwapp_tree
= proto_item_add_subtree(ti
, ett_lwapp
);
422 proto_tree_add_ether(lwapp_tree
, hf_lwapp_control_mac
, tvb
, offset
,
427 proto_tree_add_uint(lwapp_tree
, hf_lwapp_version
,
428 tvb
, offset
, 1, version
);
429 proto_tree_add_uint(lwapp_tree
, hf_lwapp_slotid
,
430 tvb
, offset
, 1, slotId
);
432 proto_tree_add_bitmask(lwapp_tree
, tvb
, offset
, hf_lwapp_flags
, ett_lwapp_flags
, flags
, ENC_NA
);
435 proto_tree_add_uint(lwapp_tree
, hf_lwapp_fragment_id
,
436 tvb
, offset
, 1, header
.fragmentId
);
439 proto_tree_add_uint(lwapp_tree
, hf_lwapp_length
,
440 tvb
, offset
, 2, header
.length
);
443 proto_tree_add_uint(lwapp_tree
, hf_lwapp_rssi
,
444 tvb
, offset
, 1, header
.rssi
);
446 proto_tree_add_uint(lwapp_tree
, hf_lwapp_snr
,
447 tvb
, offset
, 1, header
.snr
);
453 next_client
= tvb_new_subset_remaining(tvb
, (have_destmac
?6:0) + (int)sizeof(LWAPP_Header
));
454 if ((header
.flags
& LWAPP_FLAGS_T
) == 0) {
455 call_dissector(swap_frame_control
? wlan_bsfc_handle
: wlan_handle
,
456 next_client
, pinfo
, tree
);
458 dissect_control(next_client
, pinfo
, tree
);
460 return tvb_captured_length(tvb
);
464 /* registration with the filtering engine */
466 proto_register_lwapp(void)
468 static hf_register_info hf
[] = {
470 { "Version", "lwapp.version", FT_UINT8
, BASE_DEC
, NULL
, 0x00,
473 { "slotId","lwapp.slotId", FT_UINT24
, BASE_DEC
, NULL
, 0x0,
476 { "Flags", "lwapp.flags", FT_UINT8
, BASE_HEX
,
477 NULL
, 0x0, NULL
, HFILL
}},
478 { &hf_lwapp_flags_type
,
479 { "Type", "lwapp.flags.type", FT_BOOLEAN
, 8,
480 TFS(&lwapp_flags_type
), LWAPP_FLAGS_T
, NULL
, HFILL
}},
481 { &hf_lwapp_flags_fragment
,
482 { "Fragment", "lwapp.flags.fragment", FT_BOOLEAN
, 8,
483 TFS(&tfs_set_notset
), LWAPP_FLAGS_F
,
485 { &hf_lwapp_flags_fragment_type
,
486 { "Fragment Type", "lwapp.flags.fragmentType", FT_BOOLEAN
, 8,
487 TFS(&tfs_set_notset
), LWAPP_FLAGS_FT
,
489 { &hf_lwapp_fragment_id
,
490 { "Fragment Id","lwapp.fragmentId", FT_UINT8
, BASE_HEX
,
491 NULL
, 0x0, NULL
, HFILL
}},
493 { "Length","lwapp.Length", FT_UINT16
, BASE_DEC
,
494 NULL
, 0x0, NULL
, HFILL
}},
496 { "RSSI","lwapp.rssi", FT_UINT8
, BASE_HEX
,
497 NULL
, 0x0, NULL
, HFILL
}},
499 { "SNR","lwapp.snr", FT_UINT8
, BASE_HEX
,
500 NULL
, 0x0, NULL
, HFILL
}},
503 { "Control Data (not dissected yet)","lwapp.control", FT_BYTES
, BASE_NONE
,
504 NULL
, 0x0, NULL
, HFILL
}},
506 { &hf_lwapp_control_mac
,
507 { "AP Identity", "lwapp.apid", FT_ETHER
, BASE_NONE
, NULL
, 0x0,
508 "Access Point Identity", HFILL
}},
509 { &hf_lwapp_control_type
,
510 { "Control Type", "lwapp.control.type", FT_UINT8
, BASE_DEC
|BASE_EXT_STRING
, &control_msg_vals_ext
, 0x00,
512 { &hf_lwapp_control_seq_no
,
513 { "Control Sequence Number", "lwapp.control.seqno", FT_UINT8
, BASE_DEC
, NULL
, 0x00,
515 { &hf_lwapp_control_length
,
516 { "Control Length","lwapp.control.length", FT_UINT16
, BASE_DEC
,
517 NULL
, 0x0, NULL
, HFILL
}},
519 static int *ett
[] = {
525 static ei_register_info ei
[] = {
526 { &ei_lwapp_too_many_encap
, { "lwapp.too_many_encap", PI_UNDECODED
, PI_WARN
, "Too many LWAPP encapsulation levels", EXPFILL
}}
528 module_t
*lwapp_module
;
529 expert_module_t
* expert_lwapp
;
531 proto_lwapp
= proto_register_protocol ("LWAPP Encapsulated Packet", "LWAPP", "lwapp");
533 proto_lwapp_l3
= proto_register_protocol_in_name_only ("LWAPP Layer 3 Packet", "LWAPP-L3", "lwapp-l3", proto_lwapp
, FT_PROTOCOL
);
535 proto_lwapp_control
= proto_register_protocol_in_name_only ("LWAPP Control Message", "LWAPP-CNTL", "lwapp-cntl", proto_lwapp
, FT_PROTOCOL
);
536 proto_register_field_array(proto_lwapp
, hf
, array_length(hf
));
537 proto_register_subtree_array(ett
, array_length(ett
));
538 expert_lwapp
= expert_register_protocol(proto_lwapp
);
539 expert_register_field_array(expert_lwapp
, ei
, array_length(ei
));
541 lwapp_module
= prefs_register_protocol(proto_lwapp
, NULL
);
543 prefs_register_bool_preference(lwapp_module
,"swap_fc","Swap Frame Control",
544 "Swap frame control bytes (needed for some APs).",
545 &swap_frame_control
);
547 /* This dissector assumes lwapp packets in an 802.3 frame */
548 lwapp_l3_handle
= register_dissector("lwapp-l3", dissect_lwapp_l3
, proto_lwapp_l3
);
550 /* This dissector assumes a lwapp packet */
551 lwapp_handle
= register_dissector("lwapp", dissect_lwapp
, proto_lwapp
);
555 proto_reg_handoff_lwapp(void)
558 * Get handles for the Ethernet and wireless dissectors.
560 eth_withoutfcs_handle
= find_dissector_add_dependency("eth_withoutfcs", proto_lwapp
);
561 wlan_handle
= find_dissector_add_dependency("wlan_withoutfcs", proto_lwapp
);
562 wlan_bsfc_handle
= find_dissector_add_dependency("wlan_bsfc", proto_lwapp
);
565 * Ok, the following deserves some comments. We have four
566 * different ways lwapp can appear on the wire. Mostly, this is
567 * because lwapp is such a new protocol.
569 * First, lwapp can join on multiple udp ports, as encapsulated
570 * packets on top of UDP. In this case, there is a full raw
571 * ethernet frame inside of the UDP packet. This method is
572 * becoming obscelete, but we still wanted to dissect the
575 * Next, lwapp can be over UDP, but packaged for L3 tunneling. This
576 * is the new-style. In this case, LWAP headers are just transmitted
579 * The last method is lwapp directly over layer 2. For this, we
580 * dissect two different ethertypes (until IANA gives us one)
584 /* Obsoleted LWAPP via encapsulated 802.3 over UDP */
585 dissector_add_uint_with_preference("udp.port", LWAPP_8023_PORT
, lwapp_l3_handle
);
587 /* new-style lwapp directly over UDP: L3-lwapp*/
588 dissector_add_uint_range_with_preference("udp.port", LWAPP_UDP_PORT_RANGE
, lwapp_handle
);
591 dissector_add_uint("ethertype", 0x88bb, lwapp_handle
);
592 dissector_add_uint("ethertype", 0xbbbb, lwapp_handle
);
597 * Editor modelines - https://www.wireshark.org/tools/modelines.html
602 * indent-tabs-mode: nil
605 * vi: set shiftwidth=4 tabstop=8 expandtab:
606 * :indentSize=4:tabSize=8:noTabs=true: