HACK: 2nd try to match RowsetProperties
[wireshark-wip.git] / epan / dissectors / packet-radius.c
blob32a01d1291f3545e8b68bf8766c8e236a8dba44b
1 /* packet-radius.c
3 * Routines for RADIUS packet disassembly
4 * Copyright 1999 Johan Feyaerts
5 * Changed 03/12/2003 Rui Carmo (http://the.taoofmac.com - added all 3GPP VSAs, some parsing)
6 * Changed 07/2005 Luis Ontanon <luis@ontanon.org> - use FreeRADIUS' dictionary
7 * Changed 10/2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> - add Conversations support
9 * $Id$
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 * References:
31 * RFC 2865 - Remote Authentication Dial In User Service (RADIUS)
32 * RFC 2866 - RADIUS Accounting
33 * RFC 2867 - RADIUS Accounting Modifications for Tunnel Protocol Support
34 * RFC 2868 - RADIUS Attributes for Tunnel Protocol Support
35 * RFC 2869 - RADIUS Extensions
36 * RFC 3162 - RADIUS and IPv6
37 * RFC 3576 - Dynamic Authorization Extensions to RADIUS
39 * See also
41 * http://www.iana.org/assignments/radius-types
43 * and see
45 * http://freeradius.org/radiusd/man/dictionary.html
47 * for the dictionary file syntax.
52 TO (re)DO: (see svn rev 14786)
53 - dissect_3gpp_ipv6_dns_servers()
56 #include "config.h"
58 #include <stdlib.h>
59 #include <string.h>
60 #include <ctype.h>
61 #include <errno.h>
63 #include <glib.h>
65 #include <wsutil/report_err.h>
66 #include <wsutil/md5.h>
68 #include <epan/packet.h>
69 #include <epan/exceptions.h>
70 #include <epan/prefs.h>
71 #include <epan/sminmpec.h>
72 #include <epan/filesystem.h>
73 #include <epan/conversation.h>
74 #include <epan/tap.h>
75 #include <epan/addr_resolv.h>
76 #include <epan/garrayfix.h>
77 #include <epan/wmem/wmem.h>
79 #include "packet-radius.h"
81 void proto_reg_handoff_radius(void);
83 typedef struct _e_radiushdr {
84 guint8 rh_code;
85 guint8 rh_ident;
86 guint16 rh_pktlength;
87 } e_radiushdr;
89 typedef struct {
90 wmem_array_t* hf;
91 wmem_array_t* ett;
92 wmem_array_t* vend_vs;
93 } hfett_t;
95 #define AUTHENTICATOR_LENGTH 16
96 #define RD_HDR_LENGTH 4
97 #define HDR_LENGTH (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
99 #define UDP_PORT_RADIUS 1645
100 #define UDP_PORT_RADIUS_NEW 1812
101 #define UDP_PORT_RADACCT 1646
102 #define UDP_PORT_RADACCT_NEW 1813
103 #define UDP_PORT_DAE_OLD 1700 /* DAE: pre RFC */
104 #define UDP_PORT_DAE 3799 /* DAE: rfc3576 */
106 static radius_dictionary_t* dict = NULL;
108 static int proto_radius = -1;
110 static int hf_radius_req = -1;
111 static int hf_radius_rsp = -1;
112 static int hf_radius_req_frame = -1;
113 static int hf_radius_rsp_frame = -1;
114 static int hf_radius_time = -1;
116 static int hf_radius_dup = -1;
117 static int hf_radius_req_dup = -1;
118 static int hf_radius_rsp_dup = -1;
120 static int hf_radius_id = -1;
121 static int hf_radius_code = -1;
122 static int hf_radius_length = -1;
123 static int hf_radius_authenticator = -1;
125 static int hf_radius_chap_password = -1;
126 static int hf_radius_chap_ident = -1;
127 static int hf_radius_chap_string = -1;
128 static int hf_radius_framed_ip_address = -1;
130 static int hf_radius_login_ip_host = -1;
131 static int hf_radius_framed_ipx_network = -1;
133 static int hf_radius_cosine_vpi = -1;
134 static int hf_radius_cosine_vci = -1;
136 static int hf_radius_ascend_data_filter = -1;
138 static gint ett_radius = -1;
139 static gint ett_radius_avp = -1;
140 static gint ett_eap = -1;
141 static gint ett_chap = -1;
143 * Define the tap for radius
145 static int radius_tap = -1;
147 static radius_vendor_info_t no_vendor = {"Unknown Vendor",0,NULL,-1,1,1,FALSE};
149 static radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute",0,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1, NULL };
151 static dissector_handle_t eap_handle;
153 static const gchar* shared_secret = "";
154 static gboolean show_length = FALSE;
155 static guint alt_port_pref = 0;
156 static guint request_ttl = 5;
158 static guint8 authenticator[AUTHENTICATOR_LENGTH];
160 /* http://www.iana.org/assignments/radius-types */
161 static const value_string radius_pkt_type_codes[] =
163 {RADIUS_PKT_TYPE_ACCESS_REQUEST, "Access-Request"}, /* 1 RFC2865 */
164 {RADIUS_PKT_TYPE_ACCESS_ACCEPT, "Access-Accept"}, /* 2 RFC2865 */
165 {RADIUS_PKT_TYPE_ACCESS_REJECT, "Access-Reject"}, /* 3 RFC2865 */
166 {RADIUS_PKT_TYPE_ACCOUNTING_REQUEST, "Accounting-Request"}, /* 4 RFC2865 */
167 {RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE, "Accounting-Response"}, /* 5 RFC2865 */
168 {RADIUS_PKT_TYPE_ACCOUNTING_STATUS, "Accounting-Status"}, /* 6 RFC3575 */
169 {RADIUS_PKT_TYPE_PASSWORD_REQUEST, "Password-Request"}, /* 7 RFC3575 */
170 {RADIUS_PKT_TYPE_PASSWORD_ACK, "Password-Ack"}, /* 8 RFC3575 */
171 {RADIUS_PKT_TYPE_PASSWORD_REJECT, "Password-Reject"}, /* 9 RFC3575 */
172 {RADIUS_PKT_TYPE_ACCOUNTING_MESSAGE, "Accounting-Message"}, /* 10 RFC3575 */
173 {RADIUS_PKT_TYPE_ACCESS_CHALLENGE, "Access-Challenge"}, /* 11 RFC2865 */
174 {RADIUS_PKT_TYPE_STATUS_SERVER, "Status-Server"}, /* 12 RFC2865 */
175 {RADIUS_PKT_TYPE_STATUS_CLIENT, "Status-Client"}, /* 13 RFC2865 */
177 {RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST, "Resource-Free-Request"}, /* 21 RFC3575 */
178 {RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE, "Resource-Free-Response"}, /* 22 RFC3575 */
179 {RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST, "Resource-Query-Request"}, /* 23 RFC3575 */
180 {RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE, "Query_Response"}, /* 24 RFC3575 */
181 {RADIUS_PKT_TYPE_ALTERNATE_RESOURCE_RECLAIM_REQUEST, "Alternate-Resource-Reclaim-Request"}, /* 25 RFC3575 */
182 {RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST, "NAS-Reboot-Request"}, /* 26 RFC3575 */
183 {RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE, "NAS-Reboot-Response"}, /* 27 RFC3575 */
185 {RADIUS_PKT_TYPE_NEXT_PASSCODE, "Next-Passcode"}, /* 29 RFC3575 */
186 {RADIUS_PKT_TYPE_NEW_PIN, "New-Pin"}, /* 30 RFC3575 */
187 {RADIUS_PKT_TYPE_TERMINATE_SESSION, "Terminate-Session"}, /* 31 RFC3575 */
188 {RADIUS_PKT_TYPE_PASSWORD_EXPIRED, "Password-Expired"}, /* 32 RFC3575 */
189 {RADIUS_PKT_TYPE_EVENT_REQUEST, "Event-Request"}, /* 33 RFC3575 */
190 {RADIUS_PKT_TYPE_EVENT_RESPONSE, "Event-Response"}, /* 34 RFC3575|RFC5176 */
192 {RADIUS_PKT_TYPE_DISCONNECT_REQUEST, "Disconnect-Request"}, /* 40 RFC3575|RFC5176 */
193 {RADIUS_PKT_TYPE_DISCONNECT_ACK, "Disconnect-ACK"}, /* 41 RFC3575|RFC5176 */
194 {RADIUS_PKT_TYPE_DISCONNECT_NAK, "Disconnect-NAK"}, /* 42 RFC3575|RFC5176 */
195 {RADIUS_PKT_TYPE_COA_REQUEST, "CoA-Request"}, /* 43 RFC3575|RFC5176 */
196 {RADIUS_PKT_TYPE_COA_ACK, "CoA-ACK"}, /* 44 RFC3575|RFC5176 */
197 {RADIUS_PKT_TYPE_COA_NAK, "CoA-NAK"}, /* 45 RFC3575|RFC5176 */
199 {RADIUS_PKT_TYPE_IP_ADDRESS_ALLOCATE, "IP-Address-Allocate"}, /* 50 RFC3575 */
200 {RADIUS_PKT_TYPE_IP_ADDRESS_RELEASE, "IP-Address-Release"}, /* 51 RFC3575 */
202 {RADIUS_PKT_TYPE_ALU_STATE_REQUEST, "ALU-State-Request"}, /* 129 ALU AAA */
203 {RADIUS_PKT_TYPE_ALU_STATE_ACCEPT, "ALU-State-Accept"}, /* 130 ALU AAA */
204 {RADIUS_PKT_TYPE_ALU_STATE_REJECT, "ALU-State-Reject"}, /* 131 ALU AAA */
205 {RADIUS_PKT_TYPE_ALU_STATE_ERROR, "ALU-State-Error"}, /* 132 ALU AAA */
207 250-253 Experimental Use [RFC3575]
208 254-255 Reserved [RFC3575]
210 {0, NULL}
212 static value_string_ext radius_pkt_type_codes_ext = VALUE_STRING_EXT_INIT(radius_pkt_type_codes);
215 * Init Hash table stuff for conversation
218 typedef struct _radius_call_info_key
220 guint code;
221 guint ident;
222 conversation_t *conversation;
223 nstime_t req_time;
224 } radius_call_info_key;
226 static GHashTable *radius_calls;
228 typedef struct _radius_vsa_buffer_key
230 guint32 vendor_id;
231 guint32 vsa_type;
232 } radius_vsa_buffer_key;
234 typedef struct _radius_vsa_buffer
236 radius_vsa_buffer_key key;
237 guint8* data;
238 guint seg_num;
239 guint len;
240 } radius_vsa_buffer;
242 static gint radius_vsa_equal(gconstpointer k1, gconstpointer k2)
244 const radius_vsa_buffer_key* key1 = (const radius_vsa_buffer_key*) k1;
245 const radius_vsa_buffer_key* key2 = (const radius_vsa_buffer_key*) k2;
247 return (((key1->vendor_id == key2->vendor_id) &&
248 (key1->vsa_type == key2->vsa_type)
249 ) ? TRUE : FALSE);
252 static guint radius_vsa_hash(gconstpointer k)
254 const radius_vsa_buffer_key* key = (const radius_vsa_buffer_key*) k;
256 return key->vendor_id + key->vsa_type;
259 /* Compare 2 keys */
260 static gboolean radius_call_equal(gconstpointer k1, gconstpointer k2)
262 const radius_call_info_key* key1 = (const radius_call_info_key*) k1;
263 const radius_call_info_key* key2 = (const radius_call_info_key*) k2;
265 if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
266 nstime_t delta;
268 nstime_delta(&delta, &key1->req_time, &key2->req_time);
269 if (abs( (int) nstime_to_sec(&delta)) > (double) request_ttl) return 0;
271 if (key1->code == key2->code)
272 return TRUE;
273 /* check the request and response are of the same code type */
274 if ((key1->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) &&
275 ((key2->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) ||
276 (key2->code == RADIUS_PKT_TYPE_ACCESS_REJECT) ||
277 (key2->code == RADIUS_PKT_TYPE_ACCESS_CHALLENGE)))
278 return TRUE;
280 if ((key1->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) &&
281 (key2->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE))
282 return TRUE;
284 if ((key1->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) &&
285 ((key2->code == RADIUS_PKT_TYPE_PASSWORD_ACK) || (key2->code == RADIUS_PKT_TYPE_PASSWORD_REJECT)))
286 return TRUE;
288 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) &&
289 (key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE))
290 return TRUE;
292 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) &&
293 (key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE))
294 return TRUE;
296 if ((key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) &&
297 (key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE))
298 return TRUE;
300 if ((key1->code == RADIUS_PKT_TYPE_EVENT_REQUEST) &&
301 (key2->code == RADIUS_PKT_TYPE_EVENT_RESPONSE))
302 return TRUE;
304 if ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) &&
305 ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) || (key2->code == RADIUS_PKT_TYPE_DISCONNECT_NAK)))
306 return TRUE;
308 if ((key1->code == RADIUS_PKT_TYPE_COA_REQUEST) &&
309 ((key2->code == RADIUS_PKT_TYPE_COA_ACK) || (key2->code == RADIUS_PKT_TYPE_COA_NAK)))
310 return TRUE;
312 if ((key1->code == RADIUS_PKT_TYPE_ALU_STATE_REQUEST) &&
313 ((key2->code == RADIUS_PKT_TYPE_ALU_STATE_ACCEPT) ||
314 (key2->code == RADIUS_PKT_TYPE_ALU_STATE_REJECT) ||
315 (key2->code == RADIUS_PKT_TYPE_ALU_STATE_ERROR)))
316 return TRUE;
318 return FALSE;
321 /* Calculate a hash key */
322 static guint radius_call_hash(gconstpointer k)
324 const radius_call_info_key* key = (const radius_call_info_key*) k;
326 return key->ident + /*key->code + */ key->conversation->index;
330 static const gchar *dissect_chap_password(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
331 int len;
332 proto_item *ti;
333 proto_tree *chap_tree;
335 len = tvb_length(tvb);
336 if (len != 17)
337 return "[wrong length for CHAP-Password]";
339 ti = proto_tree_add_item(tree, hf_radius_chap_password, tvb, 0, len, ENC_NA);
340 chap_tree = proto_item_add_subtree(ti, ett_chap);
341 proto_tree_add_item(chap_tree, hf_radius_chap_ident, tvb, 0, 1, ENC_NA);
342 proto_tree_add_item(chap_tree, hf_radius_chap_string, tvb, 1, 16, ENC_NA);
343 return (tvb_bytes_to_str(tvb, 0, len));
346 static const gchar *dissect_framed_ip_address(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
347 int len;
348 guint32 ip;
349 guint32 ip_h;
350 const gchar *str;
352 len = tvb_length(tvb);
353 if (len != 4)
354 return "[wrong length for IP address]";
356 ip=tvb_get_ipv4(tvb,0);
357 ip_h=g_ntohl(ip);
359 if (ip_h == 0xFFFFFFFF) {
360 str = "Negotiated";
361 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
362 tvb, 0, len, ip, "%s", str);
363 } else if (ip_h == 0xFFFFFFFE) {
364 str = "Assigned";
365 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
366 tvb, 0, len, ip, "%s", str);
367 } else {
368 str = ip_to_str((guint8 *)&ip);
369 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
370 tvb, 0, len, ip, "%s (%s)",
371 get_hostname(ip), str);
374 return str;
377 static const gchar *dissect_login_ip_host(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
378 int len;
379 guint32 ip;
380 guint32 ip_h;
381 const gchar *str;
383 len = tvb_length(tvb);
384 if (len != 4)
385 return "[wrong length for IP address]";
387 ip=tvb_get_ipv4(tvb,0);
388 ip_h=g_ntohl(ip);
390 if (ip_h == 0xFFFFFFFF) {
391 str = "User-selected";
392 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
393 tvb, 0, len, ip, "%s", str);
394 } else if (ip_h == 0) {
395 str = "NAS-selected";
396 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
397 tvb, 0, len, ip, "%s", str);
398 } else {
399 str = ip_to_str((guint8 *)&ip);
400 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
401 tvb, 0, len, ip, "%s (%s)",
402 get_hostname(ip), str);
405 return str;
408 static const value_string ascenddf_filtertype[] = { {0, "generic"}, {1, "ip"}, {0, NULL} };
409 static const value_string ascenddf_filteror[] = { {0, "drop"}, {1, "forward"}, {0, NULL} };
410 static const value_string ascenddf_inout[] = { {0, "out"}, {1, "in"}, {0, NULL} };
411 static const value_string ascenddf_proto[] = { {1, "icmp"}, {6, "tcp"}, {17, "udp"}, {0, NULL} };
412 static const value_string ascenddf_portq[] = { {1, "lt"}, {2, "eq"}, {3, "gt"}, {4, "ne"}, {0, NULL} };
414 static const gchar *dissect_ascend_data_filter(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
415 wmem_strbuf_t *filterstr;
416 int len;
417 guint8 proto, srclen, dstlen;
418 guint32 srcip, dstip;
419 guint16 srcport, dstport;
420 guint8 srcportq, dstportq;
422 len=tvb_length(tvb);
424 if (len != 24) {
425 return wmem_strdup_printf(wmem_packet_scope(), "Wrong attribute length %d", len);
428 filterstr=wmem_strbuf_sized_new(wmem_packet_scope(), 64, 64);
430 proto_tree_add_item(tree, hf_radius_ascend_data_filter, tvb, 0, -1, ENC_NA);
432 wmem_strbuf_append_printf(filterstr, "%s %s %s",
433 val_to_str(tvb_get_guint8(tvb, 0), ascenddf_filtertype, "%u"),
434 val_to_str(tvb_get_guint8(tvb, 2), ascenddf_inout, "%u"),
435 val_to_str(tvb_get_guint8(tvb, 1), ascenddf_filteror, "%u"));
437 proto=tvb_get_guint8(tvb, 14);
438 if (proto) {
439 wmem_strbuf_append_printf(filterstr, " %s",
440 val_to_str(proto, ascenddf_proto, "%u"));
443 srcip=tvb_get_ipv4(tvb, 4);
444 srclen=tvb_get_guint8(tvb, 12);
445 srcport=tvb_get_ntohs(tvb, 16);
446 srcportq=tvb_get_guint8(tvb, 20);
448 if (srcip || srclen || srcportq) {
449 wmem_strbuf_append_printf(filterstr, " srcip %s/%d", ip_to_str((guint8 *) &srcip), srclen);
450 if (srcportq)
451 wmem_strbuf_append_printf(filterstr, " srcport %s %d",
452 val_to_str(srcportq, ascenddf_portq, "%u"), srcport);
455 dstip=tvb_get_ipv4(tvb, 8);
456 dstlen=tvb_get_guint8(tvb, 13);
457 dstport=tvb_get_ntohs(tvb, 18);
458 dstportq=tvb_get_guint8(tvb, 21);
460 if (dstip || dstlen || dstportq) {
461 wmem_strbuf_append_printf(filterstr, " dstip %s/%d", ip_to_str((guint8 *) &dstip), dstlen);
462 if (dstportq)
463 wmem_strbuf_append_printf(filterstr, " dstport %s %d",
464 val_to_str(dstportq, ascenddf_portq, "%u"), dstport);
467 return wmem_strbuf_get_str(filterstr);
470 static const gchar *dissect_framed_ipx_network(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
471 int len;
472 guint32 net;
473 const gchar *str;
475 len = tvb_length(tvb);
476 if (len != 4)
477 return "[wrong length for IPX network]";
479 net=tvb_get_ntohl(tvb,0);
481 if (net == 0xFFFFFFFE)
482 str = "NAS-selected";
483 else
484 str = wmem_strdup_printf(wmem_packet_scope(), "0x%08X", net);
485 proto_tree_add_ipxnet_format_value(tree, hf_radius_framed_ipx_network, tvb, 0,
486 len, net, "Framed-IPX-Network: %s", str);
488 return str;
491 static const gchar* dissect_cosine_vpvc(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
492 guint vpi, vci;
494 if ( tvb_length(tvb) != 4 )
495 return "[Wrong Length for VP/VC AVP]";
497 vpi = tvb_get_ntohs(tvb,0);
498 vci = tvb_get_ntohs(tvb,2);
500 proto_tree_add_uint(tree,hf_radius_cosine_vpi,tvb,0,2,vpi);
501 proto_tree_add_uint(tree,hf_radius_cosine_vci,tvb,2,2,vci);
503 return wmem_strdup_printf(wmem_packet_scope(), "%u/%u",vpi,vci);
506 static const value_string daylight_saving_time_vals[] = {
507 {0, "No adjustment"},
508 {1, "+1 hour adjustment for Daylight Saving Time"},
509 {2, "+2 hours adjustment for Daylight Saving Time"},
510 {3, "Reserved"},
511 {0, NULL}
514 static const gchar*
515 dissect_radius_3gpp_ms_tmime_zone(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
517 int offset = 0;
518 guint8 oct, daylight_saving_time;
519 char sign;
521 /* 3GPP TS 23.040 version 6.6.0 Release 6
522 * 9.2.3.11 TP-Service-Centre-Time-Stamp (TP-SCTS)
524 * The Time Zone indicates the difference, expressed in quarters of an hour,
525 * between the local time and GMT. In the first of the two semi-octets,
526 * the first bit (bit 3 of the seventh octet of the TP-Service-Centre-Time-Stamp field)
527 * represents the algebraic sign of this difference (0: positive, 1: negative).
530 oct = tvb_get_guint8(tvb, offset);
531 sign = (oct & 0x08) ? '-' : '+';
532 oct = (oct >> 4) + (oct & 0x07) * 10;
534 proto_tree_add_text(tree, tvb, offset, 1, "Timezone: GMT %c%d hours %d minutes", sign, oct / 4, oct % 4 * 15);
535 offset++;
537 daylight_saving_time = tvb_get_guint8(tvb, offset) & 0x3;
538 proto_tree_add_text(tree, tvb, offset, 1, "%s", val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
540 return wmem_strdup_printf(wmem_packet_scope(), "Timezone: GMT %c%d hours %d minutes %s ",
541 sign, oct / 4, oct % 4 * 15, val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
545 static void
546 radius_decrypt_avp(gchar *dest,int dest_len,tvbuff_t *tvb,int offset,int length)
548 md5_state_t md_ctx, old_md_ctx;
549 md5_byte_t digest[AUTHENTICATOR_LENGTH];
550 int i, j;
551 gint totlen = 0, returned_length, padded_length;
552 guint8 *pd;
553 guchar c;
555 DISSECTOR_ASSERT(dest_len > 0);
556 dest[0] = '\0';
557 if ( length <= 0 )
558 return;
560 /* The max avp length is 253 (255 - 2 for type & length), but only the
561 * User-Password is marked with encrypt=1 in dictionary.rfc2865, and the
562 * User-Password max length is only 128 (130 - 2 for type & length) per
563 * tools.ietf.org/html/rfc2865#section-5.2, so enforce that limit here.
565 if ( length > 128 )
566 length = 128;
568 md5_init(&md_ctx);
569 md5_append(&md_ctx, (const guint8*)shared_secret, (int)strlen(shared_secret));
570 old_md_ctx = md_ctx;
571 md5_append(&md_ctx, authenticator, AUTHENTICATOR_LENGTH);
572 md5_finish(&md_ctx, digest);
574 padded_length = length + ((length % AUTHENTICATOR_LENGTH) ?
575 (AUTHENTICATOR_LENGTH - (length % AUTHENTICATOR_LENGTH)) : 0);
576 pd = (guint8 *)wmem_alloc0(wmem_packet_scope(), padded_length);
577 tvb_memcpy(tvb, pd, offset, length);
579 for ( i = 0; i < padded_length; i += AUTHENTICATOR_LENGTH ) {
580 for ( j = 0; j < AUTHENTICATOR_LENGTH; j++ ) {
581 c = pd[i + j] ^ digest[j];
582 if ( isprint(c) ) {
583 returned_length = g_snprintf(&dest[totlen], dest_len - totlen,
584 "%c", c);
585 totlen += MIN(returned_length, dest_len - totlen - 1);
587 else if ( c ) {
588 returned_length = g_snprintf(&dest[totlen], dest_len - totlen,
589 "\\%03o", c);
590 totlen += MIN(returned_length, dest_len - totlen - 1);
594 md_ctx = old_md_ctx;
595 md5_append(&md_ctx, &pd[i], AUTHENTICATOR_LENGTH);
596 md5_finish(&md_ctx, digest);
601 void radius_integer(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
602 guint32 uintv;
604 switch (len) {
605 case 1:
606 uintv = tvb_get_guint8(tvb,offset);
607 break;
608 case 2:
609 uintv = tvb_get_ntohs(tvb,offset);
610 break;
611 case 3:
612 uintv = tvb_get_ntoh24(tvb,offset);
613 break;
614 case 4:
615 uintv = tvb_get_ntohl(tvb,offset);
616 break;
617 case 8: {
618 guint64 uintv64 = tvb_get_ntoh64(tvb,offset);
619 proto_tree_add_uint64(tree,a->hf_alt,tvb,offset,len,uintv64);
620 proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uintv64);
621 return;
623 default:
624 proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len);
625 return;
627 proto_tree_add_item(tree,a->hf,tvb, offset, len, ENC_BIG_ENDIAN);
629 if (a->vs) {
630 proto_item_append_text(avp_item, "%s(%u)", val_to_str_const(uintv, a->vs, "Unknown"),uintv);
631 } else {
632 proto_item_append_text(avp_item, "%u", uintv);
636 void radius_signed(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
637 guint32 uintv;
639 switch (len) {
640 case 1:
641 uintv = tvb_get_guint8(tvb,offset);
642 break;
643 case 2:
644 uintv = tvb_get_ntohs(tvb,offset);
645 break;
646 case 3:
647 uintv = tvb_get_ntoh24(tvb,offset);
648 break;
649 case 4:
650 uintv = tvb_get_ntohl(tvb,offset);
651 break;
652 case 8: {
653 guint64 uintv64 = tvb_get_ntoh64(tvb,offset);
654 proto_tree_add_int64(tree,a->hf_alt,tvb,offset,len,uintv64);
655 proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uintv64);
656 return;
658 default:
659 proto_item_append_text(avp_item, "[unhandled signed integer length(%u)]", len);
660 return;
663 proto_tree_add_int(tree,a->hf,tvb,offset,len,uintv);
665 if (a->vs) {
666 proto_item_append_text(avp_item, "%s(%d)", val_to_str_const(uintv, a->vs, "Unknown"),uintv);
667 } else {
668 proto_item_append_text(avp_item, "%d", uintv);
672 void radius_string(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
673 switch (a->encrypt) {
675 case 0: /* not encrypted */
676 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_UTF_8|ENC_NA);
677 proto_item_append_text(avp_item, "%s", tvb_format_text(tvb, offset, len));
678 break;
680 case 1: /* encrypted like User-Password as defined in RFC 2865 */
681 if (*shared_secret == '\0') {
682 proto_item_append_text(avp_item, "Encrypted");
683 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
684 } else {
685 gchar *buffer;
686 buffer=(gchar *)wmem_alloc(wmem_packet_scope(), 1024); /* an AVP value can be at most 253 bytes */
687 radius_decrypt_avp(buffer,1024,tvb,offset,len);
688 proto_item_append_text(avp_item, "Decrypted: %s", buffer);
689 proto_tree_add_string(tree, a->hf, tvb, offset, len, buffer);
691 break;
693 case 2: /* encrypted like Tunnel-Password as defined in RFC 2868 */
694 proto_item_append_text(avp_item, "Encrypted");
695 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
696 break;
698 case 3: /* encrypted like Ascend-Send-Secret as defined by Ascend^WLucent^WAlcatel-Lucent */
699 proto_item_append_text(avp_item, "Encrypted");
700 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
701 break;
705 void radius_octets(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
706 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
707 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
710 void radius_ipaddr(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
711 guint32 ip;
712 gchar buf[MAX_IP_STR_LEN];
714 if (len != 4) {
715 proto_item_append_text(avp_item, "[wrong length for IP address]");
716 return;
719 ip=tvb_get_ipv4(tvb,offset);
721 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
723 ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
724 proto_item_append_text(avp_item, "%s", buf);
727 void radius_ipv6addr(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
728 struct e_in6_addr ipv6_buff;
729 gchar txtbuf[256];
731 if (len != 16) {
732 proto_item_append_text(avp_item, "[wrong length for IPv6 address]");
733 return;
736 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
738 tvb_get_ipv6(tvb, offset, &ipv6_buff);
739 ip6_to_str_buf(&ipv6_buff, txtbuf);
740 proto_item_append_text(avp_item, "%s", txtbuf);
743 void radius_ipv6prefix(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
744 struct e_in6_addr ipv6_buff;
745 gchar txtbuf[256];
746 guint8 n;
748 if ((len < 2) || (len > 18) ) {
749 proto_item_append_text(avp_item, "[wrong length for IPv6 prefix]");
750 return;
753 /* first byte is reserved == 0x00 */
754 if (tvb_get_guint8(tvb, offset)) {
755 proto_item_append_text(avp_item, "[invalid reserved byte for IPv6 prefix]");
756 return;
759 /* this is the prefix length */
760 n = tvb_get_guint8(tvb, offset + 1);
761 if (n > 128) {
762 proto_item_append_text(avp_item, "[invalid IPv6 prefix length]");
763 return;
766 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
768 /* cannot use tvb_get_ipv6() here, since the prefix most likely is truncated */
769 memset(&ipv6_buff, 0, sizeof ipv6_buff);
770 tvb_memcpy(tvb, &ipv6_buff, offset + 2, len - 2);
771 ip6_to_str_buf(&ipv6_buff, txtbuf);
772 proto_item_append_text(avp_item, "%s/%u", txtbuf, n);
776 void radius_combo_ip(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
777 guint32 ip;
778 struct e_in6_addr ipv6_buff;
779 gchar buf[256];
781 if (len == 4){
782 ip=tvb_get_ipv4(tvb,offset);
784 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
786 ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
787 proto_item_append_text(avp_item, "%s", buf);
788 } else if (len == 16) {
789 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
791 tvb_get_ipv6(tvb, offset, &ipv6_buff);
792 ip6_to_str_buf(&ipv6_buff, buf);
793 proto_item_append_text(avp_item, "%s", buf);
794 } else {
795 proto_item_append_text(avp_item, "[wrong length for both of IPv4 and IPv6 address]");
796 return;
800 void radius_ipxnet(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
801 guint32 net;
803 if (len != 4) {
804 proto_item_append_text(avp_item, "[wrong length for IPX network]");
805 return;
808 net=tvb_get_ntohl(tvb,offset);
810 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
812 proto_item_append_text(avp_item, "0x%08X", net);
815 void radius_date(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
816 nstime_t time_ptr;
818 if (len != 4) {
819 proto_item_append_text(avp_item, "[wrong length for timestamp]");
820 return;
822 time_ptr.secs = tvb_get_ntohl(tvb,offset);
823 time_ptr.nsecs = 0;
825 proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr);
826 proto_item_append_text(avp_item, "%s", abs_time_to_str(&time_ptr, ABSOLUTE_TIME_LOCAL, TRUE));
830 * "abinary" is Ascend's binary format for filters. See dissect_ascend_data_filter().
832 void radius_abinary(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
833 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
834 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
837 void radius_ether(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
838 if (len != 6) {
839 proto_item_append_text(avp_item, "[wrong length for ethernet address]");
840 return;
843 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
844 proto_item_append_text(avp_item, "%s", tvb_ether_to_str(tvb, offset));
847 void radius_ifid(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
848 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
849 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
852 static void add_tlv_to_tree(proto_tree* tlv_tree, proto_item* tlv_item, packet_info* pinfo, tvbuff_t* tvb, radius_attr_info_t* dictionary_entry, guint32 tlv_length, guint32 offset) {
853 proto_item_append_text(tlv_item, ": ");
854 dictionary_entry->type(dictionary_entry,tlv_tree,pinfo,tvb,offset,tlv_length,tlv_item);
857 void radius_tlv(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
858 proto_item* item;
859 gint tlv_num = 0;
861 while (len > 0) {
862 radius_attr_info_t* dictionary_entry = NULL;
863 guint32 tlv_type;
864 guint32 tlv_length;
866 proto_item* tlv_item;
867 proto_item* tlv_len_item;
868 proto_tree* tlv_tree;
870 if (len < 2) {
871 item = proto_tree_add_text(tree, tvb, offset, 0,
872 "Not enough room in packet for TLV header");
873 PROTO_ITEM_SET_GENERATED(item);
874 return;
876 tlv_type = tvb_get_guint8(tvb,offset);
877 tlv_length = tvb_get_guint8(tvb,offset+1);
879 if (tlv_length < 2) {
880 item = proto_tree_add_text(tree, tvb, offset, 0,
881 "TLV too short: length %u < 2", tlv_length);
882 PROTO_ITEM_SET_GENERATED(item);
883 return;
886 if (len < (gint)tlv_length) {
887 item = proto_tree_add_text(tree, tvb, offset, 0,
888 "Not enough room in packet for TLV");
889 PROTO_ITEM_SET_GENERATED(item);
890 return;
893 len -= tlv_length;
895 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(a->tlvs_by_id,GUINT_TO_POINTER(tlv_type));
897 if (! dictionary_entry ) {
898 dictionary_entry = &no_dictionary_entry;
901 tlv_item = proto_tree_add_text(tree, tvb, offset, tlv_length,
902 "TLV: l=%u t=%s(%u)", tlv_length,
903 dictionary_entry->name, tlv_type);
905 tlv_length -= 2;
906 offset += 2;
908 tlv_tree = proto_item_add_subtree(tlv_item,dictionary_entry->ett);
910 if (show_length) {
911 tlv_len_item = proto_tree_add_uint(tlv_tree,
912 dictionary_entry->hf_len,
913 tvb,0,0,tlv_length);
914 PROTO_ITEM_SET_GENERATED(tlv_len_item);
917 add_tlv_to_tree(tlv_tree, tlv_item, pinfo, tvb, dictionary_entry,
918 tlv_length, offset);
919 offset += tlv_length;
920 tlv_num++;
923 proto_item_append_text(avp_item, "%d TLV(s) inside", tlv_num);
926 static void add_avp_to_tree(proto_tree* avp_tree, proto_item* avp_item, packet_info* pinfo, tvbuff_t* tvb, radius_attr_info_t* dictionary_entry, guint32 avp_length, guint32 offset) {
927 proto_item* pi;
929 if (dictionary_entry->tagged) {
930 guint tag;
932 if (avp_length == 0) {
933 pi = proto_tree_add_text(avp_tree, tvb, offset,
934 0, "AVP too short for tag");
935 PROTO_ITEM_SET_GENERATED(pi);
936 return;
939 tag = tvb_get_guint8(tvb, offset);
941 if (tag <= 0x1f) {
942 proto_tree_add_uint(avp_tree,
943 dictionary_entry->hf_tag,
944 tvb, offset, 1, tag);
946 proto_item_append_text(avp_item,
947 " Tag=0x%.2x", tag);
949 offset++;
950 avp_length--;
954 if ( dictionary_entry->dissector ) {
955 tvbuff_t* tvb_value;
956 const gchar* str;
958 tvb_value = tvb_new_subset(tvb, offset, avp_length, (gint) avp_length);
960 str = dictionary_entry->dissector(avp_tree,tvb_value,pinfo);
962 proto_item_append_text(avp_item, ": %s",str);
963 } else {
964 proto_item_append_text(avp_item, ": ");
966 dictionary_entry->type(dictionary_entry,avp_tree,pinfo,tvb,offset,avp_length,avp_item);
970 static gboolean vsa_buffer_destroy(gpointer k _U_, gpointer v, gpointer p _U_) {
971 radius_vsa_buffer* vsa_buffer = (radius_vsa_buffer*)v;
972 g_free((gpointer)vsa_buffer->data);
973 g_free(v);
974 return TRUE;
977 static void vsa_buffer_table_destroy(void *table) {
978 if (table) {
979 g_hash_table_foreach_remove((GHashTable *)table, vsa_buffer_destroy, NULL);
980 g_hash_table_destroy((GHashTable *)table);
985 void dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, guint length) {
986 proto_item* item;
987 gboolean last_eap = FALSE;
988 guint8* eap_buffer = NULL;
989 guint eap_seg_num = 0;
990 guint eap_tot_len_captured = 0;
991 guint eap_tot_len = 0;
992 proto_tree* eap_tree = NULL;
993 tvbuff_t* eap_tvb = NULL;
995 GHashTable* vsa_buffer_table = NULL;
997 /* Forces load of header fields, if not already done so */
998 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1001 * In case we throw an exception, clean up whatever stuff we've
1002 * allocated (if any).
1004 CLEANUP_PUSH_PFX(la, g_free, eap_buffer);
1005 CLEANUP_PUSH_PFX(lb, vsa_buffer_table_destroy, (void *)vsa_buffer_table);
1007 while (length > 0) {
1008 radius_attr_info_t* dictionary_entry = NULL;
1009 gint tvb_len;
1010 guint32 avp_type;
1011 guint32 avp_length;
1012 guint32 vendor_id;
1014 proto_item* avp_item;
1015 proto_item* avp_len_item;
1016 proto_tree* avp_tree;
1018 if (length < 2) {
1019 item = proto_tree_add_text(tree, tvb, offset, 0,
1020 "Not enough room in packet for AVP header");
1021 PROTO_ITEM_SET_GENERATED(item);
1022 break; /* exit outer loop, then cleanup & return */
1024 avp_type = tvb_get_guint8(tvb,offset);
1025 avp_length = tvb_get_guint8(tvb,offset+1);
1027 if (avp_length < 2) {
1028 item = proto_tree_add_text(tree, tvb, offset, 0,
1029 "AVP too short: length %u < 2", avp_length);
1030 PROTO_ITEM_SET_GENERATED(item);
1031 break; /* exit outer loop, then cleanup & return */
1034 if (length < avp_length) {
1035 item = proto_tree_add_text(tree, tvb, offset, 0,
1036 "Not enough room in packet for AVP");
1037 PROTO_ITEM_SET_GENERATED(item);
1038 break; /* exit outer loop, then cleanup & return */
1041 length -= avp_length;
1043 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(avp_type));
1045 if (! dictionary_entry ) {
1046 dictionary_entry = &no_dictionary_entry;
1049 avp_item = proto_tree_add_text(tree, tvb, offset, avp_length,
1050 "AVP: l=%u t=%s(%u)", avp_length,
1051 dictionary_entry->name, avp_type);
1053 avp_length -= 2;
1054 offset += 2;
1056 if (avp_type == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC) {
1057 radius_vendor_info_t* vendor;
1058 proto_tree* vendor_tree;
1059 gint max_offset = offset + avp_length;
1060 const gchar* vendor_str;
1062 /* XXX TODO: handle 2 byte codes for USR */
1064 if (avp_length < 4) {
1065 proto_item_append_text(avp_item, " [AVP too short; no room for vendor ID]");
1066 offset += avp_length;
1067 continue; /* while (length > 0) */
1069 vendor_id = tvb_get_ntohl(tvb,offset);
1071 avp_length -= 4;
1072 offset += 4;
1074 vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1075 vendor_str = val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown");
1076 if (!vendor) {
1077 vendor = &no_vendor;
1079 proto_item_append_text(avp_item, " v=%s(%u)", vendor_str,
1080 vendor_id);
1082 vendor_tree = proto_item_add_subtree(avp_item,vendor->ett);
1084 while (offset < max_offset) {
1085 guint32 avp_vsa_type;
1086 guint32 avp_vsa_len;
1087 guint8 avp_vsa_flags = 0;
1088 guint32 avp_vsa_header_len = vendor->type_octets + vendor->length_octets + (vendor->has_flags ? 1 : 0);
1090 switch (vendor->type_octets) {
1091 case 1:
1092 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1093 break;
1094 case 2:
1095 avp_vsa_type = tvb_get_ntohs(tvb,offset);
1096 offset += 2;
1097 break;
1098 case 4:
1099 avp_vsa_type = tvb_get_ntohl(tvb,offset);
1100 offset += 4;
1101 break;
1102 default:
1103 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1106 switch (vendor->length_octets) {
1107 case 1:
1108 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1109 break;
1110 case 0:
1111 avp_vsa_len = avp_length;
1112 break;
1113 case 2:
1114 avp_vsa_len = tvb_get_ntohs(tvb,offset);
1115 offset += 2;
1116 break;
1117 default:
1118 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1121 if (vendor->has_flags) {
1122 avp_vsa_flags = tvb_get_guint8(tvb,offset++);
1125 if (avp_vsa_len < avp_vsa_header_len) {
1126 proto_tree_add_text(tree, tvb, offset+1, 1,
1127 "[VSA too short]");
1128 break; /* exit while (offset < max_offset) loop */
1131 avp_vsa_len -= avp_vsa_header_len;
1133 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(avp_vsa_type));
1135 if ( !dictionary_entry ) {
1136 dictionary_entry = &no_dictionary_entry;
1139 if (vendor->has_flags){
1140 avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1141 "VSA: l=%u t=%s(%u) C=0x%02x",
1142 avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type, avp_vsa_flags);
1143 } else {
1144 avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1145 "VSA: l=%u t=%s(%u)",
1146 avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type);
1149 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1151 if (show_length) {
1152 avp_len_item = proto_tree_add_uint(avp_tree,
1153 dictionary_entry->hf_len,
1154 tvb,0,0,avp_length);
1155 PROTO_ITEM_SET_GENERATED(avp_len_item);
1158 if (vendor->has_flags) {
1159 radius_vsa_buffer_key key;
1160 radius_vsa_buffer* vsa_buffer = NULL;
1161 key.vendor_id = vendor_id;
1162 key.vsa_type = avp_vsa_type;
1164 if (!vsa_buffer_table) {
1165 vsa_buffer_table = g_hash_table_new(radius_vsa_hash, radius_vsa_equal);
1168 vsa_buffer = (radius_vsa_buffer *)g_hash_table_lookup(vsa_buffer_table, &key);
1169 if (vsa_buffer) {
1170 vsa_buffer->data = (guint8 *)g_realloc(vsa_buffer->data, vsa_buffer->len + avp_vsa_len);
1171 tvb_memcpy(tvb, vsa_buffer->data + vsa_buffer->len, offset, avp_vsa_len);
1172 vsa_buffer->len += avp_vsa_len;
1173 vsa_buffer->seg_num++;
1176 if (avp_vsa_flags & 0x80) {
1177 if (!vsa_buffer) {
1178 vsa_buffer = (radius_vsa_buffer *)g_malloc(sizeof(radius_vsa_buffer));
1179 vsa_buffer->key.vendor_id = vendor_id;
1180 vsa_buffer->key.vsa_type = avp_vsa_type;
1181 vsa_buffer->len = avp_vsa_len;
1182 vsa_buffer->seg_num = 1;
1183 vsa_buffer->data = (guint8 *)g_malloc(avp_vsa_len);
1184 tvb_memcpy(tvb, vsa_buffer->data, offset, avp_vsa_len);
1185 g_hash_table_insert(vsa_buffer_table, &(vsa_buffer->key), vsa_buffer);
1187 proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1188 proto_item_append_text(avp_item, ": VSA fragment[%u]", vsa_buffer->seg_num);
1189 } else {
1190 if (vsa_buffer) {
1191 tvbuff_t* vsa_tvb = NULL;
1192 proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1193 proto_item_append_text(avp_item, ": Last VSA fragment[%u]", vsa_buffer->seg_num);
1194 vsa_tvb = tvb_new_child_real_data(tvb, vsa_buffer->data, vsa_buffer->len, vsa_buffer->len);
1195 tvb_set_free_cb(vsa_tvb, g_free);
1196 add_new_data_source(pinfo, vsa_tvb, "Reassembled VSA");
1197 add_avp_to_tree(avp_tree, avp_item, pinfo, vsa_tvb, dictionary_entry, vsa_buffer->len, 0);
1198 g_hash_table_remove(vsa_buffer_table, &(vsa_buffer->key));
1199 g_free(vsa_buffer);
1201 } else {
1202 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1205 } else {
1206 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1209 offset += avp_vsa_len;
1210 } /* while (offset < max_offset) */
1211 continue; /* while (length > 0) */
1214 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1216 if (show_length) {
1217 avp_len_item = proto_tree_add_uint(avp_tree,
1218 dictionary_entry->hf_len,
1219 tvb,0,0,avp_length);
1220 PROTO_ITEM_SET_GENERATED(avp_len_item);
1223 tvb_len = tvb_length_remaining(tvb, offset);
1225 if ((gint)avp_length < tvb_len)
1226 tvb_len = avp_length;
1228 if (avp_type == RADIUS_ATTR_TYPE_EAP_MESSAGE) {
1229 eap_seg_num++;
1231 /* Show this as an EAP fragment. */
1232 if (tree)
1233 proto_tree_add_text(avp_tree, tvb, offset, tvb_len,
1234 "EAP fragment");
1236 if (eap_tvb != NULL) {
1238 * Oops, a non-consecutive EAP-Message
1239 * attribute.
1241 proto_item_append_text(avp_item, " (non-consecutive)");
1242 } else {
1244 * RFC 2869 says, in section 5.13, describing
1245 * the EAP-Message attribute:
1247 * The NAS places EAP messages received
1248 * from the authenticating peer into one
1249 * or more EAP-Message attributes and
1250 * forwards them to the RADIUS Server
1251 * within an Access-Request message.
1252 * If multiple EAP-Messages are
1253 * contained within an Access-Request or
1254 * Access-Challenge packet, they MUST be
1255 * in order and they MUST be consecutive
1256 * attributes in the Access-Request or
1257 * Access-Challenge packet.
1259 * ...
1261 * The String field contains EAP packets,
1262 * as defined in [3]. If multiple
1263 * EAP-Message attributes are present
1264 * in a packet their values should be
1265 * concatenated; this allows EAP packets
1266 * longer than 253 octets to be passed
1267 * by RADIUS.
1269 * Do reassembly of EAP-Message attributes.
1270 * We just concatenate all the attributes,
1271 * and when we see either the end of the
1272 * attribute list or a non-EAP-Message
1273 * attribute, we know we're done.
1276 if (eap_buffer == NULL)
1277 eap_buffer = (guint8 *)g_malloc(eap_tot_len_captured + tvb_len);
1278 else
1279 eap_buffer = (guint8 *)g_realloc(eap_buffer,
1280 eap_tot_len_captured + tvb_len);
1281 tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset,
1282 tvb_len);
1283 eap_tot_len_captured += tvb_len;
1284 eap_tot_len += avp_length;
1286 if ( tvb_bytes_exist(tvb, offset + avp_length + 1, 1) ) {
1287 guint8 next_type = tvb_get_guint8(tvb, offset + avp_length);
1289 if ( next_type != RADIUS_ATTR_TYPE_EAP_MESSAGE ) {
1290 /* Non-EAP-Message attribute */
1291 last_eap = TRUE;
1293 } else {
1295 * No more attributes, either because
1296 * we're at the end of the packet or
1297 * because we're at the end of the
1298 * captured packet data.
1300 last_eap = TRUE;
1303 if (last_eap && eap_buffer) {
1304 gboolean save_writable;
1306 proto_item_append_text(avp_item, " Last Segment[%u]",
1307 eap_seg_num);
1309 eap_tree = proto_item_add_subtree(avp_item,ett_eap);
1311 eap_tvb = tvb_new_child_real_data(tvb, eap_buffer,
1312 eap_tot_len_captured,
1313 eap_tot_len);
1314 tvb_set_free_cb(eap_tvb, g_free);
1315 add_new_data_source(pinfo, eap_tvb, "Reassembled EAP");
1318 * Don't free this when we're done -
1319 * it's associated with a tvbuff.
1321 eap_buffer = NULL;
1324 * Set the columns non-writable,
1325 * so that the packet list shows
1326 * this as an RADIUS packet, not
1327 * as an EAP packet.
1329 save_writable = col_get_writable(pinfo->cinfo);
1330 col_set_writable(pinfo->cinfo, FALSE);
1332 call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
1334 col_set_writable(pinfo->cinfo, save_writable);
1335 } else {
1336 proto_item_append_text(avp_item, " Segment[%u]",
1337 eap_seg_num);
1341 offset += avp_length;
1342 } else {
1343 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry,
1344 avp_length, offset);
1345 offset += avp_length;
1348 } /* while (length > 0) */
1350 CLEANUP_CALL_AND_POP_PFX(lb); /* vsa_buffer_table_destroy(vsa_buffer_table) */
1353 * Call the cleanup handler to free any reassembled data we haven't
1354 * attached to a tvbuff, and pop the handler.
1356 CLEANUP_CALL_AND_POP_PFX(la);
1359 /* This function tries to determine whether a packet is radius or not */
1360 static gboolean
1361 is_radius(tvbuff_t *tvb)
1363 guint8 code;
1364 guint16 length;
1366 code=tvb_get_guint8(tvb, 0);
1367 if (try_val_to_str_ext(code, &radius_pkt_type_codes_ext) == NULL) {
1368 return FALSE;
1371 /* Check for valid length value:
1372 * Length
1374 * The Length field is two octets. It indicates the length of the
1375 * packet including the Code, Identifier, Length, Authenticator and
1376 * Attribute fields. Octets outside the range of the Length field
1377 * MUST be treated as padding and ignored on reception. If the
1378 * packet is shorter than the Length field indicates, it MUST be
1379 * silently discarded. The minimum length is 20 and maximum length
1380 * is 4096.
1382 length=tvb_get_ntohs(tvb, 2);
1383 if ( (length<20) || (length>4096) ) {
1384 return FALSE;
1387 return TRUE;
1390 static void register_radius_fields(const char*);
1392 static int
1393 dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
1395 proto_tree *radius_tree = NULL;
1396 proto_tree *avptree = NULL;
1397 proto_item *ti, *hidden_item;
1398 proto_item *avptf;
1399 guint avplength;
1400 e_radiushdr rh;
1401 radius_info_t *rad_info;
1403 conversation_t* conversation;
1404 radius_call_info_key radius_call_key;
1405 radius_call_info_key *new_radius_call_key;
1406 radius_call_t *radius_call = NULL;
1407 static address null_address = { AT_NONE, -1, 0, NULL };
1409 /* does this look like radius ? */
1410 if(!is_radius(tvb)){
1411 return 0;
1414 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
1415 col_clear(pinfo->cinfo, COL_INFO);
1417 rh.rh_code=tvb_get_guint8(tvb,0);
1418 rh.rh_ident=tvb_get_guint8(tvb,1);
1419 rh.rh_pktlength=tvb_get_ntohs(tvb,2);
1422 /* Initialise stat info for passing to tap */
1423 rad_info = wmem_new(wmem_packet_scope(), radius_info_t);
1424 rad_info->code = 0;
1425 rad_info->ident = 0;
1426 rad_info->req_time.secs = 0;
1427 rad_info->req_time.nsecs = 0;
1428 rad_info->is_duplicate = FALSE;
1429 rad_info->request_available = FALSE;
1430 rad_info->req_num = 0; /* frame number request seen */
1431 rad_info->rspcode = 0;
1432 /* tap stat info */
1433 rad_info->code = rh.rh_code;
1434 rad_info->ident = rh.rh_ident;
1435 tap_queue_packet(radius_tap, pinfo, rad_info);
1437 col_add_fstr(pinfo->cinfo,COL_INFO,"%s(%d) (id=%d, l=%d)",
1438 val_to_str_ext_const(rh.rh_code, &radius_pkt_type_codes_ext, "Unknown Packet"),
1439 rh.rh_code, rh.rh_ident, rh.rh_pktlength);
1441 if (tree)
1443 /* Forces load of header fields, if not already done so */
1444 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1446 ti = proto_tree_add_item(tree,proto_radius, tvb, 0, rh.rh_pktlength, ENC_NA);
1447 radius_tree = proto_item_add_subtree(ti, ett_radius);
1448 proto_tree_add_uint(radius_tree,hf_radius_code, tvb, 0, 1, rh.rh_code);
1449 proto_tree_add_uint_format(radius_tree,hf_radius_id, tvb, 1, 1, rh.rh_ident,
1450 "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident);
1454 * Make sure the length is sane.
1456 if (rh.rh_pktlength < HDR_LENGTH)
1458 if (tree)
1460 proto_tree_add_uint_format_value(radius_tree, hf_radius_length,
1461 tvb, 2, 2, rh.rh_pktlength, "%u (bogus, < %u)",
1462 rh.rh_pktlength, HDR_LENGTH);
1464 return tvb_length(tvb);
1467 avplength = rh.rh_pktlength - HDR_LENGTH;
1468 if (tree)
1470 proto_tree_add_uint(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength);
1471 proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4, AUTHENTICATOR_LENGTH,ENC_NA);
1473 tvb_memcpy(tvb, authenticator, 4, AUTHENTICATOR_LENGTH);
1475 /* Conversation support REQUEST/RESPONSES */
1476 switch (rh.rh_code)
1478 case RADIUS_PKT_TYPE_ACCESS_REQUEST:
1479 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
1480 case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
1481 case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST:
1482 case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST:
1483 case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST:
1484 case RADIUS_PKT_TYPE_EVENT_REQUEST:
1485 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
1486 case RADIUS_PKT_TYPE_COA_REQUEST:
1487 case RADIUS_PKT_TYPE_ALU_STATE_REQUEST:
1488 /* Don't bother creating conversations if we're encapsulated within
1489 * an error packet, such as an ICMP destination unreachable */
1490 if (pinfo->flags.in_error_pkt)
1491 break;
1493 if (tree)
1495 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_req, tvb, 0, 0, TRUE);
1496 PROTO_ITEM_SET_HIDDEN(hidden_item);
1499 /* Keep track of the address and port whence the call came
1500 * so that we can match up requests with replies.
1502 * Because it is UDP and the reply can come from any IP
1503 * and port (not necessarily the request dest), we only
1504 * track the source IP and port of the request to match
1505 * the reply.
1509 * XXX - can we just use NO_ADDR_B? Unfortunately,
1510 * you currently still have to pass a non-null
1511 * pointer for the second address argument even
1512 * if you do that.
1514 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1515 &null_address, pinfo->ptype, pinfo->srcport,
1516 pinfo->destport, 0);
1517 if (conversation == NULL)
1519 /* It's not part of any conversation - create a new one. */
1520 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1521 &null_address, pinfo->ptype, pinfo->srcport,
1522 pinfo->destport, 0);
1525 /* Prepare the key data */
1526 radius_call_key.code = rh.rh_code;
1527 radius_call_key.ident = rh.rh_ident;
1528 radius_call_key.conversation = conversation;
1529 radius_call_key.req_time = pinfo->fd->abs_ts;
1531 /* Look up the request */
1532 radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
1533 if (radius_call != NULL)
1535 /* We've seen a request with this ID, with the same
1536 destination, before - but was it *this* request? */
1537 if (pinfo->fd->num != radius_call->req_num)
1539 /* No, so it's a duplicate request. Mark it as such. */
1540 rad_info->is_duplicate = TRUE;
1541 rad_info->req_num = radius_call->req_num;
1542 col_append_fstr(pinfo->cinfo, COL_INFO,
1543 ", Duplicate Request ID:%u", rh.rh_ident);
1545 if (tree)
1547 proto_item* item;
1548 hidden_item = proto_tree_add_uint(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1549 PROTO_ITEM_SET_HIDDEN(hidden_item);
1550 item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0,0, rh.rh_ident);
1551 PROTO_ITEM_SET_GENERATED(item);
1555 else
1557 /* Prepare the value data.
1558 "req_num" and "rsp_num" are frame numbers;
1559 frame numbers are 1-origin, so we use 0
1560 to mean "we don't yet know in which frame
1561 the reply for this call appears". */
1562 new_radius_call_key = wmem_new(wmem_file_scope(), radius_call_info_key);
1563 *new_radius_call_key = radius_call_key;
1564 radius_call = wmem_new(wmem_file_scope(), radius_call_t);
1565 radius_call->req_num = pinfo->fd->num;
1566 radius_call->rsp_num = 0;
1567 radius_call->ident = rh.rh_ident;
1568 radius_call->code = rh.rh_code;
1569 radius_call->responded = FALSE;
1570 radius_call->req_time = pinfo->fd->abs_ts;
1571 radius_call->rspcode = 0;
1573 /* Store it */
1574 g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
1576 if (tree && radius_call->rsp_num)
1578 proto_item* item;
1579 item = proto_tree_add_uint_format(radius_tree,
1580 hf_radius_rsp_frame, tvb, 0, 0, radius_call->rsp_num,
1581 "The response to this request is in frame %u",
1582 radius_call->rsp_num);
1583 PROTO_ITEM_SET_GENERATED(item);
1585 break;
1586 case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
1587 case RADIUS_PKT_TYPE_ACCESS_REJECT:
1588 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
1589 case RADIUS_PKT_TYPE_PASSWORD_ACK:
1590 case RADIUS_PKT_TYPE_PASSWORD_REJECT:
1591 case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE:
1592 case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE:
1593 case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE:
1594 case RADIUS_PKT_TYPE_EVENT_RESPONSE:
1595 case RADIUS_PKT_TYPE_DISCONNECT_ACK:
1596 case RADIUS_PKT_TYPE_DISCONNECT_NAK:
1597 case RADIUS_PKT_TYPE_COA_ACK:
1598 case RADIUS_PKT_TYPE_COA_NAK:
1599 case RADIUS_PKT_TYPE_ACCESS_CHALLENGE:
1600 case RADIUS_PKT_TYPE_ALU_STATE_ACCEPT:
1601 case RADIUS_PKT_TYPE_ALU_STATE_REJECT:
1602 case RADIUS_PKT_TYPE_ALU_STATE_ERROR:
1603 /* Don't bother finding conversations if we're encapsulated within
1604 * an error packet, such as an ICMP destination unreachable */
1605 if (pinfo->flags.in_error_pkt)
1606 break;
1608 if (tree)
1610 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE);
1611 PROTO_ITEM_SET_HIDDEN(hidden_item);
1614 /* Check for RADIUS response. A response must match a call that
1615 * we've seen, and the response must be sent to the same
1616 * port and address that the call came from.
1618 * Because it is UDP and the reply can come from any IP
1619 * and port (not necessarily the request dest), we only
1620 * track the source IP and port of the request to match
1621 * the reply.
1624 /* XXX - can we just use NO_ADDR_B? Unfortunately,
1625 * you currently still have to pass a non-null
1626 * pointer for the second address argument even
1627 * if you do that.
1629 conversation = find_conversation(pinfo->fd->num, &null_address,
1630 &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1631 if (conversation != NULL)
1633 /* Look only for matching request, if
1634 matching conversation is available. */
1635 /* Prepare the key data */
1636 radius_call_key.code = rh.rh_code;
1637 radius_call_key.ident = rh.rh_ident;
1638 radius_call_key.conversation = conversation;
1639 radius_call_key.req_time = pinfo->fd->abs_ts;
1641 radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
1642 if (radius_call)
1644 /* Indicate the frame to which this is a reply. */
1645 if (radius_call->req_num)
1647 rad_info->request_available = TRUE;
1648 rad_info->req_num = radius_call->req_num;
1649 radius_call->responded = TRUE;
1651 if (tree)
1653 nstime_t delta;
1654 proto_item* item;
1655 item = proto_tree_add_uint_format(radius_tree,
1656 hf_radius_req_frame, tvb, 0, 0,
1657 radius_call->req_num,
1658 "This is a response to a request in frame %u",
1659 radius_call->req_num);
1660 PROTO_ITEM_SET_GENERATED(item);
1661 nstime_delta(&delta, &pinfo->fd->abs_ts, &radius_call->req_time);
1662 item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
1663 PROTO_ITEM_SET_GENERATED(item);
1667 if (radius_call->rsp_num == 0)
1669 /* We have not yet seen a response to that call, so
1670 this must be the first response; remember its
1671 frame number. */
1672 radius_call->rsp_num = pinfo->fd->num;
1674 else
1676 /* We have seen a response to this call - but was it
1677 *this* response? (disregard provisional responses) */
1678 if ( (radius_call->rsp_num != pinfo->fd->num) && (radius_call->rspcode == rh.rh_code) )
1680 /* No, so it's a duplicate response. Mark it as such. */
1681 rad_info->is_duplicate = TRUE;
1682 col_append_fstr(pinfo->cinfo, COL_INFO,
1683 ", Duplicate Response ID:%u", rh.rh_ident);
1685 if (tree)
1687 proto_item* item;
1688 hidden_item = proto_tree_add_uint(radius_tree,
1689 hf_radius_dup, tvb, 0,0, rh.rh_ident);
1690 PROTO_ITEM_SET_HIDDEN(hidden_item);
1691 item = proto_tree_add_uint(radius_tree,
1692 hf_radius_rsp_dup, tvb, 0, 0, rh.rh_ident);
1693 PROTO_ITEM_SET_GENERATED(item);
1697 /* Now store the response code (after comparison above) */
1698 radius_call->rspcode = rh.rh_code;
1699 rad_info->rspcode = rh.rh_code;
1702 break;
1703 default:
1704 break;
1707 if (radius_call)
1709 rad_info->req_time.secs = radius_call->req_time.secs;
1710 rad_info->req_time.nsecs = radius_call->req_time.nsecs;
1713 if (avplength > 0)
1715 /* list the attribute value pairs */
1716 avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
1717 avplength, "Attribute Value Pairs");
1718 avptree = proto_item_add_subtree(avptf, ett_radius_avp);
1719 dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
1720 avplength);
1723 return tvb_length(tvb);
1727 static void register_attrs(gpointer k _U_, gpointer v, gpointer p) {
1728 radius_attr_info_t* a = (radius_attr_info_t *)v;
1729 int i;
1730 gint* ett = &(a->ett);
1731 gchar* abbrev = wmem_strdup_printf(wmem_epan_scope(), "radius.%s", a->name);
1732 hf_register_info hfri[] = {
1733 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1734 { NULL, { NULL,NULL, FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1735 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1736 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}
1738 guint len_hf = 2;
1739 hfett_t* ri = (hfett_t *)p;
1741 for(i=0; abbrev[i]; i++) {
1742 if(abbrev[i] == '-') abbrev[i] = '_';
1743 if(abbrev[i] == '/') abbrev[i] = '_';
1746 hfri[0].p_id = &(a->hf);
1747 hfri[1].p_id = &(a->hf_len);
1749 hfri[0].hfinfo.name = a->name;
1750 hfri[0].hfinfo.abbrev = abbrev;
1752 hfri[1].hfinfo.name = "Length";
1753 hfri[1].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.len", abbrev);
1754 hfri[1].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Length", a->name);
1756 if (a->type == radius_integer) {
1757 hfri[0].hfinfo.type = FT_UINT32;
1758 hfri[0].hfinfo.display = BASE_DEC;
1760 hfri[2].p_id = &(a->hf_alt);
1761 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1762 hfri[2].hfinfo.abbrev = abbrev;
1763 hfri[2].hfinfo.type = FT_UINT64;
1764 hfri[2].hfinfo.display = BASE_DEC;
1766 if (a->vs) {
1767 hfri[0].hfinfo.strings = VALS(a->vs);
1770 len_hf++;
1771 }else if (a->type == radius_signed) {
1772 hfri[0].hfinfo.type = FT_INT32;
1773 hfri[0].hfinfo.display = BASE_DEC;
1775 hfri[2].p_id = &(a->hf_alt);
1776 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1777 hfri[2].hfinfo.abbrev = abbrev;
1778 hfri[2].hfinfo.type = FT_INT64;
1779 hfri[2].hfinfo.display = BASE_DEC;
1781 if (a->vs) {
1782 hfri[0].hfinfo.strings = VALS(a->vs);
1785 len_hf++;
1786 } else if (a->type == radius_string) {
1787 hfri[0].hfinfo.type = FT_STRING;
1788 hfri[0].hfinfo.display = BASE_NONE;
1790 if (a->encrypt != 0) {
1792 * This attribute is encrypted, so create an
1793 * alternative field for the encrypted value.
1795 hfri[2].p_id = &(a->hf_alt);
1796 hfri[2].hfinfo.name = wmem_strdup_printf(wmem_epan_scope(), "%s (encrypted)", a->name);
1797 hfri[2].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s_encrypted", abbrev);
1798 hfri[2].hfinfo.type = FT_BYTES;
1799 hfri[2].hfinfo.display = BASE_NONE;
1801 len_hf++;
1803 } else if (a->type == radius_octets) {
1804 hfri[0].hfinfo.type = FT_BYTES;
1805 hfri[0].hfinfo.display = BASE_NONE;
1806 } else if (a->type == radius_ipaddr) {
1807 hfri[0].hfinfo.type = FT_IPv4;
1808 hfri[0].hfinfo.display = BASE_NONE;
1809 } else if (a->type == radius_ipv6addr) {
1810 hfri[0].hfinfo.type = FT_IPv6;
1811 hfri[0].hfinfo.display = BASE_NONE;
1812 } else if (a->type == radius_ipv6prefix) {
1813 hfri[0].hfinfo.type = FT_BYTES;
1814 hfri[0].hfinfo.display = BASE_NONE;
1815 } else if (a->type == radius_ipxnet) {
1816 hfri[0].hfinfo.type = FT_IPXNET;
1817 hfri[0].hfinfo.display = BASE_NONE;
1818 } else if (a->type == radius_date) {
1819 hfri[0].hfinfo.type = FT_ABSOLUTE_TIME;
1820 hfri[0].hfinfo.display = ABSOLUTE_TIME_LOCAL;
1821 } else if (a->type == radius_abinary) {
1822 hfri[0].hfinfo.type = FT_BYTES;
1823 hfri[0].hfinfo.display = BASE_NONE;
1824 } else if (a->type == radius_ifid) {
1825 hfri[0].hfinfo.type = FT_BYTES;
1826 hfri[0].hfinfo.display = BASE_NONE;
1827 } else if (a->type == radius_combo_ip) {
1828 hfri[0].hfinfo.type = FT_IPv4;
1829 hfri[0].hfinfo.display = BASE_NONE;
1831 hfri[2].p_id = &(a->hf_alt);
1832 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1833 hfri[2].hfinfo.abbrev = wmem_strdup(wmem_epan_scope(), abbrev);
1834 hfri[2].hfinfo.type = FT_IPv6;
1835 hfri[2].hfinfo.display = BASE_NONE;
1837 len_hf++;
1838 } else if (a->type == radius_tlv) {
1839 hfri[0].hfinfo.type = FT_BYTES;
1840 hfri[0].hfinfo.display = BASE_NONE;
1841 } else {
1842 hfri[0].hfinfo.type = FT_BYTES;
1843 hfri[0].hfinfo.display = BASE_NONE;
1846 if (a->tagged) {
1847 hfri[len_hf].p_id = &(a->hf_tag);
1848 hfri[len_hf].hfinfo.name = "Tag";
1849 hfri[len_hf].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.tag", abbrev);
1850 hfri[len_hf].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Tag", a->name);
1851 hfri[len_hf].hfinfo.type = FT_UINT8;
1852 hfri[len_hf].hfinfo.display = BASE_HEX;
1853 len_hf++;
1856 wmem_array_append(ri->hf,hfri,len_hf);
1857 wmem_array_append_one(ri->ett,ett);
1859 if (a->tlvs_by_id) {
1860 g_hash_table_foreach(a->tlvs_by_id,register_attrs,ri);
1864 static void register_vendors(gpointer k _U_, gpointer v, gpointer p) {
1865 radius_vendor_info_t* vnd = (radius_vendor_info_t *)v;
1866 hfett_t* ri = (hfett_t *)p;
1867 value_string vnd_vs;
1868 gint* ett_p = &(vnd->ett);
1870 vnd_vs.value = vnd->code;
1871 vnd_vs.strptr = vnd->name;
1873 wmem_array_append_one(ri->vend_vs,vnd_vs);
1874 wmem_array_append_one(ri->ett,ett_p);
1876 g_hash_table_foreach(vnd->attrs_by_id,register_attrs,ri);
1880 extern void radius_register_avp_dissector(guint32 vendor_id, guint32 attribute_id, radius_avp_dissector_t radius_avp_dissector) {
1881 radius_vendor_info_t* vendor;
1882 radius_attr_info_t* dictionary_entry;
1883 GHashTable* by_id;
1885 DISSECTOR_ASSERT(radius_avp_dissector != NULL);
1887 if (vendor_id) {
1888 vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1890 if ( ! vendor ) {
1891 vendor = (radius_vendor_info_t *)g_malloc(sizeof(radius_vendor_info_t));
1893 vendor->name = g_strdup_printf("%s-%u",
1894 val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown"),
1895 vendor_id);
1896 vendor->code = vendor_id;
1897 vendor->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1898 vendor->ett = no_vendor.ett;
1900 /* XXX: Default "standard" values: Should be parameters ? */
1901 vendor->type_octets = 1;
1902 vendor->length_octets = 1;
1903 vendor->has_flags = FALSE;
1905 g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(vendor->code),vendor);
1906 g_hash_table_insert(dict->vendors_by_name,(gpointer)(vendor->name),vendor);
1909 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1910 by_id = vendor->attrs_by_id;
1911 } else {
1912 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1913 by_id = dict->attrs_by_id;
1916 if (!dictionary_entry) {
1917 dictionary_entry = (radius_attr_info_t *)g_malloc(sizeof(radius_attr_info_t));
1919 dictionary_entry->name = g_strdup_printf("Unknown-Attribute-%u",attribute_id);
1920 dictionary_entry->code = attribute_id;
1921 dictionary_entry->encrypt = 0;
1922 dictionary_entry->type = NULL;
1923 dictionary_entry->vs = NULL;
1924 dictionary_entry->hf = no_dictionary_entry.hf;
1925 dictionary_entry->tagged = 0;
1926 dictionary_entry->hf_tag = -1;
1927 dictionary_entry->hf_len = no_dictionary_entry.hf_len;
1928 dictionary_entry->ett = no_dictionary_entry.ett;
1929 dictionary_entry->tlvs_by_id = NULL;
1931 g_hash_table_insert(by_id,GUINT_TO_POINTER(dictionary_entry->code),dictionary_entry);
1934 dictionary_entry->dissector = radius_avp_dissector;
1938 /* Discard and init any state we've saved */
1939 static void
1940 radius_init_protocol(void)
1942 if (radius_calls != NULL)
1944 g_hash_table_destroy(radius_calls);
1945 radius_calls = NULL;
1948 radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
1951 static void register_radius_fields(const char* unused _U_) {
1952 hf_register_info base_hf[] = {
1953 { &hf_radius_req,
1954 { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1955 "TRUE if RADIUS request", HFILL }},
1956 { &hf_radius_rsp,
1957 { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1958 "TRUE if RADIUS response", HFILL }},
1959 { &hf_radius_req_frame,
1960 { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1961 NULL, HFILL }},
1962 { &hf_radius_rsp_frame,
1963 { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1964 NULL, HFILL }},
1965 { &hf_radius_time,
1966 { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
1967 "Timedelta between Request and Response", HFILL }},
1968 { &hf_radius_code,
1969 { "Code","radius.code", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &radius_pkt_type_codes_ext, 0x0,
1970 NULL, HFILL }},
1971 { &hf_radius_id,
1972 { "Identifier", "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0,
1973 NULL, HFILL }},
1974 { &hf_radius_authenticator,
1975 { "Authenticator", "radius.authenticator", FT_BYTES, BASE_NONE, NULL, 0x0,
1976 NULL, HFILL }},
1977 { &hf_radius_length,
1978 { "Length","radius.length", FT_UINT16, BASE_DEC, NULL, 0x0,
1979 NULL, HFILL }},
1980 { &(no_dictionary_entry.hf),
1981 { "Unknown-Attribute","radius.Unknown_Attribute", FT_BYTES, BASE_NONE, NULL, 0x0,
1982 NULL, HFILL }},
1983 { &(no_dictionary_entry.hf_len),
1984 { "Unknown-Attribute Length","radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0,
1985 NULL, HFILL }},
1986 { &hf_radius_chap_password,
1987 { "CHAP-Password","radius.CHAP_Password", FT_BYTES, BASE_NONE, NULL, 0x0,
1988 NULL, HFILL }},
1989 { &hf_radius_chap_ident,
1990 { "CHAP Ident","radius.CHAP_Ident", FT_UINT8, BASE_HEX, NULL, 0x0,
1991 NULL, HFILL }},
1992 { &hf_radius_chap_string,
1993 { "CHAP String","radius.CHAP_String", FT_BYTES, BASE_NONE, NULL, 0x0,
1994 NULL, HFILL }},
1995 { &hf_radius_framed_ip_address,
1996 { "Framed-IP-Address","radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0,
1997 NULL, HFILL }},
1998 { &hf_radius_login_ip_host,
1999 { "Login-IP-Host","radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0,
2000 NULL, HFILL }},
2001 { &hf_radius_framed_ipx_network,
2002 { "Framed-IPX-Network","radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0,
2003 NULL, HFILL }},
2004 { &hf_radius_cosine_vpi,
2005 { "Cosine-VPI","radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0,
2006 NULL, HFILL }},
2007 { &hf_radius_cosine_vci,
2008 { "Cosine-VCI","radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
2009 NULL, HFILL }},
2010 { &hf_radius_dup,
2011 { "Duplicate Message", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2012 NULL, HFILL }},
2013 { &hf_radius_req_dup,
2014 { "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2015 NULL, HFILL }},
2016 { &hf_radius_rsp_dup,
2017 { "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2018 NULL, HFILL }},
2019 { &hf_radius_ascend_data_filter,
2020 { "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES, BASE_NONE, NULL, 0x0,
2021 NULL, HFILL }}
2024 gint *base_ett[] = {
2025 &ett_radius,
2026 &ett_radius_avp,
2027 &ett_eap,
2028 &ett_chap,
2029 &(no_dictionary_entry.ett),
2030 &(no_vendor.ett),
2033 hfett_t ri;
2034 char* dir = NULL;
2035 gchar* dict_err_str = NULL;
2037 ri.hf = wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info));
2038 ri.ett = wmem_array_new(wmem_epan_scope(), sizeof(gint *));
2039 ri.vend_vs = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2041 wmem_array_append(ri.hf, base_hf, array_length(base_hf));
2042 wmem_array_append(ri.ett, base_ett, array_length(base_ett));
2044 dir = get_persconffile_path("radius", FALSE);
2046 if (test_for_directory(dir) != EISDIR) {
2047 /* Although dir isn't a directory it may still use memory */
2048 g_free(dir);
2050 dir = get_datafile_path("radius");
2052 if (test_for_directory(dir) != EISDIR) {
2053 g_free(dir);
2054 dir = NULL;
2058 if (dir) {
2059 radius_load_dictionary(dict,dir,"dictionary",&dict_err_str);
2061 if (dict_err_str) {
2062 report_failure("radius: %s",dict_err_str);
2063 g_free(dict_err_str);
2066 g_hash_table_foreach(dict->attrs_by_id,register_attrs,&ri);
2067 g_hash_table_foreach(dict->vendors_by_id,register_vendors,&ri);
2070 g_free(dir);
2072 proto_register_field_array(proto_radius,(hf_register_info*)wmem_array_get_raw(ri.hf),wmem_array_get_count(ri.hf));
2073 proto_register_subtree_array((gint**)wmem_array_get_raw(ri.ett), wmem_array_get_count(ri.ett));
2075 no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
2078 * Handle attributes that have a special format.
2080 radius_register_avp_dissector(0,3,dissect_chap_password);
2081 radius_register_avp_dissector(0,8,dissect_framed_ip_address);
2082 radius_register_avp_dissector(0,14,dissect_login_ip_host);
2083 radius_register_avp_dissector(0,23,dissect_framed_ipx_network);
2084 radius_register_avp_dissector(VENDOR_COSINE,5,dissect_cosine_vpvc);
2086 * XXX - should we just call dissect_ascend_data_filter()
2087 * in radius_abinary()?
2089 * Note that there is no attribute 242 in dictionary.redback.
2091 radius_register_avp_dissector(VENDOR_ASCEND,242,dissect_ascend_data_filter);
2092 radius_register_avp_dissector(VENDOR_REDBACK,242,dissect_ascend_data_filter);
2093 radius_register_avp_dissector(0,242,dissect_ascend_data_filter);
2096 * XXX - we should special-case Cisco attribute 252; see the comment in
2097 * dictionary.cisco.
2099 radius_register_avp_dissector(VENDOR_THE3GPP,23,dissect_radius_3gpp_ms_tmime_zone);
2103 void
2104 proto_register_radius(void)
2106 module_t *radius_module;
2108 proto_radius = proto_register_protocol("Radius Protocol", "RADIUS", "radius");
2109 new_register_dissector("radius", dissect_radius, proto_radius);
2110 register_init_routine(&radius_init_protocol);
2111 radius_module = prefs_register_protocol(proto_radius, proto_reg_handoff_radius);
2112 prefs_register_string_preference(radius_module,"shared_secret","Shared Secret",
2113 "Shared secret used to decode User Passwords",
2114 &shared_secret);
2115 prefs_register_bool_preference(radius_module,"show_length","Show AVP Lengths",
2116 "Whether to add or not to the tree the AVP's payload length",
2117 &show_length);
2118 prefs_register_uint_preference(radius_module, "alternate_port","Alternate Port",
2119 "An alternate UDP port to decode as RADIUS", 10, &alt_port_pref);
2120 prefs_register_uint_preference(radius_module, "request_ttl", "Request TimeToLive",
2121 "Time to live for a radius request used for matching it with a response", 10, &request_ttl);
2122 radius_tap = register_tap("radius");
2123 proto_register_prefix("radius",register_radius_fields);
2125 dict = (radius_dictionary_t *)g_malloc(sizeof(radius_dictionary_t));
2126 dict->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
2127 dict->attrs_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2128 dict->vendors_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
2129 dict->vendors_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2130 dict->tlvs_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2133 void
2134 proto_reg_handoff_radius(void)
2136 static gboolean initialized = FALSE;
2137 static dissector_handle_t radius_handle;
2138 static guint alt_port;
2140 if (!initialized) {
2141 radius_handle = find_dissector("radius");
2142 dissector_add_uint("udp.port", UDP_PORT_RADIUS, radius_handle);
2143 dissector_add_uint("udp.port", UDP_PORT_RADIUS_NEW, radius_handle);
2144 dissector_add_uint("udp.port", UDP_PORT_RADACCT, radius_handle);
2145 dissector_add_uint("udp.port", UDP_PORT_RADACCT_NEW, radius_handle);
2146 dissector_add_uint("udp.port", UDP_PORT_DAE_OLD, radius_handle);
2147 dissector_add_uint("udp.port", UDP_PORT_DAE, radius_handle);
2149 eap_handle = find_dissector("eap");
2151 initialized = TRUE;
2152 } else {
2153 if (alt_port != 0)
2154 dissector_delete_uint("udp.port", alt_port, radius_handle);
2157 if (alt_port_pref != 0)
2158 dissector_add_uint("udp.port", alt_port_pref, radius_handle);
2160 alt_port = alt_port_pref;
2164 * Editor modelines - http://www.wireshark.org/tools/modelines.html
2166 * Local variables:
2167 * c-basic-offset: 8
2168 * tab-width: 8
2169 * indent-tabs-mode: t
2170 * End:
2172 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2173 * :indentSize=8:tabSize=8:noTabs=false: