2 * Routines for DIAMETER packet disassembly
4 * $Id: packet-diameter.c,v 1.10 2001/01/03 07:53:43 guy Exp $
6 * Copyright (c) 2000 by David Frascone <chaos@mindspring.com>
8 * Ethereal - Network traffic analyzer
10 * Copyright 1999 Johan Feyaerts
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
48 /* This must be defined before we include our dictionary defs */
50 typedef struct _value_value_pair {
65 #include "packet-diameter.h"
66 #include "packet-diameter-defs.h"
68 #define COMMAND_CODE_OFFSET 20
69 #define NTP_TIME_DIFF (2208988800UL)
71 #undef SCTP_DISSECTORS_ENABLED
73 #define UDP_PORT_DIAMETER 2645
74 #define TCP_PORT_DIAMETER 1812
75 #ifdef SCTP_DISSECTORS_ENABLED
76 #define SCTP_PORT_DIAMETER 1812
78 /* #define UDP_PORT_DIAMETER 1812 -- Compiling this in breaks RADIUS */
80 static int proto_diameter = -1;
81 static int hf_diameter_length = -1;
82 static int hf_diameter_code = -1;
83 static int hf_diameter_id =-1;
84 static int hf_diameter_flags = -1;
85 static int hf_diameter_ns = -1;
86 static int hf_diameter_nr = -1;
88 static gint ett_diameter = -1;
89 static gint ett_diameter_avp = -1;
90 static gint ett_diameter_avpinfo = -1;
92 static char gbl_diameterString[200];
93 static int gbl_diameterUdpPort=UDP_PORT_DIAMETER;
94 static int gbl_diameterTcpPort=TCP_PORT_DIAMETER;
95 #ifdef SCTP_DISSECTORS_ENABLED
96 static int gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
98 gboolean gbl_commandCodeInHeader = FALSE;
100 typedef struct _e_diameterhdr {
101 guint8 code; /* Must be 254 for diameter */
108 guint16 nextReceived;
114 guint16 nextReceived;
119 typedef struct _e_avphdr {
123 guint32 avp_vendorId; /* optional */
124 guint32 avp_tag; /* optional */
128 #define AUTHENTICATOR_LENGTH 12
130 #define DIAM_FLAGS_A 0x10
131 #define DIAM_FLAGS_W 0x08
132 #define AVP_FLAGS_P 0x0010
133 #define AVP_FLAGS_T 0x0008
134 #define AVP_FLAGS_V 0x0004
135 #define AVP_FLAGS_R 0x0002
136 #define AVP_FLAGS_M 0x0001
138 void proto_reg_handoff_diameter(void);
140 static guint32 match_numval(guint32 val, const value_value_pair *vs)
145 if (vs[i].val1 == val)
153 static gchar *rdconvertbufftostr(gchar *dest,guint8 length,const guint8 *pd)
155 /*converts the raw buffer into printable text */
162 for (i=0; i < (guint32)length; i++)
164 if( isalnum((int)pd[i])||ispunct((int)pd[i])
165 ||((int)pd[i]==' ')) {
166 dest[totlen]=(gchar)pd[i];
171 sprintf(&(dest[totlen]), "\\%03u", pd[i]);
172 totlen=totlen+strlen(&(dest[totlen]));
180 static gchar *rd_match_strval(guint32 val, const value_string *vs) {
182 result=match_strval(val,vs);
183 if (result == NULL ) {
188 static char *complexValCheck(e_avphdr *avp, const char *data, size_t dataLen)
191 static char returnStr[1024];
193 switch (avp->avp_type) {
194 case DIAMETER_ATT_INTEGRITY_CHECK_VALUE:
201 memcpy(&icv, data, 8);
202 icv.transform=ntohl(icv.transform);
203 icv.keyid=ntohl(icv.keyid);
207 "transform: 0x%08x (%d) keyid: 0x%08x (%d) Hash: ",
208 icv.transform, icv.transform, icv.keyid, icv.keyid);
210 rdconvertbufftostr(&returnStr[strlen(returnStr)],
218 } /* complexValCheck */
219 static char *customValCheck(int code, int value)
222 case DIAMETER_ATT_QOS_SERVICE_TYPE:
223 return rd_match_strval(value, diameter_qos_service_type_vals);
225 case DIAMETER_ATT_SERVICE_TYPE:
226 return rd_match_strval(value, diameter_service_type_vals);
228 case DIAMETER_ATT_PROHIBIT:
229 return rd_match_strval(value, diameter_prohibit_vals);
231 case DIAMETER_ATT_PROMPT:
232 return rd_match_strval(value, diameter_prompt_vals);
234 case DIAMETER_ATT_SOURCE_PORT:
235 return rd_match_strval(value, diameter_source_port_vals);
237 case DIAMETER_ATT_NAS_PORT_TYPE:
238 return rd_match_strval(value, diameter_nas_port_type_vals);
240 case DIAMETER_ATT_INTERFACE_ADDRESS:
241 return rd_match_strval(value, diameter_interface_address_vals);
243 case DIAMETER_ATT_FRAMED_ROUTING:
244 return rd_match_strval(value, diameter_framed_routing_vals);
246 case DIAMETER_ATT_COMMAND_CODE:
247 return rd_match_strval(value, diameter_command_code_vals);
249 case DIAMETER_ATT_FRAMED_IP_ADDRESS:
250 return rd_match_strval(value, diameter_framed_ip_address_vals);
252 case DIAMETER_ATT_ARAP_ZONE_ACCESS:
253 return rd_match_strval(value, diameter_arap_zone_access_vals);
255 case DIAMETER_ATT_ACCT_AUTHENTIC:
256 return rd_match_strval(value, diameter_acct_authentic_vals);
258 case DIAMETER_ATT_FRAMED_PROTOCOL:
259 return rd_match_strval(value, diameter_framed_protocol_vals);
261 case DIAMETER_ATT_FRAMED_COMPRESSION:
262 return rd_match_strval(value, diameter_framed_compression_vals);
264 case DIAMETER_ATT_AUTHENTICATION_TYPE:
265 return rd_match_strval(value, diameter_authentication_type_vals);
267 case DIAMETER_ATT_ACCT_TERMINATE_CAUSE:
268 return rd_match_strval(value, diameter_acct_terminate_cause_vals);
270 case DIAMETER_ATT_PROTOCOL:
271 return rd_match_strval(value, diameter_protocol_vals);
273 case DIAMETER_ATT_DESTINATION_PORT:
274 return rd_match_strval(value, diameter_destination_port_vals);
276 case DIAMETER_ATT_TERMINATION_ACTION:
277 return rd_match_strval(value, diameter_termination_action_vals);
279 case DIAMETER_ATT_EXTENSION_ID:
280 return rd_match_strval(value, diameter_extension_id_vals);
282 case DIAMETER_ATT_MERIT_LAS_CODE:
283 return rd_match_strval(value, diameter_merit_las_code_vals);
285 case DIAMETER_ATT_LOGIN_SERVICE:
286 return rd_match_strval(value, diameter_login_service_vals);
288 case DIAMETER_ATT_RSVP_SERVICE_TYPE:
289 return rd_match_strval(value, diameter_rsvp_service_type_vals);
291 case DIAMETER_ATT_REBOOT_TYPE:
292 return rd_match_strval(value, diameter_reboot_type_vals);
294 case DIAMETER_ATT_ACCT_STATUS_TYPE:
295 return rd_match_strval(value, diameter_acct_status_type_vals);
302 static gchar *rd_value_to_str(e_avphdr *avph,const u_char *pd, int offset)
309 static char buffer[1024];
311 dataLen = avph->avp_length - sizeof(e_avphdr);
313 if (!(avph->avp_flags & AVP_FLAGS_V))
315 if (!(avph->avp_flags & AVP_FLAGS_T))
318 /* prints the values of the attribute value pairs into a text buffer */
320 print_type=match_numval(avph->avp_type,diameter_printinfo);
322 sprintf(buffer,"Value: ");
323 cont=&buffer[strlen(buffer)];
326 case DIAMETER_COMPLEX:
327 valstr=complexValCheck(avph, &(pd[offset]), dataLen);
329 strcpy(cont, valstr);
333 /* Intentional fall through */
335 case DIAMETER_STRING:
336 rdconvertbufftostr(cont,dataLen,
339 case DIAMETER_ADDRESS:
340 sprintf(cont,"%u.%u.%u.%u",(guint8)pd[offset],
341 (guint8)pd[offset+1],(guint8)pd[offset+2],
342 (guint8)pd[offset+3]);
344 case DIAMETER_INTEGER32:
345 /* Check for custom values */
346 intval=pntohl(&(pd[offset]));
347 valstr=customValCheck(avph->avp_type, intval);
349 sprintf(cont,"%s (%u)", valstr, intval);
351 sprintf(cont,"%u", intval);
354 case DIAMETER_INTEGER64:
355 sprintf(cont,"Unsupported Conversion");
360 intval=pntohl(&(pd[offset]));
361 intval -= NTP_TIME_DIFF;
362 lt=*localtime((time_t *)&intval);
364 "%a, %d %b %Y %H:%M:%S %z",<);
369 rdconvertbufftostr(cont,dataLen,
373 if (cont == buffer) {
374 strcpy(cont,"Unknown Value");
380 static void dissect_attribute_value_pairs(const u_char *pd, int offset,
381 frame_data *fd, proto_tree *tree, int avplength) {
382 /* adds the attribute value pairs to the tree */
392 int vendorOffset, tagOffset;
395 proto_tree_add_text(tree, NullTVB,offset,0,
396 "No Attribute Value Pairs Found");
400 while (avplength > 0 ) {
401 vendorOffset = tagOffset = 0;
402 memcpy(&avph,&pd[offset],sizeof(e_avphdr));
403 avph.avp_type = ntohl(avph.avp_type);
404 avph.avp_length = ntohs(avph.avp_length);
405 avph.avp_flags = ntohs(avph.avp_flags);
407 if (avph.avp_flags & AVP_FLAGS_V) {
408 vendorId = ntohl(avph.avp_vendorId);
410 if (avph.avp_flags & AVP_FLAGS_T) {
411 tag = ntohl(avph.avp_tag);
413 dataOffset = sizeof(e_avphdr);
415 /* only a vendor id */
416 dataOffset = sizeof(e_avphdr) - sizeof(guint32);
419 if (avph.avp_flags & AVP_FLAGS_T) {
420 /* tag in vendor field */
421 tag = ntohl(avph.avp_vendorId);
423 dataOffset = sizeof(e_avphdr) - sizeof(guint32);
425 /* No vendor or tag info */
426 dataOffset = sizeof(e_avphdr) -
434 fixAmt = 4 - (avph.avp_length % 4);
435 if (fixAmt == 4) fixAmt = 0;
436 avplength=avplength - (avph.avp_length + fixAmt);
437 avptpstrval=match_strval(avph.avp_type, diameter_attrib_type_vals);
438 if (avptpstrval == NULL) avptpstrval="Unknown Type";
439 if (!BYTES_ARE_IN_FRAME(offset, avph.avp_length)) {
442 avptf = proto_tree_add_text(tree,NullTVB,
443 offset, avph.avp_length,
444 "%s(%d) l:0x%x (%d bytes)",
445 avptpstrval,avph.avp_type,avph.avp_length,
447 avptree = proto_item_add_subtree(avptf,
448 ett_diameter_avpinfo);
449 if (avptree !=NULL) {
450 proto_tree_add_text(avptree,NullTVB,
453 avptpstrval,avph.avp_type);
454 proto_tree_add_text(avptree,NullTVB,
456 "Length: 0x%x(%d bytes)",
457 avph.avp_length, avph.avp_length);
458 proto_tree_add_text(avptree,NullTVB,
460 "Flags: P:%d T:%d V:%d R:%d M:%d",
461 (avph.avp_flags & AVP_FLAGS_P)?1:0,
462 (avph.avp_flags & AVP_FLAGS_T)?1:0,
463 (avph.avp_flags & AVP_FLAGS_V)?1:0,
464 (avph.avp_flags & AVP_FLAGS_R)?1:0,
465 (avph.avp_flags & AVP_FLAGS_M)?1:0);
467 proto_tree_add_text(avptree,NullTVB,
468 offset+vendorOffset, 4,
469 "VendorId: 0x%08x (%d)",
473 proto_tree_add_text(avptree,NullTVB,
478 valstr=rd_value_to_str(&avph, pd, offset+dataOffset);
479 proto_tree_add_text(avptree,NullTVB,
480 offset+dataOffset, avph.avp_length - dataOffset,
481 "Data: (%d bytes) %s",
482 avph.avp_length - dataOffset, valstr);
484 offset=offset+avph.avp_length + fixAmt;
488 void dissect_diameter(const u_char *pd, int offset, frame_data *fd,
491 proto_tree *diameter_tree,*avptree;
492 proto_item *ti,*avptf;
493 int avplength,hdrlength, offsetavp;
497 int nextSend=0, nextReceived=0;
501 OLD_CHECK_DISPLAY_AS_DATA(proto_diameter, pd, offset, fd, tree);
503 if (gbl_commandCodeInHeader)
504 hdrlength=sizeof(e_diameterhdr);
506 hdrlength = sizeof(e_diameterhdr) - (2 * sizeof(guint32));
508 memcpy(&dh,&pd[offset],hdrlength);
509 /* Fix byte ordering in our static structure */
510 dh.pktLength = ntohs(dh.pktLength);
511 dh.identifier = ntohl(dh.identifier);
513 /* Our code is in first avp */
514 if (gbl_commandCodeInHeader) {
515 dh.u.new.commandCode = ntohl(dh.u.new.commandCode);
516 dh.u.new.vendorId = ntohl(dh.u.new.vendorId);
518 if ((DIAM_FLAGS_W & dh.flagsVer)) {
519 dh.u.new.nextSend = ntohs(dh.u.new.nextSend);
520 dh.u.new.nextReceived = ntohs(dh.u.new.nextReceived);
521 nextSend = dh.u.new.nextSend;
522 nextReceived = dh.u.new.nextReceived;
526 commandCode = dh.u.new.commandCode;
528 if ((DIAM_FLAGS_W & dh.flagsVer)) {
529 dh.u.old.nextSend = ntohs(dh.u.old.nextSend);
530 dh.u.old.nextReceived = ntohs(dh.u.old.nextReceived);
531 nextSend = dh.u.old.nextSend;
532 nextReceived = dh.u.old.nextReceived;
536 memcpy(&commandCode, &pd[offset+COMMAND_CODE_OFFSET], 4);
537 commandCode = ntohl(commandCode);
540 codestrval= match_strval(commandCode,diameter_command_code_vals);
541 if (codestrval==NULL) {
542 codestrval="Unknown Packet";
544 if (check_col(fd, COL_PROTOCOL))
545 col_set_str(fd, COL_PROTOCOL, "DIAMETER");
546 if (check_col(fd, COL_INFO)) {
547 if (DIAM_FLAGS_W & dh.flagsVer) {
548 if (DIAM_FLAGS_A & dh.flagsVer) {
549 sprintf(buffer,"ACK (id=%d, l=%d, s=%d, r=%d)",
550 dh.identifier, dh.pktLength, nextSend,
553 sprintf(buffer,"%s(%d) (id=%d, l=%d, s=%d, r=%d)",
554 codestrval,commandCode, dh.identifier, dh.pktLength,
555 nextSend, nextReceived);
558 if (DIAM_FLAGS_A & dh.flagsVer) {
559 sprintf(buffer,"ACK (id=%d, l=%d)",
560 dh.identifier, dh.pktLength);
562 sprintf(buffer,"%s(%d) (id=%d, l=%d)",
563 codestrval,commandCode,
564 dh.identifier, dh.pktLength);
567 col_add_fstr(fd,COL_INFO,buffer);
572 ti = proto_tree_add_protocol_format(tree, proto_diameter, NullTVB,
573 offset, dh.pktLength, "%s",
575 diameter_tree = proto_item_add_subtree(ti, ett_diameter);
577 if (!(DIAM_FLAGS_A & dh.flagsVer)) {
578 proto_tree_add_uint_format(diameter_tree,
582 1, dh.code, "Packet code:0x%02x",
586 proto_tree_add_uint_format(diameter_tree,
588 NullTVB, offset+1, 1,
590 "Packet flags/Version: 0x%02x (Flags:0x%x,"
591 " A:%d W:%d Version=0x%1x (%d)",
592 dh.flagsVer, (dh.flagsVer&0xf8)>>3,
593 (DIAM_FLAGS_A & dh.flagsVer)?1:0,
594 (DIAM_FLAGS_W & dh.flagsVer)?1:0,
595 dh.flagsVer&0x07, dh.flagsVer&0x07);
596 proto_tree_add_uint_format(diameter_tree,
597 hf_diameter_length, NullTVB,
600 "Packet length: 0x%04x (%d)",dh.pktLength,
602 proto_tree_add_uint_format(diameter_tree,hf_diameter_id,
603 NullTVB, offset+4, 4,
604 dh.identifier, "Packet identifier: 0x%08x (%d)",
605 dh.identifier, dh.identifier);
606 if (gbl_commandCodeInHeader) {
607 proto_tree_add_uint_format(diameter_tree,hf_diameter_id,
608 NullTVB, offset+8, 4,
609 dh.identifier, "Command Code: 0x%08x (%d:%s)",
610 dh.u.new.commandCode, dh.u.new.commandCode,
612 proto_tree_add_uint_format(diameter_tree,hf_diameter_id,
613 NullTVB, offset+12, 4,
614 dh.identifier, "VendorId: 0x%08x (%d)",
615 dh.u.new.vendorId, dh.u.new.vendorId);
616 if (DIAM_FLAGS_W & dh.flagsVer) {
617 proto_tree_add_uint_format(diameter_tree,
618 hf_diameter_ns, NullTVB,
621 "Ns: 0x%02x(%d)",nextSend, nextSend);
623 proto_tree_add_uint_format(diameter_tree,
624 hf_diameter_nr, NullTVB,
627 "Nr: 0x%02x(%d)", nextReceived,
631 if (DIAM_FLAGS_W & dh.flagsVer) {
632 proto_tree_add_uint_format(diameter_tree,
633 hf_diameter_ns, NullTVB,
636 "Ns: 0x%02x(%d)",nextSend, nextSend);
638 proto_tree_add_uint_format(diameter_tree,
639 hf_diameter_nr, NullTVB,
642 "Nr: 0x%02x(%d)", nextReceived,
647 /* Update the lengths */
648 avplength= dh.pktLength -hdrlength;
649 offsetavp=offset+hdrlength;
651 /* list the attribute value pairs */
653 avptf = proto_tree_add_text(diameter_tree,
654 NullTVB,offset+hdrlength,avplength,
655 "Attribute value pairs");
656 avptree = proto_item_add_subtree(avptf,
658 if (avptree !=NULL) {
659 dissect_attribute_value_pairs( pd,
660 offsetavp,fd,avptree,avplength);
665 /* registration with the filtering engine */
667 proto_register_diameter(void)
669 static hf_register_info hf[] = {
671 { "Code","diameter.code", FT_UINT8, BASE_DEC, NULL, 0x0,
674 { &hf_diameter_flags,
675 { "Flags+Version", "diameter.flags", FT_UINT8, BASE_DEC, NULL, 0x0,
678 { &hf_diameter_length,
679 { "Length","diameter.length", FT_UINT32, BASE_DEC, NULL, 0x0,
683 { "Identifier", "diameter.id", FT_UINT32, BASE_DEC, NULL, 0x0,
687 { "Next Send", "diameter.ns", FT_UINT16, BASE_DEC, NULL, 0x0,
690 { "Next Received", "diameter.nr", FT_UINT16, BASE_DEC, NULL, 0x0,
694 static gint *ett[] = {
697 &ett_diameter_avpinfo
699 module_t *diameter_module;
701 proto_diameter = proto_register_protocol (gbl_diameterString,
702 "DIAMETER", "diameter");
703 proto_register_field_array(proto_diameter, hf, array_length(hf));
704 proto_register_subtree_array(ett, array_length(ett));
706 /* Register a configuration option for port */
707 diameter_module = prefs_register_protocol(proto_diameter,
708 proto_reg_handoff_diameter);
709 prefs_register_uint_preference(diameter_module, "udp.port",
711 "Set the port for DIAMETER messages (if"
712 " other than RADIUS port)",
714 &gbl_diameterUdpPort);
715 prefs_register_uint_preference(diameter_module, "tcp.port",
717 "Set the TCP port for DIAMETER messages",
719 &gbl_diameterTcpPort);
720 #ifdef SCTP_DISSECTORS_ENABLED
721 prefs_register_uint_preference(diameter_module, "sctp.port",
722 "DIAMETER SCTP Port",
723 "Set the SCTP port for DIAMETER messages",
725 &gbl_diameterSctpPort);
727 prefs_register_bool_preference(diameter_module, "command_in_header",
728 "Command code in header",
729 "Whether the command code is in the header, or in the first AVP",
730 &gbl_commandCodeInHeader);
734 proto_reg_handoff_diameter(void)
736 static int Initialized=FALSE;
737 static int UdpPort=0;
738 static int TcpPort=0;
739 #ifdef SCTP_DISSECTORS_ENABLED
740 static int SctpPort=0;
743 old_dissector_delete("udp.port", UdpPort, dissect_diameter);
744 old_dissector_delete("tcp.port", TcpPort, dissect_diameter);
745 #ifdef SCTP_DISSECTORS_ENABLED
746 old_dissector_delete("sctp.srcport", SctpPort, dissect_diameter);
747 old_dissector_delete("sctp.destport", SctpPort, dissect_diameter);
753 /* set port for future deletes */
754 UdpPort=gbl_diameterUdpPort;
755 TcpPort=gbl_diameterTcpPort;
756 #ifdef SCTP_DISSECTORS_ENABLED
757 SctpPort=gbl_diameterSctpPort;
760 strcpy(gbl_diameterString, "Diameter Protocol");
762 /* g_warning ("Diameter: Adding tcp dissector to port %d",
763 gbl_diameterTcpPort); */
764 old_dissector_add("tcp.port", gbl_diameterTcpPort, dissect_diameter);
765 old_dissector_add("udp.port", gbl_diameterUdpPort, dissect_diameter);
766 #ifdef SCTP_DISSECTORS_ENABLED
767 old_dissector_add("sctp.srcport", gbl_diameterSctpPort, dissect_diameter);
768 old_dissector_add("sctp.destport", gbl_diameterSctpPort, dissect_diameter);