/**************************************************************************** 
**
** File: isakmp.c
**
** Extensions and additions by: Stuart Stock (stuart@ins.com)
** Original Author: Mike Borella
**
** Comments: Dump ISAKMP headers under IPSec DOI 
**
** See RFC 2408 "Internet Security Association and Key Management Protocol"
** and RFC 2407 "The Internet IP Security Domain Interpretation for ISAKMP"
**
** and when you can't find the value anywhere else, look in:
** draft-ietf-ipsec-ike-01 "The Internet Key Exchange (IKE)"
**
** $Id: isakmp.c,v 1.11 2007/06/25 13:03:00 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 "isakmp.h"
#include "payload.h"

extern strmap_t ipproto_map[];
extern struct arg_t * my_args;

#define ISAKMP_DOI_IPSEC         1

/*
 * Static part of ISAKMP header
 */

typedef struct isakmp
{
  char i_cookie[8];
  char r_cookie[8];
#if defined(WORDS_BIGENDIAN)
  u_int16_t maj_version:4,
            min_version:4,
            next_payload:8;
#else
  u_int16_t next_payload:8,
            min_version:4,
            maj_version:4;
#endif
  u_int8_t  exchange_type;
  u_int8_t  flags;
  u_int32_t msg_id;
  u_int32_t length;
} isakmp_t;


/*
 * ISAKMP Generic Payload Header
 */

typedef struct isakmpgeneric
{
  u_int8_t  next_payload;
  u_int8_t  reserved;
  u_int16_t length;
} isakmpgeneric_t;

/*
 * ISAKMP proposal payload (partial)
 */

typedef struct isakmpproposal
{
  u_int8_t  number;
  u_int8_t  protocol_id;
  u_int8_t  spi_size;
  u_int8_t  num_transforms;
} isakmpproposal_t;


/*
 * ISAKMP transform payload (partial)
 */

typedef struct isakmptransform
{
  u_int8_t  number;
  u_int8_t  id;
  u_int16_t reserved;
} isakmptransform_t;

/*
 * ISAKMP flags
 */

#define ISAKMP_FLAG_ENCRYPTION 0x01
#define ISAKMP_FLAG_COMMIT     0x02
#define ISAKMP_FLAG_AUTHONLY   0x04

/*
 * ISAKMP payload types
 */

#define ISAKMP_PAYLOAD_NONE             0
#define ISAKMP_PAYLOAD_SA               1
#define ISAKMP_PAYLOAD_PROPOSAL         2
#define ISAKMP_PAYLOAD_TRANSFORM        3
#define ISAKMP_PAYLOAD_KEYEXCHANGE      4
#define ISAKMP_PAYLOAD_IDENTIFICATION   5
#define ISAKMP_PAYLOAD_CERTIFICATE      6
#define ISAKMP_PAYLOAD_CERTIFICATEREQ   7
#define ISAKMP_PAYLOAD_HASH             8
#define ISAKMP_PAYLOAD_SIGNATURE        9
#define ISAKMP_PAYLOAD_NONCE            10
#define ISAKMP_PAYLOAD_NOTIFICATION     11
#define ISAKMP_PAYLOAD_DELETE           12
#define ISAKMP_PAYLOAD_VENDORID         13

/*
 * ISAKMP payload map
 */

strmap_t isakmp_payload_map[] = 
{
  { ISAKMP_PAYLOAD_NONE,           "none" },
  { ISAKMP_PAYLOAD_SA,             "security association" },
  { ISAKMP_PAYLOAD_PROPOSAL,       "proposal" },
  { ISAKMP_PAYLOAD_TRANSFORM,      "transform" },
  { ISAKMP_PAYLOAD_KEYEXCHANGE,    "key exchange" },
  { ISAKMP_PAYLOAD_IDENTIFICATION, "identification" },
  { ISAKMP_PAYLOAD_CERTIFICATE,    "certificate" },
  { ISAKMP_PAYLOAD_CERTIFICATEREQ, "certificate request" },
  { ISAKMP_PAYLOAD_HASH,           "hash" },
  { ISAKMP_PAYLOAD_SIGNATURE,      "signature" },
  { ISAKMP_PAYLOAD_NONCE,          "nonce" },
  { ISAKMP_PAYLOAD_NOTIFICATION,   "notification" },
  { ISAKMP_PAYLOAD_DELETE,         "delete" },
  { ISAKMP_PAYLOAD_VENDORID,       "vendor id" },
  { 0, "" }
};


