/**************************************************************************** 
** File: radius_3gpp2.c
**
** Author: Mike Borella
**
** Comments: Support for decoding 3GPP2 RADIUS attributes.
**
** $Id: radius_3gpp2.c,v 1.4 2006/11/21 07:47:34 farooq-i-azam Exp $
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Library General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include "radius_3gpp2.h"
#include "iana.h"

/*
 * 3GPP2 vendor specific attributes
 */

#define RADIUS_ATTR_3GPP2_IKEPSKREQUEST     1
#define RADIUS_ATTR_3GPP2_SECURITYLEVEL     2
#define RADIUS_ATTR_3GPP2_IKEPSK            3
#define RADIUS_ATTR_3GPP2_REVERSETUNNEL     4
#define RADIUS_ATTR_3GPP2_DIFFSERVCLASS     5

#define RADIUS_ATTR_3GPP2_HAADDR            7

#define RADIUS_ATTR_3GPP2_SERVINGPCFADDR    9
#define RADIUS_ATTR_3GPP2_BSID              10
#define RADIUS_ATTR_3GPP2_USERZONE          11
#define RADIUS_ATTR_3GPP2_FORWARDMUX        12
#define RADIUS_ATTR_3GPP2_REVERSEMUX        13
#define RADIUS_ATTR_3GPP2_MNHAKEY           14
#define RADIUS_ATTR_3GPP2_MNHAKEYREQUEST    15
#define RADIUS_ATTR_3GPP2_SERVICEOPTION     16
#define RADIUS_ATTR_3GPP2_FORTRAFFICTYPE    17
#define RADIUS_ATTR_3GPP2_REVTRAFFICTYPE    18
#define RADIUS_ATTR_3GPP2_FUNDFRAMESIZE     19
#define RADIUS_ATTR_3GPP2_FORFUNDRC         20
#define RADIUS_ATTR_3GPP2_REVFUNDRC         21
#define RADIUS_ATTR_3GPP2_IPTECHNOLOGY      22
#define RADIUS_ATTR_3GPP2_COMPTUNNELIND     23
#define RADIUS_ATTR_3GPP2_RELEASEINDICATOR  24
#define RADIUS_ATTR_3GPP2_BADPPPFRAMECOUNT  25
#define RADIUS_ATTR_3GPP2_NUMACTIVETRANS    30
#define RADIUS_ATTR_3GPP2_SDBOCTETCNTTERM   31
#define RADIUS_ATTR_3GPP2_SDBOCTETCNTORIG   32
#define RADIUS_ATTR_3GPP2_NUMSDBTERM        33
#define RADIUS_ATTR_3GPP2_NUMSDBORIG        34
#define RADIUS_ATTR_3GPP2_IPQOS             36
#define RADIUS_ATTR_3GPP2_AIRLINKQOS        39
#define RADIUS_ATTR_3GPP2_AIRLINKRECTYPE    40
#define RADIUS_ATTR_3GPP2_RPSESSIONID       41
#define RADIUS_ATTR_3GPP2_AIRLINKSEQNO      42
#define RADIUS_ATTR_3GPP2_NUMHDLCBYTESRECVD 43
#define RADIUS_ATTR_3GPP2_CORRELATIONID     44
#define RADIUS_ATTR_3GPP2_MOBILEORIGTERMIND 45
#define RADIUS_ATTR_3GPP2_INBMIPOCTETCNT    46
#define RADIUS_ATTR_3GPP2_OUTMIPOCTETCNT    47
#define RADIUS_ATTR_3GPP2_SESSIONCONTINUE   48
#define RADIUS_ATTR_3GPP2_ACTIVETIME        49
#define RADIUS_ATTR_3GPP2_DCCHFRAMEFORMAT   50
#define RADIUS_ATTR_3GPP2_SESSIONBEGIN      51
#define RADIUS_ATTR_3GPP2_SOURCEIPV6ADDR    52
#define RADIUS_ATTR_3GPP2_ESN               52
#define RADIUS_ATTR_3GPP2_IPV6PDSNADDR      53
#define RADIUS_ATTR_3GPP2_DNSUPDATEREQD     54 

/*
 * 3GPP2 vendor specific attribute map
 */

