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@gmail.com> - use FreeRADIUS' dictionary
7 * Changed 10/2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> - add Conversations support
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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
41 * http://www.iana.org/assignments/radius-types
46 TO (re)DO: (see svn rev 14786)
47 - dissect_3gpp_ipv6_dns_servers()
57 #include <epan/packet.h>
58 #include <epan/prefs.h>
59 #include <epan/report_err.h>
60 #include <epan/crypt/crypt-md5.h>
61 #include <epan/sminmpec.h>
62 #include <epan/filesystem.h>
63 #include <epan/conversation.h>
65 #include <epan/addr_resolv.h>
66 #include <epan/emem.h>
67 #include <epan/garrayfix.h>
69 #include "packet-radius.h"
71 typedef struct _e_radiushdr {
83 #define AUTHENTICATOR_LENGTH 16
84 #define RD_HDR_LENGTH 4
85 #define HDR_LENGTH (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
87 #define UDP_PORT_RADIUS 1645
88 #define UDP_PORT_RADIUS_NEW 1812
89 #define UDP_PORT_RADACCT 1646
90 #define UDP_PORT_RADACCT_NEW 1813
92 static radius_dictionary_t* dict = NULL;
94 static int proto_radius = -1;
96 static int hf_radius_req = -1;
97 static int hf_radius_rsp = -1;
98 static int hf_radius_req_frame = -1;
99 static int hf_radius_rsp_frame = -1;
100 static int hf_radius_time = -1;
102 static int hf_radius_dup = -1;
103 static int hf_radius_req_dup = -1;
104 static int hf_radius_rsp_dup = -1;
106 static int hf_radius_id = -1;
107 static int hf_radius_code = -1;
108 static int hf_radius_length = -1;
109 static int hf_radius_authenticator = -1;
111 static int hf_radius_framed_ip_address = -1;
112 static int hf_radius_login_ip_host = -1;
113 static int hf_radius_framed_ipx_network = -1;
115 static int hf_radius_cosine_vpi = -1;
116 static int hf_radius_cosine_vci = -1;
118 static gint ett_radius = -1;
119 static gint ett_radius_avp = -1;
120 static gint ett_eap = -1;
123 * Define the tap for radius
125 static int radius_tap = -1;
127 radius_vendor_info_t no_vendor = {"Unknown Vendor",0,NULL,-1};
129 radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute",0,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1 };
131 static dissector_handle_t eap_handle;
132 static dissector_handle_t radius_handle;
134 static const gchar* shared_secret = "";
135 static gboolean show_length = FALSE;
136 static guint alt_port = 0;
137 static guint alt_port_pref = 0;
139 static guint8 authenticator[AUTHENTICATOR_LENGTH];
141 static const value_string* radius_vendors = NULL;
143 /* http://www.iana.org/assignments/radius-types */
144 static const value_string radius_vals[] =
146 {RADIUS_ACCESS_REQUEST, "Access-Request"}, /* 1 RFC2865 */
147 {RADIUS_ACCESS_ACCEPT, "Access-Accept"}, /* 2 RFC2865 */
148 {RADIUS_ACCESS_REJECT, "Access-Reject"}, /* 3 RFC2865 */
149 {RADIUS_ACCOUNTING_REQUEST, "Accounting-Request"}, /* 4 RFC2865 */
150 {RADIUS_ACCOUNTING_RESPONSE, "Accounting-Response"}, /* 5 RFC2865 */
151 {RADIUS_ACCOUNTING_STATUS, "Accounting-Status"}, /* 6 RFC2865 */
152 {RADIUS_ACCESS_PASSWORD_REQUEST, "Password-Request"}, /* 7 RFC3575 */
153 {RADIUS_ACCESS_PASSWORD_ACK, "Password-Ack"}, /* 8 RFC3575 */
154 {RADIUS_ACCESS_PASSWORD_REJECT, "Password-Reject"}, /* 9 RFC3575 */
155 {RADIUS_ACCOUNTING_MESSAGE, "Accounting-Message"}, /* 10 RFC3575 */
156 {RADIUS_ACCESS_CHALLENGE, "Access-challenge"}, /* 11 RFC2865 */
157 {RADIUS_STATUS_SERVER, "Status-Server"}, /* 12 RFC2865 */
158 {RADIUS_STATUS_CLIENT, "Status-Client"}, /* 13 RFC2865 */
160 21 Resource-Free-Request [RFC3575]
161 22 Resource-Free-Response [RFC3575]
162 23 Resource-Query-Request [RFC3575]
163 24 Resource-Query-Response [RFC3575]
164 25 Alternate-Resource-
165 Reclaim-Request [RFC3575]
166 26 NAS-Reboot-Request [RFC3575]
168 {RADIUS_VENDOR_SPECIFIC_CODE, "Vendor-Specific"}, /* 26 */
170 27 NAS-Reboot-Response [RFC3575]
173 {RADIUS_ASCEND_ACCESS_NEXT_CODE, "Next-Passcode"}, /* 29 RFC3575 */
174 {RADIUS_ASCEND_ACCESS_NEW_PIN, "New-Pin"}, /* 30 RFC3575 */
175 {31, "Terminate-Session"}, /* 31 RFC3575 */
176 {RADIUS_ASCEND_PASSWORD_EXPIRED, "Password-Expired"}, /* 32 RFC3575 */
177 {RADIUS_ASCEND_ACCESS_EVENT_REQUEST, "Event-Request"}, /* 33 RFC3575 */
178 {RADIUS_ASCEND_ACCESS_EVENT_RESPONSE, "Event-Response"}, /* 34 RFC3575 */
179 {RADIUS_DISCONNECT_REQUEST, "Disconnect-Request"}, /* 40 RFC3575 */
180 {RADIUS_DISCONNECT_REQUEST_ACK, "Disconnect-ACK"}, /* 41 RFC3575 */
181 {RADIUS_DISCONNECT_REQUEST_NAK, "Disconnect-NAK"} , /* 42 RFC3575 */
182 {RADIUS_CHANGE_FILTER_REQUEST, "CoA-Request"}, /* 43 */
183 {RADIUS_CHANGE_FILTER_REQUEST_ACK, "CoA-ACK"}, /* 44 */
184 {RADIUS_CHANGE_FILTER_REQUEST_NAK, "CoA-NAK"}, /* 45 */
186 50 IP-Address-Allocate [RFC3575]
187 51 IP-Address-Release [RFC3575]
188 250-253 Experimental Use [RFC3575]
189 254 Reserved [RFC3575]
191 {RADIUS_RESERVED, "Reserved"},
196 * Init Hash table stuff for converation
199 typedef struct _radius_call_info_key
203 conversation_t *conversation;
205 } radius_call_info_key;
207 static GMemChunk *radius_call_info_key_chunk;
208 static GMemChunk *radius_call_info_value_chunk;
209 static GHashTable *radius_calls;
212 static gint radius_call_equal(gconstpointer k1, gconstpointer k2)
214 const radius_call_info_key* key1 = (const radius_call_info_key*) k1;
215 const radius_call_info_key* key2 = (const radius_call_info_key*) k2;
217 if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
220 nstime_delta(&delta, &key1->req_time, &key2->req_time);
221 if (abs( (int) nstime_to_sec(&delta)) > (double) 5) return 0;
223 if (key1->code == key2->code)
225 /* check the request and response are of the same code type */
226 if (key1->code == RADIUS_ACCESS_REQUEST && ( key2->code == RADIUS_ACCESS_ACCEPT || key2->code == RADIUS_ACCESS_REJECT ) )
229 if (key1->code == RADIUS_ACCOUNTING_REQUEST && key2->code == RADIUS_ACCOUNTING_RESPONSE )
232 if (key1->code == RADIUS_ACCESS_PASSWORD_REQUEST && ( key2->code == RADIUS_ACCESS_PASSWORD_ACK || key2->code == RADIUS_ACCESS_PASSWORD_REJECT ) )
235 if (key1->code == RADIUS_ASCEND_ACCESS_EVENT_REQUEST && key2->code == RADIUS_ASCEND_ACCESS_EVENT_RESPONSE )
238 if (key1->code == RADIUS_DISCONNECT_REQUEST && ( key2->code == RADIUS_DISCONNECT_REQUEST_ACK || key2->code == RADIUS_DISCONNECT_REQUEST_NAK ) )
241 if (key1->code == RADIUS_CHANGE_FILTER_REQUEST && ( key2->code == RADIUS_CHANGE_FILTER_REQUEST_ACK || key2->code == RADIUS_CHANGE_FILTER_REQUEST_NAK ) )
247 /* Calculate a hash key */
248 static guint radius_call_hash(gconstpointer k)
250 const radius_call_info_key* key = (const radius_call_info_key*) k;
252 return key->ident + /*key->code + */ key->conversation->index;
256 static const gchar *dissect_framed_ip_address(proto_tree* tree, tvbuff_t* tvb) {
262 len = tvb_length(tvb);
264 return "[wrong length for IP address]";
266 ip=tvb_get_ipv4(tvb,0);
269 if (ip_h == 0xFFFFFFFF) {
271 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
272 tvb, 0, len, ip, "Framed-IP-Address: %s", str);
273 } else if (ip_h == 0xFFFFFFFE) {
275 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
276 tvb, 0, len, ip, "Framed-IP-Address: %s", str);
278 str = ip_to_str((guint8 *)&ip);
279 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
280 tvb, 0, len, ip, "Framed-IP-Address: %s (%s)",
281 get_hostname(ip), str);
287 static const gchar *dissect_login_ip_host(proto_tree* tree, tvbuff_t* tvb) {
293 len = tvb_length(tvb);
295 return "[wrong length for IP address]";
297 ip=tvb_get_ipv4(tvb,0);
300 if (ip_h == 0xFFFFFFFF) {
301 str = "User-selected";
302 proto_tree_add_ipv4_format(tree, hf_radius_login_ip_host,
303 tvb, 0, len, ip, "Login-IP-Host: %s", str);
304 } else if (ip_h == 0) {
305 str = "NAS-selected";
306 proto_tree_add_ipv4_format(tree, hf_radius_login_ip_host,
307 tvb, 0, len, ip, "Login-IP-Host: %s", str);
309 str = ip_to_str((guint8 *)&ip);
310 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
311 tvb, 0, len, ip, "Login-IP-Host: %s (%s)",
312 get_hostname(ip), str);
318 static const gchar *dissect_framed_ipx_network(proto_tree* tree, tvbuff_t* tvb) {
323 len = tvb_length(tvb);
325 return "[wrong length for IPX network]";
327 net=tvb_get_ntohl(tvb,0);
329 if (net == 0xFFFFFFFE)
330 str = "NAS-selected";
332 str = ep_strdup_printf("0x%08X", net);
333 proto_tree_add_ipxnet_format(tree, hf_radius_framed_ipx_network, tvb, 0,
334 len, net, "Framed-IPX-Network: %s", str);
339 static const gchar* dissect_cosine_vpvc(proto_tree* tree, tvbuff_t* tvb) {
342 if ( tvb_length(tvb) != 4 )
343 return "[Wrong Length for VP/VC AVP]";
345 vpi = tvb_get_ntohs(tvb,0);
346 vci = tvb_get_ntohs(tvb,2);
348 proto_tree_add_uint(tree,hf_radius_cosine_vpi,tvb,0,2,vpi);
349 proto_tree_add_uint(tree,hf_radius_cosine_vci,tvb,2,2,vci);
351 return ep_strdup_printf("%u/%u",vpi,vci);
355 radius_decrypt_avp(gchar *dest,int dest_len,tvbuff_t *tvb,int offset,int length)
358 md5_byte_t digest[16];
360 size_t totlen, returned_length;
364 DISSECTOR_ASSERT(dest_len > 2); /* \"\"\0 */
368 dest_len -= 1; /* Need to add trailing \" */
371 md5_append(&md_ctx,(const guint8*)shared_secret,strlen(shared_secret));
372 md5_append(&md_ctx,authenticator, AUTHENTICATOR_LENGTH);
373 md5_finish(&md_ctx,digest);
375 pd = tvb_get_ptr(tvb,offset,length);
376 for( i = 0 ; i < AUTHENTICATOR_LENGTH && i < length ; i++ ) {
377 c = pd[i] ^ digest[i];
379 returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
381 totlen += MIN(returned_length, dest_len-totlen-1);
383 returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
385 totlen += MIN(returned_length, dest_len-totlen-1);
389 if ( isprint(pd[i]) ) {
390 returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
392 totlen += MIN(returned_length, dest_len-totlen-1);
394 returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
396 totlen += MIN(returned_length, dest_len-totlen-1);
400 g_snprintf(&dest[totlen], dest_len+1-totlen, "%c", '"');
404 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) {
409 uint = tvb_get_ntohs(tvb,offset);
412 uint = tvb_get_ntoh24(tvb,offset);
415 uint = tvb_get_ntohl(tvb,offset);
418 guint64 uint64 = tvb_get_ntoh64(tvb,offset);
419 proto_tree_add_uint64(tree,a->hf64,tvb,offset,len,uint64);
420 proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uint64);
424 proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len);
428 proto_tree_add_uint(tree,a->hf,tvb,offset,len,uint);
431 proto_item_append_text(avp_item, "%s(%u)", val_to_str(uint, a->vs, "Unknown"),uint);
433 proto_item_append_text(avp_item, "%u", uint);
437 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) {
439 if (*shared_secret == '\0') {
440 proto_item_append_text(avp_item, "Encrypted");
441 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
444 buffer=ep_alloc(1024); /* an AVP value can be at most 253 bytes */
445 radius_decrypt_avp(buffer,1024,tvb,offset,len);
446 proto_item_append_text(avp_item, "Decrypted: %s", buffer);
447 proto_tree_add_string(tree, a->hf, tvb, offset, len, buffer);
450 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
451 proto_item_append_text(avp_item, "%s", tvb_format_text(tvb, offset, len));
455 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) {
456 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
457 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
460 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) {
462 gchar buf[MAX_IP_STR_LEN];
465 proto_item_append_text(avp_item, "[wrong length for IP address]");
469 ip=tvb_get_ipv4(tvb,offset);
471 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
473 ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
474 proto_item_append_text(avp_item, "%s", buf);
477 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) {
478 struct e_in6_addr ipv6_buff;
482 proto_item_append_text(avp_item, "[wrong length for IPv6 address]");
486 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
488 tvb_get_ipv6(tvb, offset, &ipv6_buff);
489 ip6_to_str_buf(&ipv6_buff, txtbuf);
490 proto_item_append_text(avp_item, "%s", txtbuf);
493 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) {
497 proto_item_append_text(avp_item, "[wrong length for IPX network]");
501 net=tvb_get_ntohl(tvb,offset);
503 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
505 proto_item_append_text(avp_item, "0x%08X", net);
508 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) {
512 proto_item_append_text(avp_item, "[wrong length for timestamp]");
515 time_ptr.secs = tvb_get_ntohl(tvb,offset);
518 proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr);
519 proto_item_append_text(avp_item, "%s", abs_time_to_str(&time_ptr));
522 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) {
523 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
524 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
527 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) {
528 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
529 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
532 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) {
535 if (dictionary_entry->tagged) {
538 if (avp_length == 0) {
539 pi = proto_tree_add_text(avp_tree, tvb, offset,
540 0, "AVP too short for tag");
541 PROTO_ITEM_SET_GENERATED(pi);
545 tag = tvb_get_guint8(tvb, offset);
548 proto_tree_add_uint(avp_tree,
549 dictionary_entry->hf_tag,
550 tvb, offset, 1, tag);
552 proto_item_append_text(avp_item,
560 if ( dictionary_entry->dissector ) {
564 tvb_value = tvb_new_subset(tvb, offset, avp_length, (gint) avp_length);
566 str = dictionary_entry->dissector(avp_tree,tvb_value);
568 proto_item_append_text(avp_item, ": %s",str);
570 proto_item_append_text(avp_item, ": ");
572 dictionary_entry->type(dictionary_entry,avp_tree,pinfo,tvb,offset,avp_length,avp_item);
576 static void dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, guint length) {
578 gboolean last_eap = FALSE;
579 guint8* eap_buffer = NULL;
580 guint eap_seg_num = 0;
581 guint eap_tot_len_captured = 0;
582 guint eap_tot_len = 0;
583 proto_tree* eap_tree = NULL;
584 tvbuff_t* eap_tvb = NULL;
587 * In case we throw an exception, clean up whatever stuff we've
588 * allocated (if any).
590 CLEANUP_PUSH(g_free, eap_buffer);
593 radius_attr_info_t* dictionary_entry = NULL;
599 proto_item* avp_item;
600 proto_item* avp_len_item;
601 proto_tree* avp_tree;
604 item = proto_tree_add_text(tree, tvb, offset, 0,
605 "Not enough room in packet for AVP header");
606 PROTO_ITEM_SET_GENERATED(item);
609 avp_type = tvb_get_guint8(tvb,offset);
610 avp_length = tvb_get_guint8(tvb,offset+1);
612 if (avp_length < 2) {
613 item = proto_tree_add_text(tree, tvb, offset, 0,
614 "AVP too short: length %u < 2", avp_length);
615 PROTO_ITEM_SET_GENERATED(item);
619 if (length < avp_length) {
620 item = proto_tree_add_text(tree, tvb, offset, 0,
621 "Not enough room in packet for AVP");
622 PROTO_ITEM_SET_GENERATED(item);
626 length -= avp_length;
628 dictionary_entry = g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(avp_type));
630 if (! dictionary_entry ) {
631 dictionary_entry = &no_dictionary_entry;
634 avp_item = proto_tree_add_text(tree, tvb, offset, avp_length,
635 "AVP: l=%u t=%s(%u)", avp_length,
636 dictionary_entry->name, avp_type);
641 if (avp_type == RADIUS_VENDOR_SPECIFIC_CODE) {
642 radius_vendor_info_t* vendor;
643 proto_tree* vendor_tree;
644 gint max_offset = offset + avp_length;
645 const gchar* vendor_str;
647 /* XXX TODO: handle 2 byte codes for USR */
649 if (avp_length < 4) {
650 proto_item_append_text(avp_item, " [AVP too short; no room for vendor ID]");
651 offset += avp_length;
654 vendor_id = tvb_get_ntohl(tvb,offset);
659 vendor = g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
661 vendor_str = vendor->name;
663 vendor_str = val_to_str(vendor_id, sminmpec_values, "Unknown");
666 proto_item_append_text(avp_item, " v=%s(%u)", vendor_str,
669 vendor_tree = proto_item_add_subtree(avp_item,vendor->ett);
671 while (offset < max_offset) {
672 guint32 avp_vsa_type = tvb_get_guint8(tvb,offset++);
673 guint32 avp_vsa_len = tvb_get_guint8(tvb,offset++);
676 if (avp_vsa_len < 2) {
677 proto_tree_add_text(tree, tvb, offset+1, 1,
684 dictionary_entry = g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(avp_vsa_type));
686 if ( !dictionary_entry ) {
687 dictionary_entry = &no_dictionary_entry;
690 avp_item = proto_tree_add_text(vendor_tree,tvb,offset-2,avp_vsa_len+2,
691 "VSA: l=%u t=%s(%u)",
692 avp_vsa_len+2, dictionary_entry->name, avp_vsa_type);
694 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
697 avp_len_item = proto_tree_add_uint(avp_tree,
698 dictionary_entry->hf_len,
700 PROTO_ITEM_SET_GENERATED(avp_len_item);
703 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
705 offset += avp_vsa_len;
710 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
713 avp_len_item = proto_tree_add_uint(avp_tree,
714 dictionary_entry->hf_len,
716 PROTO_ITEM_SET_GENERATED(avp_len_item);
719 tvb_len = tvb_length_remaining(tvb, offset);
721 if ((gint)avp_length < tvb_len)
722 tvb_len = avp_length;
724 if (avp_type == RADIUS_EAP_MESSAGE_CODE) {
727 /* Show this as an EAP fragment. */
729 proto_tree_add_text(avp_tree, tvb, offset, tvb_len,
732 if (eap_tvb != NULL) {
734 * Oops, a non-consecutive EAP-Message
737 proto_item_append_text(avp_item, " (non-consecutive)");
740 * RFC 2869 says, in section 5.13, describing
741 * the EAP-Message attribute:
743 * The NAS places EAP messages received
744 * from the authenticating peer into one
745 * or more EAP-Message attributes and
746 * forwards them to the RADIUS Server
747 * within an Access-Request message.
748 * If multiple EAP-Messages are
749 * contained within an Access-Request or
750 * Access-Challenge packet, they MUST be
751 * in order and they MUST be consecutive
752 * attributes in the Access-Request or
753 * Access-Challenge packet.
757 * The String field contains EAP packets,
758 * as defined in [3]. If multiple
759 * EAP-Message attributes are present
760 * in a packet their values should be
761 * concatenated; this allows EAP packets
762 * longer than 253 octets to be passed
765 * Do reassembly of EAP-Message attributes.
766 * We just concatenate all the attributes,
767 * and when we see either the end of the
768 * attribute list or a non-EAP-Message
769 * attribute, we know we're done.
772 if (eap_buffer == NULL)
773 eap_buffer = g_malloc(eap_tot_len_captured + tvb_len);
775 eap_buffer = g_realloc(eap_buffer,
776 eap_tot_len_captured + tvb_len);
777 tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset,
779 eap_tot_len_captured += tvb_len;
780 eap_tot_len += avp_length;
782 if ( tvb_bytes_exist(tvb, offset + avp_length + 1, 1) ) {
783 guint8 next_type = tvb_get_guint8(tvb, offset + avp_length);
785 if ( next_type != RADIUS_EAP_MESSAGE_CODE ) {
786 /* Non-EAP-Message attribute */
791 * No more attributes, either because
792 * we're at the end of the packet or
793 * because we're at the end of the
794 * captured packet data.
799 if (last_eap && eap_buffer) {
800 gboolean save_writable;
802 proto_item_append_text(avp_item, " Last Segment[%u]",
805 eap_tree = proto_item_add_subtree(avp_item,ett_eap);
807 eap_tvb = tvb_new_real_data(eap_buffer,
808 eap_tot_len_captured,
810 tvb_set_free_cb(eap_tvb, g_free);
811 tvb_set_child_real_data_tvbuff(tvb, eap_tvb);
812 add_new_data_source(pinfo, eap_tvb, "Reassembled EAP");
815 * Don't free this when we're done -
816 * it's associated with a tvbuff.
821 * Set the columns non-writable,
822 * so that the packet list shows
823 * this as an RADIUS packet, not
826 save_writable = col_get_writable(pinfo->cinfo);
827 col_set_writable(pinfo->cinfo, FALSE);
829 call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
831 col_set_writable(pinfo->cinfo, save_writable);
833 proto_item_append_text(avp_item, " Segment[%u]",
838 offset += avp_length;
840 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry,
842 offset += avp_length;
848 * Call the cleanup handler to free any reassembled data we haven't
849 * attached to a tvbuff, and pop the handler.
851 CLEANUP_CALL_AND_POP;
854 /* This function tries to determine whether a packet is radius or not */
856 is_radius(tvbuff_t *tvb)
861 code=tvb_get_guint8(tvb, 0);
863 case RADIUS_ACCESS_REQUEST:
864 case RADIUS_ACCESS_ACCEPT:
865 case RADIUS_ACCESS_REJECT:
866 case RADIUS_ACCOUNTING_REQUEST:
867 case RADIUS_ACCOUNTING_RESPONSE:
868 case RADIUS_ACCOUNTING_STATUS:
869 case RADIUS_ACCESS_PASSWORD_REQUEST:
870 case RADIUS_ACCESS_PASSWORD_ACK:
871 case RADIUS_ACCESS_PASSWORD_REJECT:
872 case RADIUS_ACCOUNTING_MESSAGE:
873 case RADIUS_ACCESS_CHALLENGE:
874 case RADIUS_STATUS_SERVER:
875 case RADIUS_STATUS_CLIENT:
876 case RADIUS_VENDOR_SPECIFIC_CODE:
877 case RADIUS_ASCEND_ACCESS_NEXT_CODE:
878 case RADIUS_ASCEND_ACCESS_NEW_PIN:
879 case RADIUS_ASCEND_PASSWORD_EXPIRED:
880 case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
881 case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
882 case RADIUS_DISCONNECT_REQUEST:
883 case RADIUS_DISCONNECT_REQUEST_ACK:
884 case RADIUS_DISCONNECT_REQUEST_NAK:
885 case RADIUS_CHANGE_FILTER_REQUEST:
886 case RADIUS_CHANGE_FILTER_REQUEST_ACK:
887 case RADIUS_CHANGE_FILTER_REQUEST_NAK:
888 case RADIUS_EAP_MESSAGE_CODE:
889 case RADIUS_MESSAGE_AUTHENTICATOR:
895 /* Check for valid length value:
898 * The Length field is two octets. It indicates the length of the
899 * packet including the Code, Identifier, Length, Authenticator and
900 * Attribute fields. Octets outside the range of the Length field
901 * MUST be treated as padding and ignored on reception. If the
902 * packet is shorter than the Length field indicates, it MUST be
903 * silently discarded. The minimum length is 20 and maximum length
906 length=tvb_get_ntohs(tvb, 2);
907 if ( (length<20) || (length>4096) ) {
914 static void register_radius_fields(const char*);
917 dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
919 proto_tree *radius_tree = NULL;
920 proto_tree *avptree = NULL;
925 radius_info_t *rad_info;
928 conversation_t* conversation;
929 radius_call_info_key radius_call_key;
930 radius_call_info_key *new_radius_call_key = NULL;
931 radius_call_t *radius_call = NULL;
933 static address null_address = { AT_NONE, 0, NULL };
936 /* does this look like radius ? */
942 if (check_col(pinfo->cinfo, COL_PROTOCOL))
943 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
944 if (check_col(pinfo->cinfo, COL_INFO))
945 col_clear(pinfo->cinfo, COL_INFO);
947 rh.rh_code=tvb_get_guint8(tvb,0);
948 rh.rh_ident=tvb_get_guint8(tvb,1);
949 rh.rh_pktlength=tvb_get_ntohs(tvb,2);
952 /* Initialise stat info for passing to tap */
953 rad_info = ep_alloc(sizeof(radius_info_t));
956 rad_info->req_time.secs = 0;
957 rad_info->req_time.nsecs = 0;
958 rad_info->is_duplicate = FALSE;
959 rad_info->request_available = FALSE;
960 rad_info->req_num = 0; /* frame number request seen */
961 rad_info->rspcode = 0;
963 rad_info->code = rh.rh_code;
964 rad_info->ident = rh.rh_ident;
965 tap_queue_packet(radius_tap, pinfo, rad_info);
968 if (check_col(pinfo->cinfo, COL_INFO))
970 col_add_fstr(pinfo->cinfo,COL_INFO,"%s(%d) (id=%d, l=%d)",
971 val_to_str(rh.rh_code,radius_vals,"Unknown Packet"),
972 rh.rh_code, rh.rh_ident, rh.rh_pktlength);
978 if(!radius_vendors) register_radius_fields(NULL);
980 ti = proto_tree_add_item(tree,proto_radius, tvb, 0, rh.rh_pktlength, FALSE);
982 radius_tree = proto_item_add_subtree(ti, ett_radius);
984 proto_tree_add_uint(radius_tree,hf_radius_code, tvb, 0, 1, rh.rh_code);
986 proto_tree_add_uint_format(radius_tree,hf_radius_id, tvb, 1, 1, rh.rh_ident,
987 "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident);
991 * Make sure the length is sane.
993 if (rh.rh_pktlength < HDR_LENGTH)
997 proto_tree_add_uint_format(radius_tree, hf_radius_length,
998 tvb, 2, 2, rh.rh_pktlength,
999 "Length: %u (bogus, < %u)",
1000 rh.rh_pktlength, HDR_LENGTH);
1004 avplength = rh.rh_pktlength - HDR_LENGTH;
1007 proto_tree_add_uint(radius_tree, hf_radius_length, tvb,
1008 2, 2, rh.rh_pktlength);
1010 proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4,AUTHENTICATOR_LENGTH,FALSE);
1012 tvb_memcpy(tvb,authenticator,4,AUTHENTICATOR_LENGTH);
1016 /* Conversation support REQUEST/RESPONSES */
1019 case RADIUS_ACCESS_REQUEST:
1020 case RADIUS_ACCOUNTING_REQUEST:
1021 case RADIUS_ACCESS_PASSWORD_REQUEST:
1022 case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
1023 case RADIUS_DISCONNECT_REQUEST:
1024 case RADIUS_CHANGE_FILTER_REQUEST:
1025 proto_tree_add_boolean_hidden(radius_tree, hf_radius_req, tvb, 0, 0, TRUE);
1026 /* Keep track of the address and port whence the call came
1027 * so that we can match up requests with replies.
1029 * Because it is UDP and the reply can come from any IP
1030 * and port (not necessarly the request dest), we only
1031 * track the source IP and port of the request to match
1036 * XXX - can we just use NO_ADDR_B? Unfortunately,
1037 * you currently still have to pass a non-null
1038 * pointer for the second address argument even
1041 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1042 &null_address, pinfo->ptype, pinfo->srcport,
1043 pinfo->destport, 0);
1044 if (conversation == NULL)
1046 /* It's not part of any conversation - create a new one. */
1047 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1048 &null_address, pinfo->ptype, pinfo->srcport,
1049 pinfo->destport, 0);
1052 /* Prepare the key data */
1053 radius_call_key.code = rh.rh_code;
1054 radius_call_key.ident = rh.rh_ident;
1055 radius_call_key.conversation = conversation;
1056 radius_call_key.req_time = pinfo->fd->abs_ts;
1058 /* Look up the request */
1059 radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
1060 if (radius_call != NULL)
1062 /* We've seen a request with this ID, with the same
1063 destination, before - but was it *this* request? */
1064 if (pinfo->fd->num != radius_call->req_num)
1066 /* No, so it's a duplicate request. Mark it as such. */
1067 rad_info->is_duplicate = TRUE;
1068 rad_info->req_num = radius_call->req_num;
1069 if (check_col(pinfo->cinfo, COL_INFO))
1071 col_append_fstr(pinfo->cinfo, COL_INFO,
1072 ", Duplicate Request ID:%u",
1078 proto_tree_add_uint_hidden(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1079 item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0,0, rh.rh_ident);
1080 PROTO_ITEM_SET_GENERATED(item);
1086 /* Prepare the value data.
1087 "req_num" and "rsp_num" are frame numbers;
1088 frame numbers are 1-origin, so we use 0
1089 to mean "we don't yet know in which frame
1090 the reply for this call appears". */
1091 new_radius_call_key = g_mem_chunk_alloc(radius_call_info_key_chunk);
1092 *new_radius_call_key = radius_call_key;
1093 radius_call = g_mem_chunk_alloc(radius_call_info_value_chunk);
1094 radius_call->req_num = pinfo->fd->num;
1095 radius_call->rsp_num = 0;
1096 radius_call->ident = rh.rh_ident;
1097 radius_call->code = rh.rh_code;
1098 radius_call->responded = FALSE;
1099 radius_call->req_time=pinfo->fd->abs_ts;
1100 radius_call->rspcode = 0;
1103 g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
1105 if (radius_call && radius_call->rsp_num)
1107 proto_item* item = proto_tree_add_uint_format(radius_tree, hf_radius_rsp_frame,
1108 tvb, 0, 0, radius_call->rsp_num,
1109 "The response to this request is in frame %u",
1110 radius_call->rsp_num);
1111 PROTO_ITEM_SET_GENERATED(item);
1114 case RADIUS_ACCESS_ACCEPT:
1115 case RADIUS_ACCESS_REJECT:
1116 case RADIUS_ACCOUNTING_RESPONSE:
1117 case RADIUS_ACCESS_PASSWORD_ACK:
1118 case RADIUS_ACCESS_PASSWORD_REJECT:
1119 case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
1120 case RADIUS_DISCONNECT_REQUEST_ACK:
1121 case RADIUS_DISCONNECT_REQUEST_NAK:
1122 case RADIUS_CHANGE_FILTER_REQUEST_ACK:
1123 case RADIUS_CHANGE_FILTER_REQUEST_NAK:
1124 proto_tree_add_boolean_hidden(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE);
1125 /* Check for RADIUS response. A response must match a call that
1126 * we've seen, and the response must be sent to the same
1127 * port and address that the call came from.
1129 * Because it is UDP and the reply can come from any IP
1130 * and port (not necessarly the request dest), we only
1131 * track the source IP and port of the request to match
1135 /* XXX - can we just use NO_ADDR_B? Unfortunately,
1136 * you currently still have to pass a non-null
1137 * pointer for the second address argument even
1140 conversation = find_conversation(pinfo->fd->num, &null_address,
1141 &pinfo->dst, pinfo->ptype, pinfo->srcport,
1142 pinfo->destport, 0);
1143 if (conversation != NULL)
1145 /* Look only for matching request, if
1146 matching conversation is available. */
1147 /* Prepare the key data */
1148 radius_call_key.code = rh.rh_code;
1149 radius_call_key.ident = rh.rh_ident;
1150 radius_call_key.conversation = conversation;
1151 radius_call_key.req_time = pinfo->fd->abs_ts;
1153 radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
1156 /* Indicate the frame to which this is a reply. */
1157 if (radius_call->req_num)
1160 rad_info->request_available = TRUE;
1161 rad_info->req_num = radius_call->req_num;
1162 radius_call->responded = TRUE;
1164 item = proto_tree_add_uint_format(radius_tree, hf_radius_req_frame,
1165 tvb, 0, 0, radius_call->req_num,
1166 "This is a response to a request in frame %u",
1167 radius_call->req_num);
1168 PROTO_ITEM_SET_GENERATED(item);
1169 nstime_delta(&delta, &pinfo->fd->abs_ts, &radius_call->req_time);
1170 item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
1171 PROTO_ITEM_SET_GENERATED(item);
1174 if (radius_call->rsp_num == 0)
1176 /* We have not yet seen a response to that call, so
1177 this must be the first response; remember its
1179 radius_call->rsp_num = pinfo->fd->num;
1183 /* We have seen a response to this call - but was it
1184 *this* response? (disregard provisional responses) */
1185 if ( (radius_call->rsp_num != pinfo->fd->num) && (radius_call->rspcode == rh.rh_code) )
1187 /* No, so it's a duplicate response. Mark it as such. */
1188 rad_info->is_duplicate = TRUE;
1189 if (check_col(pinfo->cinfo, COL_INFO))
1191 col_append_fstr(pinfo->cinfo, COL_INFO,
1192 ", Duplicate Response ID:%u",
1198 proto_tree_add_uint_hidden(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1199 item = proto_tree_add_uint(radius_tree, hf_radius_rsp_dup,
1200 tvb, 0, 0, rh.rh_ident);
1201 PROTO_ITEM_SET_GENERATED(item);
1205 /* Now store the response code (after comparison above) */
1206 radius_call->rspcode = rh.rh_code;
1207 rad_info->rspcode = rh.rh_code;
1217 rad_info->req_time.secs = radius_call->req_time.secs;
1218 rad_info->req_time.nsecs = radius_call->req_time.nsecs;
1221 if (avplength > 0) {
1222 /* list the attribute value pairs */
1223 avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
1224 avplength, "Attribute Value Pairs");
1225 avptree = proto_item_add_subtree(avptf, ett_radius_avp);
1227 dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
1233 return tvb_length(tvb);
1237 static void register_attrs(gpointer k _U_, gpointer v, gpointer p) {
1238 radius_attr_info_t* a = v;
1240 gint* ett = &(a->ett);
1241 gchar* abbrev = g_strconcat("radius.",a->name,NULL);
1242 hf_register_info hfri[] = {
1243 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1244 { NULL, { NULL,NULL, FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
1245 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1246 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }}
1251 for(i=0; abbrev[i]; i++) {
1252 if(abbrev[i] == '-') abbrev[i] = '_';
1253 if(abbrev[i] == '/') abbrev[i] = '_';
1256 hfri[0].p_id = &(a->hf);
1257 hfri[1].p_id = &(a->hf_len);
1259 hfri[0].hfinfo.name = a->name;
1260 hfri[0].hfinfo.abbrev = abbrev;
1262 hfri[1].hfinfo.name = "Length";
1263 hfri[1].hfinfo.abbrev = g_strconcat(abbrev,".len",NULL);
1264 hfri[1].hfinfo.blurb = g_strconcat(a->name," Length",NULL);
1266 if (a->type == radius_integer) {
1267 hfri[0].hfinfo.type = FT_UINT32;
1268 hfri[0].hfinfo.display = BASE_DEC;
1270 hfri[2].p_id = &(a->hf64);
1271 hfri[2].hfinfo.name = g_strdup(a->name);
1272 hfri[2].hfinfo.abbrev = abbrev;
1273 hfri[2].hfinfo.type = FT_UINT64;
1274 hfri[2].hfinfo.display = BASE_DEC;
1277 hfri[0].hfinfo.strings = VALS(a->vs);
1282 } else if (a->type == radius_string) {
1283 hfri[0].hfinfo.type = FT_STRING;
1284 hfri[0].hfinfo.display = BASE_NONE;
1285 } else if (a->type == radius_octets) {
1286 hfri[0].hfinfo.type = FT_BYTES;
1287 hfri[0].hfinfo.display = BASE_NONE;
1288 } else if (a->type == radius_ipaddr) {
1289 hfri[0].hfinfo.type = FT_IPv4;
1290 hfri[0].hfinfo.display = BASE_NONE;
1291 } else if (a->type == radius_ipv6addr) {
1292 hfri[0].hfinfo.type = FT_IPv6;
1293 hfri[0].hfinfo.display = BASE_NONE;
1294 } else if (a->type == radius_ipxnet) {
1295 hfri[0].hfinfo.type = FT_IPXNET;
1296 hfri[0].hfinfo.display = BASE_NONE;
1297 } else if (a->type == radius_date) {
1298 hfri[0].hfinfo.type = FT_ABSOLUTE_TIME;
1299 hfri[0].hfinfo.display = BASE_NONE;
1300 } else if (a->type == radius_abinary) {
1301 hfri[0].hfinfo.type = FT_BYTES;
1302 hfri[0].hfinfo.display = BASE_NONE;
1303 } else if (a->type == radius_ifid) {
1304 hfri[0].hfinfo.type = FT_BYTES;
1305 hfri[0].hfinfo.display = BASE_NONE;
1307 hfri[0].hfinfo.type = FT_BYTES;
1308 hfri[0].hfinfo.display = BASE_NONE;
1312 hfri[len_hf].p_id = &(a->hf_tag);
1313 hfri[len_hf].hfinfo.name = "Tag";
1314 hfri[len_hf].hfinfo.abbrev = g_strconcat(abbrev,".tag",NULL);
1315 hfri[len_hf].hfinfo.blurb = g_strconcat(a->name," Tag",NULL);
1316 hfri[len_hf].hfinfo.type = FT_UINT8;
1317 hfri[len_hf].hfinfo.display = BASE_HEX;
1321 g_array_append_vals(ri->hf,hfri,len_hf);
1322 g_array_append_val(ri->ett,ett);
1326 static void register_vendors(gpointer k _U_, gpointer v, gpointer p) {
1327 radius_vendor_info_t* vnd = v;
1329 value_string vnd_vs;
1330 gint* ett_p = &(vnd->ett);
1332 vnd_vs.value = vnd->code;
1333 vnd_vs.strptr = vnd->name;
1335 g_array_append_val(ri->vend_vs,vnd_vs);
1336 g_array_append_val(ri->ett,ett_p);
1338 g_hash_table_foreach(vnd->attrs_by_id,register_attrs,ri);
1342 extern void radius_register_avp_dissector(guint32 vendor_id, guint32 attribute_id, radius_avp_dissector_t radius_avp_dissector) {
1343 radius_vendor_info_t* vendor;
1344 radius_attr_info_t* dictionary_entry;
1347 DISSECTOR_ASSERT(radius_avp_dissector != NULL);
1350 vendor = g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1353 vendor = g_malloc(sizeof(radius_vendor_info_t));
1355 vendor->name = g_strdup_printf("%s-%u",val_to_str(vendor_id, sminmpec_values, "Unknown"),vendor_id);
1356 vendor->code = vendor_id;
1357 vendor->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1358 vendor->ett = no_vendor.ett;
1360 g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(vendor->code),vendor);
1361 g_hash_table_insert(dict->vendors_by_name,(gpointer)(vendor->name),vendor);
1364 dictionary_entry = g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1365 by_id = vendor->attrs_by_id;
1367 dictionary_entry = g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1368 by_id = dict->attrs_by_id;
1371 if (!dictionary_entry) {
1372 dictionary_entry = g_malloc(sizeof(radius_attr_info_t));;
1374 dictionary_entry->name = g_strdup_printf("Unknown-Attribute-%u",attribute_id);
1375 dictionary_entry->code = attribute_id;
1376 dictionary_entry->encrypt = FALSE;
1377 dictionary_entry->type = NULL;
1378 dictionary_entry->vs = NULL;
1379 dictionary_entry->hf = no_dictionary_entry.hf;
1380 dictionary_entry->hf_len = no_dictionary_entry.hf_len;
1381 dictionary_entry->ett = no_dictionary_entry.ett;
1383 g_hash_table_insert(by_id,GUINT_TO_POINTER(dictionary_entry->code),dictionary_entry);
1386 dictionary_entry->dissector = radius_avp_dissector;
1390 static void reinit_radius(void) {
1391 if ( alt_port_pref != alt_port ) {
1394 dissector_delete("udp.port", alt_port, radius_handle);
1397 dissector_add("udp.port", alt_port_pref, radius_handle);
1399 alt_port = alt_port_pref;
1403 /* Discard and init any state we've saved */
1405 radius_init_protocol(void)
1407 if (radius_calls != NULL)
1409 g_hash_table_destroy(radius_calls);
1410 radius_calls = NULL;
1412 if (radius_call_info_key_chunk != NULL)
1414 g_mem_chunk_destroy(radius_call_info_key_chunk);
1415 radius_call_info_key_chunk = NULL;
1417 if (radius_call_info_value_chunk != NULL)
1419 g_mem_chunk_destroy(radius_call_info_value_chunk);
1420 radius_call_info_value_chunk = NULL;
1423 radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
1424 radius_call_info_key_chunk = g_mem_chunk_new("call_info_key_chunk",
1425 sizeof(radius_call_info_key),
1426 200 * sizeof(radius_call_info_key),
1428 radius_call_info_value_chunk = g_mem_chunk_new("call_info_value_chunk",
1429 sizeof(radius_call_t),
1430 200 * sizeof(radius_call_t),
1434 static void register_radius_fields(const char* unused _U_) {
1435 hf_register_info base_hf[] = {
1437 { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1438 "TRUE if RADIUS request", HFILL }},
1440 { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1441 "TRUE if RADIUS response", HFILL }},
1442 { &hf_radius_req_frame,
1443 { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1444 "Request Frame", HFILL }},
1445 { &hf_radius_rsp_frame,
1446 { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1447 "Response Frame", HFILL }},
1449 { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
1450 "Timedelta between Request and Response", HFILL }},
1452 { "Code","radius.code", FT_UINT8, BASE_DEC, VALS(radius_vals), 0x0,
1455 { "Identifier", "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0,
1457 { &hf_radius_authenticator,
1458 { "Authenticator", "radius.authenticator", FT_BYTES, BASE_HEX, NULL, 0x0,
1460 { &hf_radius_length,
1461 { "Length","radius.length", FT_UINT16, BASE_DEC, NULL, 0x0,
1463 { &(no_dictionary_entry.hf),
1464 { "Unknown-Attribute","radius.Unknown_Attribute", FT_BYTES, BASE_HEX, NULL, 0x0,
1466 { &(no_dictionary_entry.hf_len),
1467 { "Unknown-Attribute Length","radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0,
1469 { &hf_radius_framed_ip_address,
1470 { "Framed-IP-Address","radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0,
1472 { &hf_radius_login_ip_host,
1473 { "Login-IP-Host","radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0,
1475 { &hf_radius_framed_ipx_network,
1476 { "Framed-IPX-Network","radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0,
1478 { &hf_radius_cosine_vpi,
1479 { "Cosine-VPI","radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0,
1481 { &hf_radius_cosine_vci,
1482 { "Cosine-VCI","radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
1485 { "Duplicate Message", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1486 "Duplicate Message", HFILL }},
1487 { &hf_radius_req_dup,
1488 { "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1489 "Duplicate Request", HFILL }},
1490 { &hf_radius_rsp_dup,
1491 { "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1492 "Duplicate Response", HFILL }}
1495 gint *base_ett[] = {
1499 &(no_dictionary_entry.ett),
1505 gchar* dict_err_str = NULL;
1507 ri.hf = g_array_new(FALSE,TRUE,sizeof(hf_register_info));
1508 ri.ett = g_array_new(FALSE,TRUE,sizeof(gint *));
1509 ri.vend_vs = g_array_new(TRUE,TRUE,sizeof(value_string));
1511 g_array_append_vals(ri.hf, base_hf, array_length(base_hf));
1512 g_array_append_vals(ri.ett, base_ett, array_length(base_ett));
1514 dir = get_persconffile_path("radius", FALSE, FALSE);
1516 if (test_for_directory(dir) != EISDIR) {
1517 /* Although dir isn't a directory it may still use memory */
1520 dir = get_datafile_path("radius");
1522 if (test_for_directory(dir) != EISDIR) {
1529 radius_load_dictionary(dict,dir,"dictionary",&dict_err_str);
1532 g_warning("radius: %s",dict_err_str);
1533 g_free(dict_err_str);
1536 g_hash_table_foreach(dict->attrs_by_id,register_attrs,&ri);
1537 g_hash_table_foreach(dict->vendors_by_id,register_vendors,&ri);
1542 radius_vendors = (value_string*)g_array_data(ri.vend_vs);
1544 proto_register_field_array(proto_radius,(hf_register_info*)g_array_data(ri.hf),ri.hf->len);
1545 proto_register_subtree_array((gint**)g_array_data(ri.ett), ri.ett->len);
1547 g_array_free(ri.hf,FALSE);
1548 g_array_free(ri.ett,FALSE);
1549 g_array_free(ri.vend_vs,FALSE);
1551 no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1556 proto_register_radius(void)
1558 module_t *radius_module;
1560 proto_radius = proto_register_protocol("Radius Protocol", "RADIUS", "radius");
1561 new_register_dissector("radius", dissect_radius, proto_radius);
1562 register_init_routine(&radius_init_protocol);
1563 radius_module = prefs_register_protocol(proto_radius,reinit_radius);
1564 prefs_register_string_preference(radius_module,"shared_secret","Shared Secret",
1565 "Shared secret used to decode User Passwords",
1567 prefs_register_bool_preference(radius_module,"show_length","Show AVP Lengths",
1568 "Whether to add or not to the tree the AVP's payload length",
1570 prefs_register_uint_preference(radius_module, "alternate_port","Alternate Port",
1571 "An alternate UDP port to decode as RADIUS", 10, &alt_port_pref);
1572 radius_tap = register_tap("radius");
1573 proto_register_prefix("radius",register_radius_fields);
1575 dict = g_malloc(sizeof(radius_dictionary_t));
1576 dict->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1577 dict->attrs_by_name = g_hash_table_new(g_str_hash,g_str_equal);
1578 dict->vendors_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1579 dict->vendors_by_name = g_hash_table_new(g_str_hash,g_str_equal);
1583 proto_reg_handoff_radius(void)
1586 eap_handle = find_dissector("eap");
1588 radius_handle = new_create_dissector_handle(dissect_radius, proto_radius);
1590 dissector_add("udp.port", UDP_PORT_RADIUS, radius_handle);
1591 dissector_add("udp.port", UDP_PORT_RADIUS_NEW, radius_handle);
1592 dissector_add("udp.port", UDP_PORT_RADACCT, radius_handle);
1593 dissector_add("udp.port", UDP_PORT_RADACCT_NEW, radius_handle);
1595 radius_register_avp_dissector(0,8,dissect_framed_ip_address);
1596 radius_register_avp_dissector(0,14,dissect_login_ip_host);
1597 radius_register_avp_dissector(0,23,dissect_framed_ipx_network);
1598 radius_register_avp_dissector(VENDOR_COSINE,5,dissect_cosine_vpvc);