/*
 * ISAKMP exchange types
 */

#define ISAKMP_EXCHANGE_NONE             0
#define ISAKMP_EXCHANGE_BASE             1
#define ISAKMP_EXCHANGE_IDPROTECT        2
#define ISAKMP_EXCHANGE_AUTHONLY         3
#define ISAKMP_EXCHANGE_AGGRESSIVE       4
#define ISAKMP_EXCHANGE_INFORMATIONAL    5

/*
 * ISAKMP exchange map
 */

strmap_t isakmp_exchange_map[] = 
{
  { ISAKMP_EXCHANGE_NONE,           "none" },
  { ISAKMP_EXCHANGE_BASE,           "base" },
  { ISAKMP_EXCHANGE_IDPROTECT,      "identity protection" },
  { ISAKMP_EXCHANGE_AUTHONLY,       "authentication only" },
  { ISAKMP_EXCHANGE_AGGRESSIVE,     "aggressive" },
  { ISAKMP_EXCHANGE_INFORMATIONAL,  "informational" },
  { 0, "" }
};


/*
 * Situation definitions for IPSEC DOI
 */

#define ISAKMP_SIT_IDENTITYONLY  0x01
#define ISAKMP_SIT_SECRECY       0x02
#define ISAKMP_SIT_INTEGRITY     0x04

/*
 * IPSEC DOI situation map
 */

strmap_t isakmp_doisituation_map[] = 
{
  { ISAKMP_SIT_IDENTITYONLY,        "identity only" },
  { ISAKMP_SIT_SECRECY,             "secrecy" },
  { ISAKMP_SIT_INTEGRITY,           "integrity" },
  { 0, "" }
};

/*
 * Protocol ID definitions
 */

#define ISAKMP_PROTOCOLID_RESERVED     0
#define ISAKMP_PROTOCOLID_ISAKMP       1
#define ISAKMP_PROTOCOLID_IPSECAH      2
#define ISAKMP_PROTOCOLID_IPSECESP     3
#define ISAKMP_PROTOCOLID_IPCOMP       4

/*
 * Protocol ID map
 */

strmap_t isakmp_protocolid_map[] = 
{
  { ISAKMP_PROTOCOLID_RESERVED,     "reserved" },
  { ISAKMP_PROTOCOLID_ISAKMP,       "ISAKMP" },
  { ISAKMP_PROTOCOLID_IPSECAH,      "IPSEC AH" },
  { ISAKMP_PROTOCOLID_IPSECESP,     "IPSEC ESP" },
  { ISAKMP_PROTOCOLID_IPCOMP,       "IPCOMP" },
  { 0, "" }
};

/*
 * Transform definitions
 */

#define ISAKMP_TRANSFORM_RESERVED   0
#define ISAKMP_TRANSFORM_KEYIKE     1

/*
 * Transform map
 */

strmap_t isakmp_transform_map[] = 
{
  { ISAKMP_TRANSFORM_RESERVED,     "reserved" },
  { ISAKMP_TRANSFORM_KEYIKE,       "KEY_IKE" },
  { 0, "" }
};

/*
 * Attribute types
 */

#define ISAKMP_ATTR_ENCRALG          1
#define ISAKMP_ATTR_HASHALG          2
#define ISAKMP_ATTR_AUTHMETHOD       3
#define ISAKMP_ATTR_GROUPDESC        4
#define ISAKMP_ATTR_GROUPTYPE        5
#define ISAKMP_ATTR_GROUPPRIME       6
#define ISAKMP_ATTR_GROUPGEN1        7
#define ISAKMP_ATTR_GROUPGEN2        8
#define ISAKMP_ATTR_GROUPCURVEA      9
#define ISAKMP_ATTR_GROUPCURVEB      10
#define ISAKMP_ATTR_LIFETYPE         11
#define ISAKMP_ATTR_LIFEDURATION     12
#define ISAKMP_ATTR_PRF              13
#define ISAKMP_ATTR_KEYLENGTH        14
#define ISAKMP_ATTR_FIELDSIZE        15
#define ISAKMP_ATTR_GROUPORDER       16

/*
 * Attribute type map
 */

