QSIG fully implemented
[obnox/wireshark/wip.git] / epan / dissectors / packet-iapp.c
1 /* packet-iapp.c
2  * Routines for IAPP dissection
3  * Copyright 2002, Alfred Arnold <aarnold@elsa.de>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 #include <glib.h>
36
37 #include <epan/packet.h>
38 #include <epan/oui.h>
39
40 /* Initialize the protocol and registered fields */
41 static int proto_iapp = -1;
42 static int hf_iapp_version = -1;
43 static int hf_iapp_type = -1;
44
45 /* Initialize the subtree pointers */
46 static gint ett_iapp = -1;
47 static gint ett_iapp_pdu = -1;
48 static gint ett_iapp_cap = -1;
49 static gint ett_iapp_auth = -1;
50
51 #define UDP_PORT_IAPP     2313
52
53 #define IAPP_ANNOUNCE_REQUEST  0
54 #define IAPP_ANNOUNCE_RESPONSE 1
55 #define IAPP_HANDOVER_REQUEST 2
56 #define IAPP_HANDOVER_RESPONSE 3
57
58 #define IAPP_PDU_SSID 0
59 #define IAPP_PDU_BSSID 1
60 #define IAPP_PDU_OLDBSSID 2
61 #define IAPP_PDU_MSADDR 3
62 #define IAPP_PDU_CAPABILITY 4
63 #define IAPP_PDU_ANNOUNCEINT 5
64 #define IAPP_PDU_HOTIMEOUT 6
65 #define IAPP_PDU_MESSAGEID 7
66 #define IAPP_PDU_PHYTYPE 0x10
67 #define IAPP_PDU_REGDOMAIN 0x11
68 #define IAPP_PDU_CHANNEL 0x12
69 #define IAPP_PDU_BEACONINT 0x13
70 #define IAPP_PDU_OUIIDENT 0x80
71 #define IAPP_PDU_AUTHINFO 0x81
72
73 #define IAPP_CAP_FORWARDING 0x40
74 #define IAPP_CAP_WEP 0x20
75
76 #define IAPP_PHY_PROP 0x00
77 #define IAPP_PHY_FHSS 0x01
78 #define IAPP_PHY_DSSS 0x02
79 #define IAPP_PHY_IR 0x03
80 #define IAPP_PHY_OFDM 0x04
81
82 #define IAPP_DOM_FCC 0x10
83 #define IAPP_DOM_IC 0x20
84 #define IAPP_DOM_ETSI 0x30
85 #define IAPP_DOM_SPAIN 0x31
86 #define IAPP_DOM_FRANCE 0x32
87 #define IAPP_DOM_MKK 0x40
88
89 #define IAPP_AUTH_STATUS 0x01
90 #define IAPP_AUTH_USERNAME 0x02
91 #define IAPP_AUTH_PROVNAME 0x03
92 #define IAPP_AUTH_RXPKTS 0x04
93 #define IAPP_AUTH_TXPKTS 0x05
94 #define IAPP_AUTH_RXBYTES 0x06
95 #define IAPP_AUTH_TXBYTES 0x07
96 #define IAPP_AUTH_LOGINTIME 0x08
97 #define IAPP_AUTH_TIMELIMIT 0x09
98 #define IAPP_AUTH_VOLLIMIT 0x0a
99 #define IAPP_AUTH_ACCCYCLE 0x0b
100 #define IAPP_AUTH_RXGWORDS 0x0c
101 #define IAPP_AUTH_TXGWORDS 0x0d
102 #define IAPP_AUTH_IPADDR 0x0e
103 #define IAPP_AUTH_TRAILER 0xff
104
105
106 typedef struct _e_iapphdr {
107         guint8 ia_version;
108         guint8 ia_type;
109 } e_iapphdr;
110
111 typedef struct _e_pduhdr {
112         guint8 pdu_type;
113         guint8 pdu_len_h;
114         guint8 pdu_len_l;
115 } e_pduhdr;
116
117 static value_string iapp_vals[] = {
118   {IAPP_ANNOUNCE_REQUEST, "Announce Request"},
119   {IAPP_ANNOUNCE_RESPONSE, "Announce Response"},
120   {IAPP_HANDOVER_REQUEST, "Handover Request"},
121   {IAPP_HANDOVER_RESPONSE, "Handover Response"},
122   {0, NULL}};
123
124 static value_string iapp_pdu_type_vals[] = {
125   {IAPP_PDU_SSID, "Network Name"},
126   {IAPP_PDU_BSSID, "BSSID"},
127   {IAPP_PDU_OLDBSSID, "Old BSSID"},
128   {IAPP_PDU_MSADDR, "Mobile Station Address"},
129   {IAPP_PDU_CAPABILITY, "Capabilities"},
130   {IAPP_PDU_ANNOUNCEINT, "Announce Interval"},
131   {IAPP_PDU_HOTIMEOUT, "Handover Timeout"},
132   {IAPP_PDU_MESSAGEID, "Message ID"},
133   {IAPP_PDU_PHYTYPE, "PHY Type"},
134   {IAPP_PDU_REGDOMAIN, "Regulatory Domain"},
135   {IAPP_PDU_CHANNEL, "Radio Channel"},
136   {IAPP_PDU_BEACONINT, "Beacon Interval"},
137   {IAPP_PDU_OUIIDENT, "OUI Identifier"},
138   {IAPP_PDU_AUTHINFO, "ELSA Authentication Info"},
139   {0, NULL}};
140
141 static value_string iapp_cap_vals[] = {
142   {IAPP_CAP_FORWARDING, "Forwarding"},
143   {IAPP_CAP_WEP, "WEP"},
144   {0, NULL}};
145
146 static value_string iapp_phy_vals[] = {
147   {IAPP_PHY_PROP, "Proprietary"},
148   {IAPP_PHY_FHSS, "FHSS"},
149   {IAPP_PHY_DSSS, "DSSS"},
150   {IAPP_PHY_IR, "Infrared"},
151   {IAPP_PHY_OFDM, "OFDM"},
152   {0, NULL}};
153
154 static value_string iapp_dom_vals[] = {
155   {IAPP_DOM_FCC, "FCC (USA)"},
156   {IAPP_DOM_IC, "IC (Canada)"},
157   {IAPP_DOM_ETSI, "ETSI (Europe)"},
158   {IAPP_DOM_SPAIN, "Spain"},
159   {IAPP_DOM_FRANCE, "France"},
160   {IAPP_DOM_MKK, "MKK (Japan)"},
161   {0, NULL}};
162
163 static value_string iapp_auth_type_vals[] = {
164   {IAPP_AUTH_STATUS, "Status"},
165   {IAPP_AUTH_USERNAME, "User Name"},
166   {IAPP_AUTH_PROVNAME, "Provider Name"},
167   {IAPP_AUTH_RXPKTS, "Received Packets"},
168   {IAPP_AUTH_TXPKTS, "Transmitted Packets"},
169   {IAPP_AUTH_RXBYTES, "Received Octets"},
170   {IAPP_AUTH_TXBYTES, "Transmitted Octets"},
171   {IAPP_AUTH_LOGINTIME, "Session Time"},
172   {IAPP_AUTH_TIMELIMIT, "Time Limit"},
173   {IAPP_AUTH_VOLLIMIT, "Volume Limit"},
174   {IAPP_AUTH_ACCCYCLE, "Accounting Cycle"},
175   {IAPP_AUTH_TRAILER, "Authenticator"},
176   {IAPP_AUTH_RXGWORDS, "Received Gigawords"},
177   {IAPP_AUTH_TXGWORDS, "Transmitted Gigawords"},
178   {IAPP_AUTH_IPADDR, "Client IP Address"},
179   {0, NULL}};
180
181
182 /* dissect a capability bit field */
183
184 static void dissect_caps(proto_item *pitem, tvbuff_t *tvb, int offset)
185 {
186         proto_tree *captree;
187         int bit, val, thisbit;
188         const gchar *strval;
189         gchar bitval[4+1+4+1];  /* "xxxx xxxx\0" */
190
191         captree = proto_item_add_subtree(pitem, ett_iapp_cap);
192         val = tvb_get_guint8(tvb, offset + 3);
193
194         for (bit = 7; bit >= 0; bit--)
195         {
196                 thisbit = 1 << bit;
197                 strval = match_strval(thisbit, iapp_cap_vals);
198                 if (strval)
199                 {
200                         other_decode_bitfield_value(bitval, val, thisbit, 8);
201                         proto_tree_add_text(captree, tvb, offset + 3, 1, "%s %s: %s",
202                                 bitval, strval, val & thisbit ? "Yes" : "No");
203                 }
204         }
205 }
206
207 static void
208 append_authval_str(proto_item *ti, int type, int len, tvbuff_t *tvb, int offset)
209 {
210         int z, val;
211
212         proto_item_append_text(ti, " Value: ");
213
214         switch (type)
215         {
216                 case IAPP_AUTH_STATUS:
217                         proto_item_append_text(ti, "%s", tvb_get_guint8(tvb, offset + 3) ? "Authenticated" : "Not authenticated");
218                         break;
219                 case IAPP_AUTH_USERNAME:
220                 case IAPP_AUTH_PROVNAME:
221                         proto_item_append_text(ti, "\"%s\"",
222                                 tvb_format_text(tvb, offset + 3, len));
223                         break;
224                 case IAPP_AUTH_RXPKTS:
225                 case IAPP_AUTH_TXPKTS:
226                 case IAPP_AUTH_RXBYTES:
227                 case IAPP_AUTH_TXBYTES:
228                 case IAPP_AUTH_RXGWORDS:
229                 case IAPP_AUTH_TXGWORDS:
230                 case IAPP_AUTH_VOLLIMIT:
231                         val = tvb_get_ntohl(tvb, offset + 3);
232                         proto_item_append_text(ti, "%d", val);
233                         break;
234                 case IAPP_AUTH_LOGINTIME:
235                 case IAPP_AUTH_TIMELIMIT:
236                 case IAPP_AUTH_ACCCYCLE:
237                         val = tvb_get_ntohl(tvb, offset + 3);
238                         proto_item_append_text(ti, "%d seconds", val);
239                         break;
240                 case IAPP_AUTH_IPADDR:
241                         proto_item_append_text(ti, "%s",
242                                 ip_to_str(tvb_get_ptr(tvb, offset + 3, 4)));
243                         break;
244                 case IAPP_AUTH_TRAILER:
245                         for (z = 0; z < len; z++)
246                                 proto_item_append_text(ti, "%s%02x", z ? " " : "",
247                                         tvb_get_guint8(tvb, offset + 3 + z));
248                         break;
249         }
250 }
251
252 /* dissect authentication info */
253
254 static void dissect_authinfo(proto_item *pitem, tvbuff_t *tvb, int offset, int sumlen)
255 {
256         proto_tree *authtree;
257         proto_item *ti;
258         e_pduhdr pduhdr;
259         int len;
260
261         authtree = proto_item_add_subtree(pitem, ett_iapp_auth);
262
263         while (sumlen > 0)
264         {
265                 tvb_memcpy(tvb, (guint8 *)&pduhdr, offset, sizeof(e_pduhdr));
266                 len = (((int)pduhdr.pdu_len_h) << 8) + pduhdr.pdu_len_l;
267
268                 ti = proto_tree_add_text(authtree, tvb, offset, len + 3,
269                         "%s(%d)",
270                         val_to_str(pduhdr.pdu_type, iapp_auth_type_vals,
271                                 "Unknown PDU Type"),
272                         pduhdr.pdu_type);
273                 append_authval_str(ti, pduhdr.pdu_type, len, tvb, offset);
274
275                 sumlen -= (len + 3);
276                 offset += (len + 3);
277         }
278 }
279
280 /* get displayable values of PDU contents */
281
282 static gboolean
283 append_pduval_str(proto_item *ti, int type, int len, tvbuff_t *tvb, int offset,
284         gboolean is_fhss)
285 {
286         const guint8 *mac;
287         int z, val;
288         const gchar *strval;
289
290         proto_item_append_text(ti, " Value: ");
291
292         switch (type)
293         {
294                 case IAPP_PDU_SSID:
295                         proto_item_append_text(ti, "\"%s\"",
296                                 tvb_format_text(tvb, offset + 3, len));
297                         break;
298                 case IAPP_PDU_BSSID:
299                 case IAPP_PDU_OLDBSSID:
300                 case IAPP_PDU_MSADDR:
301                         mac = tvb_get_ptr(tvb, offset + 3, len);
302                         for (z = 0; z < len; z++)
303                                 proto_item_append_text(ti, "%s%02x", z ? ":" : "", mac[z]);
304                         break;
305                 case IAPP_PDU_CAPABILITY:
306                 {
307                         int mask, first = 1;
308
309                         val = tvb_get_guint8(tvb, offset + 3);
310                         proto_item_append_text(ti, "%02x (", val);
311                         for (mask = 0x80; mask; mask >>= 1)
312                                 if (val & mask)
313                                 {
314                                         strval = match_strval(mask, iapp_cap_vals);
315                                         if (strval)
316                                         {
317                                                 if (!first)
318                                                         proto_item_append_text(ti, " ");
319                                                 proto_item_append_text(ti, "%s", strval);
320                                                 first=0;
321                                         }
322                                 }
323                         proto_item_append_text(ti, ")");
324                         break;
325                 }
326                 case IAPP_PDU_ANNOUNCEINT:
327                         val = tvb_get_ntohs(tvb, offset + 3);
328                         proto_item_append_text(ti, "%d seconds", val);
329                         break;
330                 case IAPP_PDU_HOTIMEOUT:
331                 case IAPP_PDU_BEACONINT:
332                         val = tvb_get_ntohs(tvb, offset + 3);
333                         proto_item_append_text(ti, "%d Kus", val);
334                         break;
335                 case IAPP_PDU_MESSAGEID:
336                         val = tvb_get_ntohs(tvb, offset + 3);
337                         proto_item_append_text(ti, "%d", val);
338                         break;
339                 case IAPP_PDU_PHYTYPE:
340                         val = tvb_get_guint8(tvb, offset + 3);
341                         strval = val_to_str(val, iapp_phy_vals, "Unknown");
342                         proto_item_append_text(ti, "%s", strval);
343                         is_fhss = (val == IAPP_PHY_FHSS);
344                         break;
345                 case IAPP_PDU_REGDOMAIN:
346                         val = tvb_get_guint8(tvb, offset + 3);
347                         strval = val_to_str(val, iapp_dom_vals, "Unknown");
348                         proto_item_append_text(ti, "%s", strval);
349                         break;
350                 case IAPP_PDU_CHANNEL:
351                         val = tvb_get_guint8(tvb, offset + 3);
352                         if (is_fhss)
353                                 proto_item_append_text(ti, "Pattern set %d, sequence %d",
354                                                 ((val >> 6) & 3) + 1, (val & 31) + 1);
355                         else
356                                 proto_item_append_text(ti, "%d", val);
357                         break;
358                 case IAPP_PDU_OUIIDENT:
359                         for (val = z = 0; z < 3; z++)
360                                 val = (val << 8) | tvb_get_guint8(tvb, offset + 3 + z);
361                         strval = val_to_str(val, oui_vals, "Unknown");
362                         proto_item_append_text(ti, "%s", strval);
363                         break;
364         }
365         return is_fhss;
366 }
367
368 /* code to dissect a list of PDUs */
369
370 static void
371 dissect_pdus(tvbuff_t *tvb, int offset, proto_tree *pdutree, int pdulen)
372 {
373         e_pduhdr pduhdr;
374         int len;
375         proto_item *ti;
376         gboolean is_fhss;
377
378         if (!pdulen)
379         {
380                 proto_tree_add_text(pdutree, tvb, offset, 0, "No PDUs found");
381                 return;
382         }
383
384         is_fhss = FALSE;
385         while (pdulen > 0)
386         {
387                 tvb_memcpy(tvb, (guint8 *)&pduhdr, offset, sizeof(e_pduhdr));
388                 len = (((int)pduhdr.pdu_len_h) << 8) + pduhdr.pdu_len_l;
389
390                 ti = proto_tree_add_text(pdutree, tvb, offset, len + 3,
391                         "%s(%d)",
392                         val_to_str(pduhdr.pdu_type, iapp_pdu_type_vals,
393                                 "Unknown PDU Type"),
394                         pduhdr.pdu_type);
395                 is_fhss = append_pduval_str(ti, pduhdr.pdu_type, len, tvb,
396                         offset, is_fhss);
397
398                 if (pduhdr.pdu_type == IAPP_PDU_CAPABILITY)
399                         dissect_caps(ti, tvb, offset);
400
401                 if (pduhdr.pdu_type == IAPP_PDU_AUTHINFO)
402                         dissect_authinfo(ti, tvb, offset + 3, len);
403
404                 pdulen -= (len + 3);
405                 offset += (len + 3);
406         }
407 }
408
409 /* code to dissect an IAPP packet */
410 static void
411 dissect_iapp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
412 {
413         proto_item *ti, *pdutf;
414         proto_tree *iapp_tree, *pdutree;
415         e_iapphdr ih;
416         int ia_version;
417         int ia_type;
418         const gchar *codestrval;
419
420         if (check_col(pinfo->cinfo, COL_PROTOCOL))
421                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IAPP");
422
423         if (check_col(pinfo->cinfo, COL_INFO))
424                 col_clear(pinfo->cinfo, COL_INFO);
425
426         tvb_memcpy(tvb, (guint8 *)&ih, 0, sizeof(e_iapphdr));
427
428         ia_version = (int)ih.ia_version;
429         ia_type = (int)ih.ia_type;
430         codestrval = val_to_str(ia_type, iapp_vals,  "Unknown Packet");
431         if (check_col(pinfo->cinfo, COL_INFO))
432         {
433                 col_add_fstr(pinfo->cinfo, COL_INFO, "%s(%d) (version=%d)",
434                              codestrval, ia_type, ia_version);
435         }
436
437         if (tree)
438         {
439                 ti = proto_tree_add_item(tree, proto_iapp, tvb, 0, -1, FALSE);
440                 iapp_tree = proto_item_add_subtree(ti, ett_iapp);
441
442                 /* common header for all IAPP frames */
443
444                 proto_tree_add_uint(iapp_tree, hf_iapp_version, tvb, 0, 1,
445                         ih.ia_version);
446                 proto_tree_add_uint_format(iapp_tree, hf_iapp_type, tvb, 1, 1,
447                         ih.ia_type, "Type: %s(%d)", codestrval, ia_type);
448
449                 pdutf = proto_tree_add_text(iapp_tree, tvb, 2, -1,
450                                 "Protocol data units");
451                 pdutree = proto_item_add_subtree(pdutf, ett_iapp_pdu);
452
453                 if (pdutree)
454                 {
455                         dissect_pdus(tvb, 2, pdutree,
456                                 tvb_length_remaining(tvb, 2));
457                 }
458         }
459 }
460
461
462 /* Register the protocol with Wireshark */
463
464 /* this format is require because a script is used to build the C function
465    that calls all the protocol registration.
466 */
467
468 void
469 proto_register_iapp(void)
470 {
471
472 /* Setup list of header fields  See Section 1.6.1 for details*/
473         static hf_register_info hf[] = {
474                 { &hf_iapp_version,
475                         { "Version", "iapp.version", FT_UINT8, BASE_DEC, NULL, 0x00, "", HFILL }
476                 },
477                 { &hf_iapp_type,
478                         { "type", "iapp.type", FT_UINT8, BASE_DEC, NULL, 0x00, "", HFILL }
479                 },
480         };
481
482 /* Setup protocol subtree array */
483         static gint *ett[] = {
484                 &ett_iapp,
485                 &ett_iapp_pdu,
486                 &ett_iapp_cap,
487                 &ett_iapp_auth
488         };
489
490 /* Register the protocol name and description */
491         proto_iapp = proto_register_protocol("Inter-Access-Point Protocol",
492             "IAPP", "iapp");
493
494 /* Required function calls to register the header fields and subtrees used */
495         proto_register_field_array(proto_iapp, hf, array_length(hf));
496         proto_register_subtree_array(ett, array_length(ett));
497 }
498
499
500 /* If this dissector uses sub-dissector registration add a registration routine.
501    This format is required because a script is used to find these routines and
502    create the code that calls these routines.
503 */
504 void
505 proto_reg_handoff_iapp(void)
506 {
507         dissector_handle_t iapp_handle;
508
509         iapp_handle = create_dissector_handle(dissect_iapp, proto_iapp);
510         dissector_add("udp.port", UDP_PORT_IAPP, iapp_handle);
511 }