strmap_t radius_attr_3gpp2_map[] =
  {
    { RADIUS_ATTR_3GPP2_IKEPSKREQUEST,    "IKE preshared key request" },
    { RADIUS_ATTR_3GPP2_SECURITYLEVEL,    "security level" },
    { RADIUS_ATTR_3GPP2_IKEPSK,           "IKE preshared key" },
    { RADIUS_ATTR_3GPP2_REVERSETUNNEL,    "reverse tunnel" }, 

    { RADIUS_ATTR_3GPP2_HAADDR,           "home agent address" },

    { RADIUS_ATTR_3GPP2_SERVINGPCFADDR,   "serving PCF address" },
    { RADIUS_ATTR_3GPP2_BSID,             "BSID" },
    { RADIUS_ATTR_3GPP2_USERZONE,         "user zone" },
    { RADIUS_ATTR_3GPP2_FORWARDMUX,       "forward MUX option" },
    { RADIUS_ATTR_3GPP2_REVERSEMUX,       "reverse MUX option" }, 
    { RADIUS_ATTR_3GPP2_MNHAKEY,          "MN-HA key" },
    { RADIUS_ATTR_3GPP2_MNHAKEYREQUEST,   "MN-HA key request" },
    { RADIUS_ATTR_3GPP2_SERVICEOPTION,    "service option" },
    { RADIUS_ATTR_3GPP2_FORTRAFFICTYPE,   "forward fundamental traffic type" },
    { RADIUS_ATTR_3GPP2_REVTRAFFICTYPE,   "reverse fundamental traffic type" },
    { RADIUS_ATTR_3GPP2_FUNDFRAMESIZE,    "fundamental frame size" },
    { RADIUS_ATTR_3GPP2_FORFUNDRC,        "forward fundamental RC" },
    { RADIUS_ATTR_3GPP2_REVFUNDRC,        "reverse fundamental RC" },
    { RADIUS_ATTR_3GPP2_IPTECHNOLOGY,     "IP technology" },
    { RADIUS_ATTR_3GPP2_COMPTUNNELIND,    "compulsory tunnel indicator" },
    { RADIUS_ATTR_3GPP2_RELEASEINDICATOR, "release indicator" },
    { RADIUS_ATTR_3GPP2_BADPPPFRAMECOUNT, "bad PPP frame count" },
    { RADIUS_ATTR_3GPP2_NUMACTIVETRANS,   "number of active transitions" },
    { RADIUS_ATTR_3GPP2_SDBOCTETCNTTERM,  "SDB octet count terminating" },
    { RADIUS_ATTR_3GPP2_SDBOCTETCNTORIG,  "SDB octet count originating" },
    { RADIUS_ATTR_3GPP2_NUMSDBTERM,       "number of SDBs terminating" },
    { RADIUS_ATTR_3GPP2_NUMSDBORIG,       "number of SDBs originating" },
    { RADIUS_ATTR_3GPP2_IPQOS,            "IP QoS" },
    { RADIUS_ATTR_3GPP2_AIRLINKQOS,       "airlink QoS" },
    { RADIUS_ATTR_3GPP2_AIRLINKRECTYPE,   "airlink record type" },
    { RADIUS_ATTR_3GPP2_RPSESSIONID,      "RP session ID" },
    { RADIUS_ATTR_3GPP2_AIRLINKSEQNO,     "airlink sequence number" },
    { RADIUS_ATTR_3GPP2_NUMHDLCBYTESRECVD,"number of HDLC bytes received" },
    { RADIUS_ATTR_3GPP2_CORRELATIONID,    "correlation ID" },
    { RADIUS_ATTR_3GPP2_MOBILEORIGTERMIND,"mobile orig/term indicator" },
    { RADIUS_ATTR_3GPP2_INBMIPOCTETCNT,   "inbound mobile IP octet count" },
    { RADIUS_ATTR_3GPP2_OUTMIPOCTETCNT,   "outbound mobile IP octet count" },
    { RADIUS_ATTR_3GPP2_SESSIONCONTINUE,  "session continue" },
    { RADIUS_ATTR_3GPP2_SOURCEIPV6ADDR,   "source IPv6 address" },
    { RADIUS_ATTR_3GPP2_ESN,              "ESN" },
    { RADIUS_ATTR_3GPP2_IPV6PDSNADDR,     "IPv6 DNS address" },
    { RADIUS_ATTR_3GPP2_DNSUPDATEREQD,    "DNS update required" },
    { 0, ""}    
  };

extern struct arg_t * my_args;
extern strmap_t       iana_enterprise_map[];
extern strmap_t       radius_attr_map[];

/*----------------------------------------------------------------------------
**
** dump_radius_3gpp2()
**
** Parse and dump a vendor specific RADIUS attribute.  
** Returns the number of bytes read or -1 on error.
**
**----------------------------------------------------------------------------
*/