strmap_t isakmp_attr_map[] = 
{
  { ISAKMP_ATTR_ENCRALG,      "encryption algorithm" },
  { ISAKMP_ATTR_HASHALG,      "hash algorithm" },
  { ISAKMP_ATTR_AUTHMETHOD,   "authentication method" },
  { ISAKMP_ATTR_GROUPDESC,    "group description" },
  { ISAKMP_ATTR_GROUPTYPE,    "group type" },
  { ISAKMP_ATTR_GROUPPRIME,   "group prime" },
  { ISAKMP_ATTR_GROUPGEN1,    "group generator 1" },
  { ISAKMP_ATTR_GROUPGEN2,    "group generator 2" },
  { ISAKMP_ATTR_GROUPCURVEA,  "group curve A" },
  { ISAKMP_ATTR_GROUPCURVEB,  "group curve B" },
  { ISAKMP_ATTR_LIFETYPE,     "life type" },
  { ISAKMP_ATTR_LIFEDURATION, "life duration" },
  { ISAKMP_ATTR_PRF,          "PRF" },
  { ISAKMP_ATTR_KEYLENGTH,    "key length" },
  { ISAKMP_ATTR_FIELDSIZE,    "field size" },
  { ISAKMP_ATTR_GROUPORDER,   "group order" },
  { 0, "" }
};

/*
 * Encryption algorithm attribute types 
 */

#define ISAKMP_ATTR_ENCRALG_DESCBC           1
#define ISAKMP_ATTR_ENCRALG_IDEACBC          2
#define ISAKMP_ATTR_ENCRALG_BLOWFISHCBC      3
#define ISAKMP_ATTR_ENCRALG_RC5R16B64CBC     4
#define ISAKMP_ATTR_ENCRALG_3DESCBC          5
#define ISAKMP_ATTR_ENCRALG_CASTCBC          6

/*
 * Encryption algorithm attribute map 
 */

strmap_t isakmp_attr_encralg_map[] = 
{
  { ISAKMP_ATTR_ENCRALG_DESCBC,       "DES-CBC" },
  { ISAKMP_ATTR_ENCRALG_IDEACBC,      "IDEA-CBC" },
  { ISAKMP_ATTR_ENCRALG_BLOWFISHCBC,  "blowfish-CBC" },
  { ISAKMP_ATTR_ENCRALG_RC5R16B64CBC, "RC5-R16-B64-CBC" },
  { ISAKMP_ATTR_ENCRALG_3DESCBC,      "3DES-CBC" },
  { ISAKMP_ATTR_ENCRALG_CASTCBC,      "CAST-CBC" },
  { 0, "" }
};

/*
 * Hash algorithm attribute types 
 */

#define ISAKMP_ATTR_HASHALG_MD5               1
#define ISAKMP_ATTR_HASHALG_SHA               2
#define ISAKMP_ATTR_HASHALG_TIGER             3

/*
 * Hash algorithm attribute map
 */

strmap_t isakmp_attr_hashalg_map[] = 
{
  { ISAKMP_ATTR_HASHALG_MD5,          "MD5" },
  { ISAKMP_ATTR_HASHALG_SHA,          "SHA" },
  { ISAKMP_ATTR_HASHALG_TIGER,        "Tiger" },
  { 0, "" }
};

/*
 * Authentication method attribute types 
 */

#define ISAKMP_ATTR_AUTHMETHOD_PRESHAREDKEY    1
#define ISAKMP_ATTR_AUTHMETHOD_DSSSIG          2
#define ISAKMP_ATTR_AUTHMETHOD_RSASIG          3
#define ISAKMP_ATTR_AUTHMETHOD_ENCRRSA         4
#define ISAKMP_ATTR_AUTHMETHOD_REVENCRRSA      5

/*
 * Authentication method attribute map
 */

strmap_t isakmp_attr_authmethod_map[] = 
{
  { ISAKMP_ATTR_AUTHMETHOD_PRESHAREDKEY,      "pre-shared key" },
  { ISAKMP_ATTR_AUTHMETHOD_DSSSIG,            "DSS signatures" },
  { ISAKMP_ATTR_AUTHMETHOD_RSASIG,            "RSA signatures" },
  { ISAKMP_ATTR_AUTHMETHOD_ENCRRSA,           "encryption with RSA" },
  { ISAKMP_ATTR_AUTHMETHOD_REVENCRRSA,        "revised encryption with RSA" },
  { 0, "" }
};


/*
 * Group description attribute types 
 */

