a985746dadcb69aa6bfddfef341a81275ea12924
[obnox/wireshark/wip.git] / plugins / opsi / packet-opsi.c
1 /* packet-opsi.c
2  * Routines for OPSI protocol dissection
3  * Copyright 2004, Laurent Rabret (France Telecom R&D) <laurent.rabret@i.hate.spams.org>
4  *
5  * $Id$
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
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
34 #include <glib.h>
35
36 #include <epan/packet.h>
37 #include <epan/emem.h>
38 #include <epan/dissectors/packet-tcp.h>
39 #include <epan/prefs.h>
40
41 #include "packet-opsi.h"
42
43 /* Initialize the protocol and registered fields */
44 static int proto_opsi                   = -1;
45 static int hf_opsi_major_version        = -1;
46 static int hf_opsi_minor_version        = -1;
47 static int hf_opsi_opcode               = -1;
48 static int hf_opsi_hook_id              = -1;
49 static int hf_opsi_length               = -1;
50 static int hf_opsi_session_id           = -1;
51 static int hf_user_name_att             = -1;
52 static int hf_password_att              = -1;
53 static int hf_chap_password_att         = -1;
54 static int hf_nas_ip_add_att            = -1;
55 static int hf_nas_port_att              = -1;
56 static int hf_service_type_att          = -1;
57 static int hf_framed_protocol_att       = -1;
58 static int hf_framed_address_att        = -1;
59 static int hf_framed_netmask_att        = -1;
60 static int hf_framed_routing_att        = -1;
61 static int hf_framed_filter_att         = -1;
62 static int hf_framed_mtu_att            = -1;
63 static int hf_framed_compression_att    = -1;
64 static int hf_called_station_att        = -1;
65 static int hf_calling_station_att       = -1;
66 static int hf_nas_identifier_att        = -1;
67 static int hf_accounting_att            = -1;
68 static int hf_acct_session_id_att       = -1;
69 static int hf_chap_challenge_att        = -1;
70 static int hf_nas_port_type_att         = -1;
71 static int hf_designation_num_att       = -1;
72 static int hf_nas_port_id_att           = -1;
73 static int hf_smc_aaa_id_att            = -1;
74 static int hf_smc_vpn_id_att            = -1;
75 static int hf_smc_vpn_name_att          = -1;
76 static int hf_smc_ran_id_att            = -1;
77 static int hf_smc_ran_ip_att            = -1;
78 static int hf_smc_ran_name_att          = -1;
79 static int hf_smc_pop_id_att            = -1;
80 static int hf_smc_pop_name_att          = -1;
81 static int hf_smc_id_att                = -1;
82 static int hf_smc_receive_time_att      = -1;
83 static int hf_smc_stat_time_att         = -1;
84 static int hf_opsi_flags_att            = -1;
85 static int hf_opsi_application_name_att = -1;
86
87 /* Initialize the subtree pointers */
88 static gint ett_opsi                    = -1;
89 static gint ett_opsi_user_name          = -1;
90 static gint ett_opsi_user_password      = -1;
91 static gint ett_opsi_chap_password      = -1;
92 static gint ett_opsi_nas_ip_address     = -1;
93 static gint ett_opsi_nas_port           = -1;
94 static gint ett_opsi_service_type       = -1;
95 static gint ett_opsi_framed_protocol    = -1;
96 static gint ett_opsi_framed_address     = -1;
97 static gint ett_opsi_framed_netmask     = -1;
98 static gint ett_opsi_framed_routing     = -1;
99 static gint ett_opsi_framed_filter      = -1;
100 static gint ett_opsi_framed_mtu         = -1;
101 static gint ett_opsi_framed_compression = -1;
102 static gint ett_opsi_called_station_id  = -1;
103 static gint ett_opsi_calling_station_id = -1;
104 static gint ett_opsi_nas_identifier     = -1;
105 static gint ett_opsi_accounting         = -1;
106 static gint ett_opsi_acct_session_id    = -1;
107 static gint ett_opsi_chap_challenge     = -1;
108 static gint ett_opsi_nas_port_type      = -1;
109 static gint ett_opsi_designation_number = -1;
110 static gint ett_opsi_nas_port_id        = -1;
111 static gint ett_opsi_smc_aaa_id         = -1;
112 static gint ett_opsi_smc_vpn_id         = -1;
113 static gint ett_opsi_smc_vpn_name       = -1;
114 static gint ett_opsi_smc_ran_id         = -1;
115 static gint ett_opsi_smc_ran_ip         = -1;
116 static gint ett_opsi_smc_ran_name       = -1;
117 static gint ett_opsi_smc_pop_id         = -1;
118 static gint ett_opsi_smc_pop_name       = -1;
119 static gint ett_opsi_smc_id             = -1;
120 static gint ett_opsi_smc_receive_time   = -1;
121 static gint ett_opsi_smc_stat_time      = -1;
122 static gint ett_opsi_flags              = -1;
123 static gint ett_opsi_application_name   = -1;
124
125
126 /* Code mapping */
127 static const value_string opsi_opcode[] = {
128                 { DISCOVER_REQUEST,     "Discover Request" },
129                 { DISCOVER_RESPONSE,    "Discover Response" },
130                 { SERVICE_REQUEST,      "Service Request" },
131                 { SERVICE_ACCEPT,       "Service Accept" }, 
132                 { SERVICE_REJECT,       "Service Reject" },
133                 { TERMINATE_REQUEST,    "Terminate Request" },
134                 { 0,                    NULL }
135         };
136         
137 static const value_string opsi_service_type_code[] = {  
138                 { 1, "Login" },
139                 { 2, "Framed" },
140                 { 3, "Callback Login" },
141                 { 4, "Callback Framed" },
142                 { 5, "Outbound" },
143                 { 6, "Administrative" },
144                 { 7, "NAS Prompt" },
145                 { 8, "Authenticate Only" },
146                 { 9, "Callback NAS Prompt" },
147                 { 0,                    NULL }
148         };
149
150 static const value_string opsi_framed_protocol_code[] = {       
151                 { 1,    "PPP" },
152                 { 2,    "SLIP" },
153                 { 3,    "AppleTalk Remote Access Protocol (ARAP)" },
154                 { 4,    "Gandalf proprietary SingleLink/MultiLink protocol" },
155                 { 5,    "Xylogics proprietary IPX/SLIP" },
156                 { 255,  "Ascend ARA" },
157                 { 256,  "MPP" },
158                 { 257,  "EURAW" },
159                 { 258,  "EUUI" },
160                 { 259,  "X25" },
161                 { 260,  "COMB" },
162                 { 261,  "FR" },
163                 { 262,  "MP" },
164                 { 263,  "FR-CIR"},
165                 { 0,                    NULL }
166         };
167         
168 static const value_string opsi_framed_routing_code[] = {
169                 { 0,    "None" },
170                 { 1,    "Broadcast" },
171                 { 2,    "Listen" },
172                 { 3,    "Broadcast-Listen" },
173                 { 4,    "Broadcast V2" },
174                 { 5,    "Listen V2" },
175                 { 6,    "Broadcast-Listen V2" },
176                 { 0,    NULL },
177         };
178
179 static const value_string opsi_framed_compression_code[] = {
180                 { 0,    "None" },
181                 { 1,    "Van Jacobsen TCP/IP" },
182                 { 2,    "IPX header compression" },
183                 { 0,    NULL }
184         };
185
186 static const value_string opsi_nas_port_type_code[] = { 
187                 { 0, "Async" },
188                 { 1, "Sync" },
189                 { 2, "ISDN Sync" },
190                 { 3, "ISDN Async V.120" },
191                 { 4, "ISDN Async V.110" },
192                 { 5, "Virtual" },
193                 { 6, "PIAFS" },
194                 { 7, "HDLC Clear Channel" },
195                 { 8, "X.25" },
196                 { 9, "X.75" },
197                 { 10, "G.3 Fax" },
198                 { 11, "SDSL - Symmetric DSL" },
199                 { 12, "ADSL-CAP - Asymmetric DSL, Carrierless Amplitude Phase Modulation" },
200                 { 13, "ADSL-DMT - Asymmetric DSL, Discrete Multi-Tone" },
201                 { 14, "IDSL - ISDN Digital Subscriber Line" },
202                 { 15, "Ethernet" },
203                 { 16, "xDSL - Digital Subscriber Line of unknown type" },
204                 { 17, "Cable" },
205                 { 18, "Wireless - Other" },
206                 { 19, "Wireless - IEEE 802.11" },
207                 { 201,"Voice over IP" },
208                 { 0,                    NULL }
209         };
210
211         
212 /* Structure used to decode OPSI frame attributes       */
213 /* CAUTION : it is compulsory to sort this array        */
214 /* (first argument of the opsi_attribute_handle_t)      */
215 /* in ascending order                                   */
216 /*                                                      */
217 static opsi_attribute_handle_t opsi_attributes[] = {
218         { USER_NAME_ATTRIBUTE,          /* 1 */
219         "User name attribute", &ett_opsi_user_name, &hf_user_name_att, decode_string_attribute },
220         { USER_PASSWD_ATTRIBUTE,        /* 2 */
221         "User password attribute" , &ett_opsi_user_password, &hf_password_att, decode_string_attribute },
222         { CHAP_PASSWD_ATTRIBUTE,        /* 3 */
223         "CHAP password attribute", &ett_opsi_chap_password, &hf_chap_password_att, decode_string_attribute },
224         { NAS_IP_ADDRESS_ATTRIBUTE,     /* 4 */
225         "NAS IP address attribute", &ett_opsi_nas_ip_address, &hf_nas_ip_add_att, decode_ipv4_attribute },
226         {NAS_PORT_ATTRIBUTE,            /* 5 */
227         "NAS port attribute", &ett_opsi_nas_port, &hf_nas_port_att, decode_longint_attribute },
228         {SERVICE_TYPE_ATTRIBUTE,        /* 6 */
229         "Service type attribute", &ett_opsi_service_type, &hf_service_type_att, decode_value_string_attribute },
230         {FRAMED_PROTOCOL_ATTRIBUTE,     /* 7 */
231         "Framed protocol attribute", &ett_opsi_framed_protocol, &hf_framed_protocol_att, decode_value_string_attribute },
232         {FRAMED_ADDRESS_ATTRIBUTE,      /* 8 */
233         "Framed address attribute", &ett_opsi_framed_address, &hf_framed_address_att, decode_ipv4_attribute },
234         {FRAMED_NETMASK_ATTRIBUTE,      /* 9 */
235         "Framed netmask attribute", &ett_opsi_framed_netmask, &hf_framed_netmask_att, decode_ipv4_attribute },
236         {FRAMED_ROUTING_ATTRIBUTE,      /* 10 */
237         "Framed routing attribute", &ett_opsi_framed_routing, &hf_framed_routing_att, decode_value_string_attribute },
238         {FRAMED_FILTER_ATTRIBUTE,       /* 11 */
239         "Framed filter attribute", &ett_opsi_framed_filter, &hf_framed_filter_att, decode_string_attribute },
240         {FRAMED_MTU_ATTRIBUTE,          /* 12 */
241         "Framed MTU attribute", &ett_opsi_framed_mtu, &hf_framed_mtu_att, decode_longint_attribute },
242         {FRAMED_COMPRESSION_ATTRIBUTE,  /* 13 */
243         "Framed compression attribute", &ett_opsi_framed_compression, &hf_framed_compression_att, decode_value_string_attribute },
244         {CALLED_STATION_ID_ATTRIBUTE,   /* 30 */
245         "Called station ID attribute", &ett_opsi_called_station_id, &hf_called_station_att, decode_string_attribute },
246         {CALLING_STATION_ID_ATTRIBUTE,  /* 31 */
247         "Calling station ID attribute", &ett_opsi_calling_station_id, &hf_calling_station_att, decode_string_attribute },
248         {NAS_IDENTIFIER,                /* 32 */
249         "NAS Identifier attribute", &ett_opsi_nas_identifier, &hf_nas_identifier_att, decode_string_attribute },
250         {ACCOUNTING_40_ATTRIBUTE,       /* 40 */
251         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
252         {ACCOUNTING_41_ATTRIBUTE,       /* 41 */
253         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
254         {ACCOUNTING_42_ATTRIBUTE,       /* 42 */
255         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
256         {ACCOUNTING_43_ATTRIBUTE,       /* 43 */
257         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
258         {ACCOUNTING_SESSION_ID_ATTRIBUTE,       /* 44 */ 
259         "Accounting session ID attribute", &ett_opsi_acct_session_id, &hf_acct_session_id_att, decode_string_attribute },
260         {ACCOUNTING_45_ATTRIBUTE,       /* 45 */
261         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
262         {ACCOUNTING_46_ATTRIBUTE,       /* 46 */
263         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
264         {ACCOUNTING_47_ATTRIBUTE,       /* 47 */
265         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
266         {ACCOUNTING_48_ATTRIBUTE,       /* 48 */
267         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
268         {ACCOUNTING_49_ATTRIBUTE,       /* 49 */
269         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
270         {ACCOUNTING_50_ATTRIBUTE,       /* 50 */
271         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
272         {ACCOUNTING_51_ATTRIBUTE,       /* 51 */
273         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
274         {ACCOUNTING_52_ATTRIBUTE,       /* 52 */
275         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
276         {ACCOUNTING_53_ATTRIBUTE,       /* 53 */
277         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
278         {ACCOUNTING_54_ATTRIBUTE,       /* 54 */
279         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
280         {ACCOUNTING_55_ATTRIBUTE,       /* 55 */
281         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
282         {ACCOUNTING_56_ATTRIBUTE,       /* 56 */
283         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
284         {ACCOUNTING_57_ATTRIBUTE,       /* 57 */
285         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
286         {ACCOUNTING_58_ATTRIBUTE,       /* 58 */
287         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
288         {ACCOUNTING_59_ATTRIBUTE,       /* 59 */
289         "Accounting attribute", &ett_opsi_accounting, &hf_accounting_att, decode_string_attribute },
290         {CHAP_CHALLENGE_ATTRIBUTE,      /* 60 */
291         "CHAP challenge",       &ett_opsi_chap_challenge, &hf_chap_challenge_att, decode_string_attribute },
292         {NAS_PORT_TYPE_ATTRIBUTE,       /* 61 */
293         "NAS port type attribute", &ett_opsi_nas_port_type, &hf_nas_port_type_att, decode_value_string_attribute },
294         {DESIGNATION_NUMBER_ATTRIBUTE,  /* 77 */
295         "Designation number attribute", &ett_opsi_designation_number, &hf_designation_num_att, decode_string_attribute },
296         {NAS_PORT_ID_ATTRIBUTE,         /* 87 */
297         "NAS port ID attribute", &ett_opsi_nas_port_id, &hf_nas_port_id_att, decode_string_attribute },
298         {SMC_AAAID_ATTRIBUTE,           /* 651 */
299         "SMC AAA ID attribute", &ett_opsi_smc_aaa_id, &hf_smc_aaa_id_att, decode_longint_attribute },
300         {SMC_VPNID_ATTRIBUTE,           /* 652 */
301         "SMC VPN ID attribute", &ett_opsi_smc_vpn_id, &hf_smc_vpn_id_att, decode_longint_attribute },
302         {SMC_VPNNAME_ATTRIBUTE,         /* 653 */
303         "SMC VPN name attribute", &ett_opsi_smc_vpn_name, &hf_smc_vpn_name_att, decode_string_attribute },
304         {SMC_RANID_ATTRIBUTE,           /* 654 */
305         "SMC RAN ID attribute", &ett_opsi_smc_ran_id, &hf_smc_ran_id_att, decode_longint_attribute },
306         {SMC_RANIP_ATTRIBUTE,           /* 655 */
307         "SMC RAN IP attribute", &ett_opsi_smc_ran_ip, &hf_smc_ran_ip_att, decode_ipv4_attribute },
308         {SMC_RANNAME_ATTRIBUTE,         /* 656 */
309         "SMC RAN name attribute", &ett_opsi_smc_ran_name, &hf_smc_ran_name_att, decode_string_attribute },
310         {SMC_POPID_ATTRIBUTE,           /* 657 */
311         "SMC POP ID attribute", &ett_opsi_smc_pop_id, &hf_smc_pop_id_att, decode_longint_attribute },
312         {SMC_POPNAME_ATTRIBUTE,         /* 658 */
313         "SMC POP name attribute", &ett_opsi_smc_pop_name, &hf_smc_pop_name_att, decode_string_attribute },
314         {SMC_SMCID_ATTRIBUTE,           /* 659 */
315         "SMC ID attribute", &ett_opsi_smc_id, &hf_smc_id_att, decode_longint_attribute },
316         {SMC_RECEIVE_TIME_ATTRIBUTE,    /* 660 */
317         "SMC receive time attribute", &ett_opsi_smc_receive_time, &hf_smc_receive_time_att, decode_time_attribute },
318         {SMC_STAT_TIME_ATTRIBUTE,       /* 661 */
319         "SMC stat time attribute", &ett_opsi_smc_stat_time, &hf_smc_stat_time_att, decode_longint_attribute },
320         {OPSI_FLAGS_ATTRIBUTE,          /* 674 */
321         "OPSI flags attribute", &ett_opsi_flags, &hf_opsi_flags_att, decode_longint_attribute },
322         {OPSI_APPLICATION_NAME_ATTRIBUTE,/* 675 */
323         "OPSI application name attribute", &ett_opsi_application_name, &hf_opsi_application_name_att, decode_string_attribute },
324
325 };
326 #define OPSI_ATTRIBUTES_COUNT (sizeof(opsi_attributes)/sizeof(opsi_attribute_handle_t))
327
328 /* Desegmentation of OPSI (over TCP) */
329 static gboolean opsi_desegment = TRUE;
330 /* To check if we must create or update the information column  */
331 static gboolean opsi_first;
332
333 void decode_string_attribute(tvbuff_t *tvb, proto_tree *tree, int* hfValue, int offset, int length)
334 {
335         guint8* pbuffer;
336         if (length < 4) {
337                 proto_tree_add_text(tree, tvb, offset, length, "Too short attribute!");
338                 return;
339         }
340         
341         pbuffer=tvb_get_string(tvb, offset+4, length-4);
342         proto_tree_add_string(tree, *hfValue, tvb, offset+4, length-4, pbuffer);
343         g_free(pbuffer);        
344 }
345
346
347 void decode_ipv4_attribute(tvbuff_t *tvb, proto_tree *tree, int* hfValue, int offset, int length)
348 {
349         guint32 ip_address;
350         if (length < 8) {
351                 proto_tree_add_text(tree, tvb, offset, length, "Too short attribute!");
352                 return;
353         }
354         ip_address = tvb_get_ipv4(tvb, offset+4);
355         proto_tree_add_ipv4(tree, *hfValue, tvb, offset+4, 4, ip_address);
356 }
357
358 void decode_longint_attribute(tvbuff_t *tvb, proto_tree *tree, int* hfValue, int offset, int length)
359 {
360         if (length < 8) {
361                 proto_tree_add_text(tree, tvb, offset, length, "Too short attribute!");
362                 return;
363         }
364         proto_tree_add_uint(tree, *hfValue, tvb, offset+4, 4, tvb_get_ntohl(tvb, offset+4));
365 }
366
367 void decode_value_string_attribute(tvbuff_t *tvb, proto_tree *tree, int* hfValue, int offset, int length)
368 {
369         if (length < 8) {
370                 proto_tree_add_text(tree, tvb, offset, length, "Too short attribute!");
371                 return;
372         }
373         proto_tree_add_item(tree, *hfValue, tvb, offset+4, 4, FALSE);
374 }
375
376 void decode_time_attribute(tvbuff_t *tvb, proto_tree *tree, int* hfValue, int offset, int length)
377 {
378         nstime_t ns;
379         
380         if (length < 8) {
381                 proto_tree_add_text(tree, tvb, offset, length, "Too short attribute!");
382                 return;
383         }
384       ns.secs  = tvb_get_ntohl(tvb, offset+4);
385       ns.nsecs = 0;
386       proto_tree_add_time(tree, *hfValue, tvb, offset+4, 4, &ns);
387 }
388
389 /****************************************************************************/
390 /********** End of attribute decoding ***************************************/
391 /****************************************************************************/
392
393 /* To find the correct size of the PDU. Needed by the desegmentation feature*/
394 static guint get_opsi_pdu_len(tvbuff_t *tvb, int offset)
395 {
396   /*
397    * Get the length of the OPSI packet.
398    * We are guaranteed there're enough chars in tvb in order to
399    * extract the length value. No exception thrown !!
400    */
401   return tvb_get_ntohs(tvb, offset + 4);
402 }
403
404 static int get_opsi_attribute_index(int min, int max, int attribute_type)
405 {
406         int middle, at;
407         
408         middle = (min+max)/2;
409         at = opsi_attributes[middle].attribute_type;
410         if (at == attribute_type) return middle;
411         if (attribute_type > at) {
412                 return (middle == max) ? -1 : get_opsi_attribute_index(middle+1, max, attribute_type);
413         }
414         return (middle == min) ? -1 : get_opsi_attribute_index(min, middle-1, attribute_type);
415 }
416
417
418 static void 
419 dissect_attributes(tvbuff_t *tvb, proto_tree *opsi_tree, int offset, int length)
420 {
421         int i;
422         int attribute_type;
423         int attribute_length;
424         proto_item *ti;
425         proto_tree *ntree = NULL;
426         
427         while (length >= 4) {
428                 attribute_type          = tvb_get_ntohs(tvb, offset);
429                 attribute_length        = tvb_get_ntohs(tvb, offset+2);
430                 if (attribute_length > length) break;
431                 /* We perform a standard log(n) lookup */
432                 i = get_opsi_attribute_index(0, OPSI_ATTRIBUTES_COUNT-1, attribute_type);
433                 if (i == -1) {
434                         proto_tree_add_text(opsi_tree, tvb, offset, attribute_length, "Unknown attribute (%d)", attribute_type);                        
435                 }
436                 else {
437                         ti = proto_tree_add_text(opsi_tree, tvb, offset, attribute_length, "%s (%d)", opsi_attributes[i].tree_text, attribute_type);
438                         ntree = proto_item_add_subtree(ti, *opsi_attributes[i].tree_id);
439                         proto_tree_add_text(ntree, tvb, offset+2, 2, "Length (%d)", attribute_length);
440                         opsi_attributes[i].dissect(tvb, ntree, opsi_attributes[i].hf_type_attribute, offset, attribute_length);
441                 }
442                 if (attribute_length < 4) {
443                         /* Length must be at least 4, for the type and length. */
444                         break;
445                 }
446                 offset += attribute_length;
447                 length -= attribute_length;
448         }
449         if (length) {
450                 proto_tree_add_text(opsi_tree, tvb, offset, -1, "Short frame");
451         }
452 }
453
454 static void
455 dissect_opsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
456 {
457         proto_item *ti;
458         proto_tree *opsi_tree;
459         
460         if (opsi_first == TRUE) {
461                 opsi_first = FALSE;
462                 
463                 if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
464                         col_set_str(pinfo->cinfo, COL_PROTOCOL, "OPSI");
465     
466                 if (check_col(pinfo->cinfo, COL_INFO)) {
467                         col_clear(pinfo->cinfo, COL_INFO);
468                         if (tvb_length(tvb) < CODE_OFFSET+1) {
469                                 col_set_str(pinfo->cinfo, COL_INFO, "Open Policy Service Interface");
470                         }
471                         else {
472                                 col_add_fstr(pinfo->cinfo, COL_INFO, "Open Policy Service Interface, %s",
473                                         val_to_str(tvb_get_guint8(tvb, CODE_OFFSET), opsi_opcode,  
474                                      "<Unknown opcode %d>"));
475                         }
476                 }
477         }
478         else if (check_col(pinfo->cinfo, COL_INFO) && (tvb_length(tvb) > CODE_OFFSET)) {
479                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
480                           val_to_str(tvb_get_guint8(tvb, CODE_OFFSET), opsi_opcode, 
481                                      "<Unknown opcode %d>"));
482         }
483
484         if (tree) {
485                 ti = proto_tree_add_item(tree, proto_opsi, tvb, 0, -1, FALSE);
486                 opsi_tree = proto_item_add_subtree(ti, ett_opsi);
487                 if (tvb_length(tvb) < 8 ) {
488                         proto_tree_add_text(opsi_tree, tvb, 0, -1, "Too short OPSI packet!");
489                         return;
490                 }
491                 
492                 proto_tree_add_item(opsi_tree, hf_opsi_major_version, tvb, MAJOR_VERSION_OFFSET, 1, FALSE);
493                 proto_tree_add_item(opsi_tree, hf_opsi_minor_version, tvb, MINOR_VERSION_OFFSET, 1, FALSE);
494                 proto_tree_add_item(opsi_tree, hf_opsi_opcode, tvb, CODE_OFFSET, 1, FALSE);
495                 proto_tree_add_item(opsi_tree, hf_opsi_hook_id, tvb, HOOK_ID_OFFSET, 1, FALSE);
496                 proto_tree_add_item(opsi_tree, hf_opsi_length, tvb, PACKET_LENGTH_OFFSET, 2, FALSE);
497                 proto_tree_add_item(opsi_tree, hf_opsi_session_id, tvb, SESSION_OFFSET, 2, FALSE);
498                 
499                 dissect_attributes(tvb, opsi_tree, HEADER_LENGTH, MIN(((int)tvb_length(tvb)-HEADER_LENGTH), (tvb_get_ntohs(tvb, PACKET_LENGTH_OFFSET)-HEADER_LENGTH)));
500         }
501 }
502
503
504 static void
505 dissect_opsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
506 {
507         opsi_first = TRUE;
508         /* we need at least 6 bytes to get the payload size ! */
509         tcp_dissect_pdus(tvb, pinfo, tree, opsi_desegment, 6, get_opsi_pdu_len,
510                 dissect_opsi_pdu);
511 }
512
513
514 void
515 proto_register_opsi(void)
516 {                 
517
518 /* Setup list of header fields  See Section 1.6.1 for details*/
519         static hf_register_info hf[] = {
520                 { &hf_opsi_major_version,
521                         { "Major version",           "opsi.major",
522                         FT_UINT8, BASE_DEC, NULL, 0x0,          
523                         "", HFILL }
524                 },
525                 { &hf_opsi_minor_version,
526                         { "Minor version",           "opsi.minor",
527                         FT_UINT8, BASE_DEC, NULL, 0x0,          
528                         "", HFILL }
529                 },
530                 { &hf_opsi_opcode,
531                         { "Operation code",             "opsi.opcode",
532                         FT_UINT8, BASE_DEC, VALS(opsi_opcode), 0x0,
533                         "", HFILL }
534                 },
535                 { &hf_opsi_hook_id,
536                         { "Hook ID",                    "opsi.hook",
537                         FT_UINT8, BASE_DEC, NULL, 0x0,
538                         "", HFILL }
539                 },
540                 { &hf_opsi_length,
541                         { "Message length",             "opsi.length",
542                         FT_UINT16, BASE_DEC, NULL, 0x0,
543                         "", HFILL }
544                 },
545                 { &hf_opsi_session_id,
546                         { "Session ID",                 "opsi.session_id",
547                         FT_UINT16, BASE_DEC, NULL, 0x0,
548                         "", HFILL }
549                 },
550                 { &hf_user_name_att,
551                         { "User name",                  "opsi.attr.user_name",
552                         FT_STRING, BASE_NONE, NULL, 0x00,
553                         "", HFILL }
554                 },
555                 { &hf_password_att,
556                         { "User password",              "opsi.attr.password",
557                         FT_STRING, BASE_NONE, NULL, 0x00,
558                         "", HFILL }
559                 },
560                 { &hf_chap_password_att,
561                         { "CHAP password attribute",    "opsi.attr.chap_password",
562                         FT_STRING, BASE_NONE, NULL, 0x00,
563                         "", HFILL }
564                 },
565                 { &hf_nas_ip_add_att,
566                         { "NAS IP address",             "opsi.attr.nas_ip_addr",
567                         FT_IPv4, BASE_DEC, NULL, 0x00,
568                         "", HFILL }
569                 },
570                 { &hf_nas_port_att,
571                         { "NAS port",                   "opsi.attr.nas_port",
572                         FT_UINT32, BASE_HEX, NULL, 0x00,
573                         "", HFILL }
574                 },
575                 { &hf_service_type_att,
576                         { "Service type",               "opsi.attr.service_type",
577                         FT_UINT32, BASE_DEC, VALS(opsi_service_type_code), 0x0,
578                         "", HFILL }
579                 },
580                 { &hf_framed_protocol_att,
581                         { "Framed protocol",            "opsi.attr.framed_protocol",
582                         FT_UINT32, BASE_DEC, VALS(opsi_framed_protocol_code), 0x0,
583                         "", HFILL }
584                 },
585                 { &hf_framed_address_att,
586                         { "Framed address",             "opsi.attr.framed_address",
587                         FT_IPv4, BASE_DEC, NULL, 0x00,
588                         "", HFILL }
589                 },
590                 { &hf_framed_netmask_att,
591                         { "Framed netmask",             "opsi.attr.framed_netmask",
592                         FT_IPv4, BASE_DEC, NULL, 0x00,
593                         "", HFILL }
594                 },
595                 { &hf_framed_routing_att,
596                         { "Framed routing",             "opsi.attr.framed_routing",
597                         FT_UINT32, BASE_DEC, VALS(opsi_framed_routing_code), 0x0,
598                         "", HFILL }
599                 },
600                 { &hf_framed_filter_att,
601                         { "Framed filter",              "opsi.attr.framed_filter",
602                         FT_STRING, BASE_NONE, NULL, 0x00,
603                         "", HFILL }
604                 },
605                 { &hf_framed_mtu_att,
606                         { "Framed MTU",         "opsi.attr.framed_mtu",
607                         FT_UINT32, BASE_DEC, NULL, 0x00,
608                         "", HFILL }
609                 },
610                 { &hf_framed_compression_att,
611                         { "Framed compression",         "opsi.attr.framed_compression",
612                         FT_UINT32, BASE_DEC, VALS(opsi_framed_compression_code), 0x0,
613                         "", HFILL }
614                 },
615                 { &hf_called_station_att,
616                         { "Called station ID",          "opsi.attr.called_station_id",
617                         FT_STRING, BASE_NONE, NULL, 0x00,       
618                         "", HFILL }
619                 },
620                 { &hf_calling_station_att,
621                         { "Calling station ID",         "opsi.attr.calling_station_id",
622                         FT_STRING, BASE_NONE, NULL, 0x00,
623                         "", HFILL }
624                 },
625                 { &hf_nas_identifier_att,
626                         { "NAS ID",                     "opsi.attr.nas_id",
627                         FT_STRING, BASE_NONE, NULL, 0x00,
628                         "", HFILL }
629                 },
630                 { &hf_accounting_att,
631                         { "Accounting",                 "opsi.attr.accounting",
632                         FT_STRING, BASE_NONE, NULL, 0x00,
633                         "", HFILL }
634                 },
635                 { &hf_acct_session_id_att,
636                         { "Accounting session ID",      "opsi.attr.acct.session_id",
637                         FT_STRING, BASE_NONE, NULL, 0x00,
638                         "", HFILL }
639                 },
640                 { &hf_chap_challenge_att,
641                         { "CHAP challenge",             "opsi.attr.chap_challenge",
642                         FT_STRING, BASE_NONE, NULL, 0x00,
643                         "", HFILL }
644                 },
645                 { &hf_nas_port_type_att,
646                         { "NAS port type",              "opsi.attr.nas_port_type",
647                         FT_UINT32, BASE_DEC, VALS(opsi_nas_port_type_code), 0x0,
648                         "", HFILL }
649                 },
650                 { &hf_designation_num_att,
651                         { "Designation number",         "opsi.attr.designation_number",
652                         FT_STRING, BASE_NONE, NULL, 0x00,
653                         "", HFILL }
654                 },
655                 { &hf_nas_port_id_att,
656                         { "NAS port ID",                "opsi.attr.nas_port_id",
657                         FT_STRING, BASE_NONE, NULL, 0x00,
658                         "", HFILL }
659                 },
660                 { &hf_smc_aaa_id_att,
661                         { "SMC AAA ID",                 "opsi.attr.smc_aaa_id",
662                         FT_UINT32, BASE_DEC, NULL, 0x00,
663                         "", HFILL }
664                 },
665                 { &hf_smc_vpn_id_att,   
666                         { "SMC VPN ID",                 "opsi.attr.smc_vpn_id",
667                         FT_UINT32, BASE_DEC, NULL, 0x00,
668                         "", HFILL }
669                 },
670                 { &hf_smc_vpn_name_att,
671                         { "SMC VPN name",               "opsi.attr.smc_vpn_name",
672                         FT_STRING, BASE_NONE, NULL, 0x00,
673                         "", HFILL }
674                 },
675                 { &hf_smc_ran_id_att,
676                         { "SMC RAN ID",                 "opsi.attr.smc_ran_id",
677                         FT_UINT32, BASE_DEC, NULL, 0x00,
678                         "", HFILL }
679                 },
680                 { &hf_smc_ran_ip_att,   
681                         { "SMC RAN IP address",         "opsi.attr.smc_ran_ip",
682                         FT_IPv4, BASE_DEC, NULL, 0x00,
683                         "", HFILL }
684                 },
685                 { &hf_smc_ran_name_att,
686                         { "SMC RAN name",               "opsi.attr.smc_ran_name",
687                         FT_STRING, BASE_NONE, NULL, 0x00,
688                         "", HFILL }
689                 },
690                 { &hf_smc_pop_id_att,
691                         { "SMC POP id",                 "opsi.attr.smc_pop_id",
692                         FT_UINT32, BASE_DEC, NULL, 0x00,
693                         "", HFILL }
694                 },
695                 { &hf_smc_pop_name_att,
696                         { "SMC POP name",               "opsi.attr.smc_pop_name",
697                         FT_STRING, BASE_NONE, NULL, 0x00,
698                         "", HFILL }
699                 },
700                 { &hf_smc_id_att,        
701                         { "SMC ID",                     "opsi.attr.smc_id",
702                         FT_UINT32, BASE_DEC, NULL, 0x00,
703                         "", HFILL }
704                 },
705                 { &hf_smc_receive_time_att,
706                         { "SMC receive time",           "opsi.attr.smc_receive_time",
707                         FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x00,
708                         "", HFILL }
709                 },
710                 { &hf_smc_stat_time_att,
711                         { "SMC stat time",              "opsi.attr.smc_stat_time",
712                         FT_UINT32, BASE_DEC, NULL, 0x00,
713                         "", HFILL }
714                 },
715                 { &hf_opsi_flags_att,
716                         { "OPSI flags",                 "opsi.attr.flags",
717                         FT_UINT32, BASE_DEC, NULL, 0x00,
718                         "", HFILL }
719                 },
720                 { &hf_opsi_application_name_att,
721                         { "OPSI application name",      "opsi.attr.application_name",
722                         FT_STRING, BASE_NONE, NULL, 0x00,
723                         "", HFILL }
724                 },
725         };
726
727 /* Setup protocol subtree array */
728         static gint *ett[] = {
729                 &ett_opsi,
730                 &ett_opsi_user_name,
731                 &ett_opsi_user_password,
732                 &ett_opsi_chap_password,
733                 &ett_opsi_nas_ip_address,
734                 &ett_opsi_nas_port,
735                 &ett_opsi_service_type,
736                 &ett_opsi_framed_protocol,
737                 &ett_opsi_framed_address,
738                 &ett_opsi_framed_netmask,
739                 &ett_opsi_framed_routing,
740                 &ett_opsi_framed_filter,
741                 &ett_opsi_framed_mtu,
742                 &ett_opsi_framed_compression,
743                 &ett_opsi_called_station_id,
744                 &ett_opsi_calling_station_id,
745                 &ett_opsi_nas_identifier,
746                 &ett_opsi_accounting,
747                 &ett_opsi_acct_session_id,
748                 &ett_opsi_chap_challenge,
749                 &ett_opsi_nas_port_type,
750                 &ett_opsi_designation_number,
751                 &ett_opsi_nas_port_id,
752                 &ett_opsi_smc_aaa_id,
753                 &ett_opsi_smc_vpn_id,           
754                 &ett_opsi_smc_vpn_name,
755                 &ett_opsi_smc_ran_id,   
756                 &ett_opsi_smc_ran_ip,           
757                 &ett_opsi_smc_ran_name,
758                 &ett_opsi_smc_pop_id,   
759                 &ett_opsi_smc_pop_name,
760                 &ett_opsi_smc_id,               
761                 &ett_opsi_smc_receive_time,
762                 &ett_opsi_smc_stat_time,
763                 &ett_opsi_flags,
764                 &ett_opsi_application_name,
765         };
766         
767 /* For desegmentation / reassembly */
768         module_t *opsi_module;  
769
770 /* Register the protocol name and description */
771         proto_opsi = proto_register_protocol("Open Policy Service Interface",
772             "OPSI", "opsi");
773
774 /* Required function calls to register the header fields and subtrees used */
775         proto_register_field_array(proto_opsi, hf, array_length(hf));
776         proto_register_subtree_array(ett, array_length(ett));
777         
778 /* We activate the desegmentation / reassembly feature */       
779         opsi_module = prefs_register_protocol(proto_opsi, NULL);
780         prefs_register_bool_preference(opsi_module, "desegment_opsi_messages",
781                 "Reassemble OPSI messages spanning multiple TCP segments",
782                 "Whether the OPSI dissector should desegment all messages spanning multiple TCP segments",
783                 &opsi_desegment);
784 }
785
786
787 void
788 proto_reg_handoff_opsi(void)
789 {
790         dissector_handle_t opsi_handle;
791         opsi_handle = create_dissector_handle(dissect_opsi, proto_opsi);
792         dissector_add("tcp.port", TCP_PORT_OPSI, opsi_handle);
793 }