void dump_radius_3gpp2(packet_t * pkt, u_int8_t type, u_int8_t length)
{
  u_int8_t vendortype;
  u_int8_t vendorlength;
  
  /* Get the vendor type and length */
  if (get_packet_bytes((u_int8_t *) &vendortype, pkt, 1) == 0)
    return;
  if (get_packet_bytes((u_int8_t *) &vendorlength, pkt, 1) == 0)
    return;
  
  /* Display everything so far */
  if (my_args->m)
    {
      display_minimal_string(map2str(radius_attr_map, type));
      display_minimal_string("[");
      display_minimal_string(map2str(iana_enterprise_map, 
				     IANA_ENTERPRISE_3GPP2));
      display_minimal_string("]: ");
      display_minimal_string(map2str(radius_attr_3gpp2_map, vendortype));
      display_minimal_string(" ");
    }
  else
    {
      display_strmap("Attribute type", type, radius_attr_map);
      display("  Length", &length, 1, DISP_DEC);
      display_strmap("  Vendor", IANA_ENTERPRISE_3GPP2, iana_enterprise_map);
      display_strmap("  Type", vendortype, radius_attr_3gpp2_map);
      display("  Length", &vendorlength, 1, DISP_DEC);
    }

  /* Based on the vendor type, decode the data field */
  switch(vendortype)
    {
      /* This is for attributes that are 4 byte integers */
    case RADIUS_ATTR_3GPP2_IKEPSKREQUEST:
    case RADIUS_ATTR_3GPP2_SECURITYLEVEL:
    case RADIUS_ATTR_3GPP2_REVERSETUNNEL:
    case RADIUS_ATTR_3GPP2_USERZONE:
    case RADIUS_ATTR_3GPP2_FORWARDMUX:
    case RADIUS_ATTR_3GPP2_REVERSEMUX:
    case RADIUS_ATTR_3GPP2_MNHAKEYREQUEST:
    case RADIUS_ATTR_3GPP2_SERVICEOPTION:
    case RADIUS_ATTR_3GPP2_FORTRAFFICTYPE:
    case RADIUS_ATTR_3GPP2_REVTRAFFICTYPE:
    case RADIUS_ATTR_3GPP2_FUNDFRAMESIZE:
    case RADIUS_ATTR_3GPP2_FORFUNDRC:
    case RADIUS_ATTR_3GPP2_REVFUNDRC:
    case RADIUS_ATTR_3GPP2_IPTECHNOLOGY:
    case RADIUS_ATTR_3GPP2_COMPTUNNELIND:
    case RADIUS_ATTR_3GPP2_BADPPPFRAMECOUNT:
      {
	u_int32_t value;
	
	/* Get the value */
	if (get_packet_bytes((u_int8_t *) &value, pkt, 4) == 0)
	  return;
	
	/* Conversion */
	value = ntohl(value);
	
	/* Display it */
	if (my_args->m)
	  display_minimal((u_int8_t *) &value, 4, DISP_DEC);
	else
	  display("  Value", (u_int8_t *) &value, 4, DISP_DEC);
	
      }
      break;
      
    case RADIUS_ATTR_3GPP2_HAADDR:
    case RADIUS_ATTR_3GPP2_SERVINGPCFADDR:
      {
	u_int32_t addr;
	
	if (get_packet_bytes((u_int8_t *) &addr, pkt, 4) == 0)
	  return;
	
	/* Display */
	if (my_args->m)
	  display_minimal((u_int8_t *) &addr, 4, DISP_DOTTEDDEC);
	else
	  display("  Address", (u_int8_t *) &addr, 4, DISP_DOTTEDDEC);
	
      }
      break;
      
      /* This is for attributes that are hex strings */
    case RADIUS_ATTR_3GPP2_IKEPSK:
    default:
      {
	u_int8_t * generic_value;
	
	/* Allocate memory for the value then get it */
	generic_value = my_malloc(vendorlength-2);
	if (get_packet_bytes(generic_value, pkt, vendorlength-2) == 0)
	  return;
	
	/* Display */
	if (my_args->m)
	  display_minimal(generic_value, vendorlength-2, DISP_HEX);
	else
	  display("  Value", generic_value, vendorlength-2, 
		  DISP_HEX_MULTILINE);
	
	/* Free the memory of the value */
	my_free(generic_value);
	
      }
      break;
      
    } /* switch */
}
     