#define ISAKMP_ATTR_GROUPDESC_MODP768          1
#define ISAKMP_ATTR_GROUPDESC_MODP1024         2
#define ISAKMP_ATTR_GROUPDESC_EC2N155          3
#define ISAKMP_ATTR_GROUPDESC_EC2N185          4

/*
 * Group description attribute map
 */

strmap_t isakmp_attr_groupdesc_map[] = 
{
  { ISAKMP_ATTR_GROUPDESC_MODP768,        "768-bit MODP group" },
  { ISAKMP_ATTR_GROUPDESC_MODP1024,       "1024-bit MODP group" },
  { ISAKMP_ATTR_GROUPDESC_EC2N155,        "EC2N group on GP[2^155]" },
  { ISAKMP_ATTR_GROUPDESC_EC2N185,        "EC2N group on GP[2^185]" },
  { 0, "" }
};

/*
 * Group type attribute types 
 */

#define ISAKMP_ATTR_GROUPTYPE_MODP             1
#define ISAKMP_ATTR_GROUPTYPE_ECP              2
#define ISAKMP_ATTR_GROUPTYPE_EC2N             3

/*
 * Group type attribute map
 */

strmap_t isakmp_attr_grouptype_map[] = 
{
  { ISAKMP_ATTR_GROUPTYPE_MODP,        "modular exponentiation" },
  { ISAKMP_ATTR_GROUPTYPE_ECP,         "elliptical curve over GF[P]" },
  { ISAKMP_ATTR_GROUPTYPE_EC2N,        "elliptical curve over GF[2^N]" },
  { 0, "" }
};

/*
 * Life type attribute types 
 */

#define ISAKMP_ATTR_LIFETYPE_SECONDS    1
#define ISAKMP_ATTR_LIFETYPE_KILOBYTES  2

/*
 * Life type attribute map
 */

strmap_t isakmp_attr_lifetype_map[] = 
{
  { ISAKMP_ATTR_LIFETYPE_SECONDS,        "seconds" },
  { ISAKMP_ATTR_LIFETYPE_KILOBYTES,      "kilobytes" },
  { 0, "" }
};

void isakmp_next_payload(packet_t *, u_int8_t);

/*----------------------------------------------------------------------------
**
** dump_isakmp_attributes()
**
** Parse an ISAKMP attribute set and interpret it.  This is for IKE with 
** the IPSEC DOI only!
**
**----------------------------------------------------------------------------
*/

void dump_isakmp_attributes(u_int8_t *attr, int len)
{
  u_int8_t * p;
  u_int16_t  type;
  u_int16_t  length;
  u_int8_t * value;

  p = attr;

  while (1)
    {
      /* get the type */
      if (p+2 > attr+len)
	return;
      memcpy((void *) &type, (void * ) p, 2);
      type = ntohs(type);
      p = p + 2;
      if (type >= 0x8000)
	{
	  u_int16_t temp;

	  temp = type - 0x8000;
	  display_strmap("Attribute", temp, isakmp_attr_map);
	}
      else
	display_strmap("Attribute", type, isakmp_attr_map);


      /* determine whether we have a length field or not */
      if (type < 0x8000)
	{
	  /* get length field */
	  if (p+2 > attr+len)
	    return;
	  memcpy((void *) &length, (void *) p, 2);
	  length = ntohs(length);
	  p = p + 2;
	  display("  Length", (u_int8_t *) &length, 2, DISP_DEC);
	}
      else
	length = 2;

      /* 
       * Get the value.  Values without a length field should be 2 bytes.
       * It must say that in some RFC but I just can't find it...
       */

      if (p+length > attr+len)
	return;
      value = my_malloc(length);
      memcpy((void *) value, (void *) p, length);
      reverse_byte_order(value, length);
      p = p + length;

      /* display the value, based on its type */
      switch(type & 0x7fff)
	{
	case ISAKMP_ATTR_ENCRALG:
	  display_strmap("  Value", (u_int16_t) *value, 
			 isakmp_attr_encralg_map);
	  break;

	case ISAKMP_ATTR_HASHALG:
	  display_strmap("  Value", (u_int16_t) *value, 
			 isakmp_attr_hashalg_map);
	  break;

	case ISAKMP_ATTR_AUTHMETHOD:
	  display_strmap("  Value", (u_int16_t) *value, 
			 isakmp_attr_authmethod_map);
	  break;

	case ISAKMP_ATTR_GROUPDESC:
	  display_strmap("  Value", (u_int16_t) *value, 
			 isakmp_attr_groupdesc_map);
	  break;

	case ISAKMP_ATTR_GROUPTYPE:
	  display_strmap("  Value", (u_int16_t) *value, 
			 isakmp_attr_grouptype_map);
	  break;

	case ISAKMP_ATTR_LIFETYPE:
	  display_strmap("  Value", (u_int16_t) *value, 
			 isakmp_attr_lifetype_map);
	  break;

	default:
	  display("  Value", (u_int8_t *) value, length, DISP_DEC);
	  break;
	}

      my_free(value);
    }
}

/*----------------------------------------------------------------------------
**
** dump_isakmp_transform()
**
** Parse ISAKMP transform payload
**
**----------------------------------------------------------------------------
*/

void dump_isakmp_transform(packet_t * pkt)
{
  isakmpgeneric_t   hdr;
  isakmptransform_t transform;
  u_int8_t *        sa_attributes = NULL;
  int               sa_attributes_len;

  /* get the generic header */
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(hdr)) == 0)
    return;

  /* conversions */
  hdr.length = ntohs(hdr.length);

  if (my_args->m)
    {
      /* nothing to do here... */
    }
  else
    {
      display_header_banner("ISAKMP/IKE transform");
      display_strmap("Next payload", hdr.next_payload, isakmp_payload_map);
      display("Reserved", (u_int8_t *) &hdr.reserved, 1, DISP_DEC);
      display("Payload length", (u_int8_t *) &hdr.length, 2, DISP_DEC);
    }
  
  /* Get the fixed transform payload, then the SA attributes field */
  if (get_packet_bytes((u_int8_t *) &transform, pkt, 
		       sizeof(isakmptransform_t)) == 0)
    return;
  sa_attributes_len = hdr.length - 8;
  if (sa_attributes_len > 0)
    {
      sa_attributes = (u_int8_t *) my_malloc(sa_attributes_len);
      if (get_packet_bytes(sa_attributes, pkt, sa_attributes_len) == 0)
	{
	  my_free(sa_attributes);
	  return;
	}
    }

  /* conversion */
  transform.reserved = ntohs(transform.reserved);

  if (my_args->m)
    {
      /* nothing to do here... */
    }
  else
    {
      display("Transform #", (u_int8_t *) &transform.number, 1, DISP_DEC);
      display_strmap("Transform ID", transform.id, isakmp_transform_map);
      display("Reserved", (u_int8_t *) &transform.reserved, 2, DISP_DEC);
      if (sa_attributes_len > 0)
	dump_isakmp_attributes(sa_attributes, sa_attributes_len);
    }
  
  /* Dump the hex buffer */
  hexbuffer_flush();
  
  /* free the sa_attrinutes memory */
  if (sa_attributes_len > 0)
    my_free(sa_attributes);
}

/*----------------------------------------------------------------------------
**
** dump_isakmp_proposal()
**
** Parse ISAKMP proposal payload
**
**----------------------------------------------------------------------------
*/

void dump_isakmp_proposal(packet_t * pkt)
{
  isakmpgeneric_t  hdr;
  isakmpproposal_t proposal;
  u_int8_t *       spi = NULL;
  int              i;

  /* get the generic header */
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(hdr)) == 0)
    return;

  /* conversions */
  hdr.length = ntohs(hdr.length);

  if (my_args->m)
    {
      /* nothing to do here... */
    }
  else
    {
      display_header_banner("ISAKMP/IKE proposal");
      display_strmap("Next payload", hdr.next_payload, isakmp_payload_map);
      display("Reserved", (u_int8_t *) &hdr.reserved, 1, DISP_DEC);
      display("Payload length", (u_int8_t *) &hdr.length, 2, DISP_DEC);
    }

  /* get the proposal, then the SPI */
  if (get_packet_bytes((u_int8_t *) &proposal, pkt, sizeof(proposal)) == 0)
    return;
  if (proposal.spi_size > 0)
    {
      spi = my_malloc(proposal.spi_size);
      if (get_packet_bytes((u_int8_t *) &spi, pkt, proposal.spi_size) == 0)
	{
	  my_free(spi);
	  return;
	}
    }
  
  if (my_args->m)
    {
      /* nothing to do here... */
    }
  else
    {
      display("Proposal #", (u_int8_t *) &proposal.number, 1, DISP_DEC);
      display_strmap("Protocol ID", proposal.protocol_id, 
		     isakmp_protocolid_map);
      display("SPI size", (u_int8_t *) &proposal.spi_size, 1, DISP_DEC);
      display("Number of transforms", (u_int8_t *) &proposal.num_transforms, 
	      1, DISP_DEC);
      if (proposal.spi_size > 0)
	display("SPI", (u_int8_t *) &spi, proposal.spi_size, DISP_HEX);
    }

  /* Dump the hex buffer */
  hexbuffer_flush();
  
  /* free the spi memory */
  if (proposal.spi_size > 0)
    my_free(spi);

  /* Do each of the transform headers */
  i = 0;
  while (i < proposal.num_transforms)
    {
      dump_isakmp_transform(pkt);
      i++;
    }

}

/*----------------------------------------------------------------------------
**
** dump_isakmp_sa()
**
** Parse ISAKMP SA payload
**
**----------------------------------------------------------------------------
*/

void dump_isakmp_sa(packet_t * pkt)
{
  isakmpgeneric_t hdr;
  u_int32_t       doi;
  u_int32_t       situation;

  /* get the generic header */
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(hdr)) == 0)
    return;

  /* conversions */
  hdr.length = ntohs(hdr.length);

  if (my_args->m)
    {
      /* nothing to do here... */
    }
  else
    {
      display_header_banner("ISAKMP/IKE SA");
      display_strmap("Next payload", hdr.next_payload, isakmp_payload_map);
      display("Reserved", (u_int8_t *) &hdr.reserved, 1, DISP_DEC);
      display("Payload length", (u_int8_t *) &hdr.length, 2, DISP_DEC);
    }

  /*
   * The next 4 bytes is the DOI.  We need to get it now in order to figure
   * out what the situation is...
   */

  if (get_packet_bytes((u_int8_t *) &doi, pkt, 4) == 0)
    return;
  
  /* conversion */
  doi = ntohl(doi);

  if (!my_args->m)
    display("DOI", (u_int8_t *) &doi, 4, DISP_DEC);

  /* get the situation based on the DOI.  The IPSEC DOI situation is 4 bytes */
  if (doi != ISAKMP_DOI_IPSEC)
    return;
  if (get_packet_bytes((u_int8_t *) &situation, pkt, 4) == 0)
    return;

  /* conversion */
  situation = ntohl(situation);

  if (!my_args->m)
    display_strmap("Situation", situation, isakmp_doisituation_map);


  /* Dump the hex buffer */
  hexbuffer_flush();

  /*
   * Since an SA payload is always followed by a number of 
   * proposal / transform payload sets, we parse then next, automatically, 
   * then we do the given "next payload".  How annoying...
   */

  dump_isakmp_proposal(pkt);

  /* determine the next payload and parse it */
  isakmp_next_payload(pkt, hdr.next_payload); 
}

/*----------------------------------------------------------------------------
**
** dump_isakmp_vendorid()
**
** Parse ISAKMP vendorid payload
**
**----------------------------------------------------------------------------
*/

void dump_isakmp_vendorid(packet_t * pkt)
{
  isakmpgeneric_t hdr;
  u_int8_t *      vendorid;
  int             len;

  /* get the generic header */
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(hdr)) == 0)
    return;

  /* conversions */
  hdr.length = ntohs(hdr.length);

  if (my_args->m)
    {
      /* nothing to do here... */
    }
  else
    {
      display_header_banner("ISAKMP/IKE vendor ID");
      display_strmap("Next payload", hdr.next_payload, isakmp_payload_map);
      display("Reserved", (u_int8_t *) &hdr.reserved, 1, DISP_DEC);
      display("Payload length", (u_int8_t *) &hdr.length, 2, DISP_DEC);
    }
  
  /*
   * This payload contains a vendor ID hash that is hdr.length - 4 bytes
   * First allocate memory and grab it
   */

  len = hdr.length - sizeof(isakmpgeneric_t);
  vendorid = (u_int8_t *) my_malloc(len);
  if (get_packet_bytes(vendorid, pkt, len) == 0)
    {
      my_free(vendorid);
      return;
    }
  
  display("Vendor ID (hash)", vendorid, len, DISP_HEX_MULTILINE);

  /* free memory */
  my_free(vendorid);
  
  /* determine the next payload and parse it */
  isakmp_next_payload(pkt, hdr.next_payload); 
}

/*----------------------------------------------------------------------------
**
** dump_isakmp_delete()
**
** Parse ISAKMP delete payload
**
**----------------------------------------------------------------------------
*/

void dump_isakmp_delete(packet_t * pkt)
{
  isakmpgeneric_t hdr;

  /* get the generic header */
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(hdr)) == 0)
    return;

  /* conversions */
  hdr.length = ntohs(hdr.length);

  if (my_args->m)
    {
      /* nothing to do here... */
    }
  else
    {
      display_header_banner("ISAKMP/IKE delete");
      display_strmap("Next payload", hdr.next_payload, isakmp_payload_map);
      display("Reserved", (u_int8_t *) &hdr.reserved, 1, DISP_DEC);
      display("Payload length", (u_int8_t *) &hdr.length, 2, DISP_DEC);
    }

}

/*----------------------------------------------------------------------------
**
** isakmp_next_payload()
**
** Call the proper payload parser based on the given next payload field
**
**----------------------------------------------------------------------------
*/

void isakmp_next_payload(packet_t * pkt, u_int8_t next)
{
  switch (next)
    {
    case ISAKMP_PAYLOAD_DELETE:
      dump_isakmp_delete(pkt);
      break;

    case ISAKMP_PAYLOAD_SA:
      dump_isakmp_sa(pkt);
      break;

    case ISAKMP_PAYLOAD_VENDORID:
      dump_isakmp_vendorid(pkt);
      break;

    default:
      /* unrecognized payload, so we'll fall through and stop */
      break;
    }
}

/*----------------------------------------------------------------------------
**
** dump_isakmp()
**
** Parse ISAKMP packet and dump fields.  
**
**----------------------------------------------------------------------------
*/

void dump_isakmp(packet_t * pkt)
{
  isakmp_t isakmp;
  u_int8_t major;
  u_int8_t minor;
  u_int8_t next;
  u_int8_t e_flag, c_flag, a_flag;

  /* Set the layer */
  set_layer(LAYER_APPLICATION);

  /*
   * Get the main ISAKMP header
   */

  if (get_packet_bytes((u_int8_t *) &isakmp, pkt, sizeof(isakmp_t)) == 0)
    return;
  
  /*
   * Conversions
   */
  
  isakmp.msg_id = ntohl(isakmp.msg_id);
  isakmp.length = ntohl(isakmp.length);
  next = isakmp.next_payload;
  major = isakmp.maj_version;
  minor = isakmp.min_version;
  e_flag = isakmp.flags & ISAKMP_FLAG_ENCRYPTION;
  c_flag = isakmp.flags & ISAKMP_FLAG_COMMIT >> 1;
  a_flag = isakmp.flags & ISAKMP_FLAG_AUTHONLY >> 2;

  /*
   * Dump header
   */

  if (my_args->m)
    {
      /* do something here */
    }
  else
    {
      display_header_banner("ISAKMP/IKE Header");

      display("Initiatior cookie", (u_int8_t *) &isakmp.i_cookie, 8, DISP_HEX);
      display("Responder cookie", (u_int8_t *) &isakmp.r_cookie, 8, DISP_HEX);
      display_strmap("Next payload", next, isakmp_payload_map);
      display("Major version", (u_int8_t *) &major, 1, DISP_DEC);
      display("Minor version", (u_int8_t *) &minor, 1, DISP_DEC);
      display_strmap("Exchange type", isakmp.exchange_type, 
		     isakmp_exchange_map);
      display("Encryption flag", (u_int8_t *) &e_flag, 1, DISP_DEC);
      display("Commit flag", (u_int8_t *) &c_flag, 1, DISP_DEC);
      display("Auth only flag", (u_int8_t *) &a_flag, 1, DISP_DEC);
      display("Message ID", (u_int8_t *) &isakmp.msg_id, 4, DISP_DEC);
      display("Length", (u_int8_t *) &isakmp.length, 4, DISP_DEC);
    }

  /*
   * Dump the hex buffer 
   */

  hexbuffer_flush();

  /*
   * Short circuit processing if this is the only header or encryption
   * bit is set
   */

  if (next == ISAKMP_PAYLOAD_NONE || e_flag)
    {
      return;
    }

  /* determine the next payload and parse it */
  isakmp_next_payload(pkt, next);

}
