Logo Search packages:      
Sourcecode: openswan version File versions  Download package

packet.c

/* parsing packets: formats and tools
 * Copyright (C) 1997 Angelos D. Keromytis.
 * Copyright (C) 1998-2001  D. Hugh Redelmeier.
 *
 * 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.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * 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 General Public License
 * for more details.
 *
 * RCSID $Id: packet.c,v 1.49 2005-01-23 18:53:56 mcr Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <netinet/in.h>
#include <string.h>

#include <openswan.h>

#include "constants.h"
#include "oswlog.h"

#include "packet.h"

/* ISAKMP Header: for all messages
 * layout from RFC 2408 "ISAKMP" section 3.1
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                          Initiator                            !
 * !                            Cookie                             !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                          Responder                            !
 * !                            Cookie                             !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !  Next Payload ! MjVer ! MnVer ! Exchange Type !     Flags     !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                          Message ID                           !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                            Length                             !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */

static field_desc isa_fields[] = {
    { ft_raw, COOKIE_SIZE, "initiator cookie", NULL },
    { ft_raw, COOKIE_SIZE, "responder cookie", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_enum, 8/BITS_PER_BYTE, "ISAKMP version", &version_names },
    { ft_enum, 8/BITS_PER_BYTE, "exchange type", &exchange_names },
    { ft_set, 8/BITS_PER_BYTE, "flags", flag_bit_names },
    { ft_raw, 32/BITS_PER_BYTE, "message ID", NULL },
    { ft_len, 32/BITS_PER_BYTE, "length", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_hdr_desc = { "ISAKMP Message", isa_fields, sizeof(struct isakmp_hdr) };

/* Generic portion of all ISAKMP payloads.
 * layout from RFC 2408 "ISAKMP" section 3.2
 * This describes the first 32-bit chunk of all payloads.
 * The previous next payload depends on the actual payload type.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */

static field_desc isag_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_generic_desc = { "ISAKMP Generic Payload", isag_fields, sizeof(struct isakmp_generic) };


/* ISAKMP Data Attribute (generic representation within payloads)
 * layout from RFC 2408 "ISAKMP" section 3.3
 * This is not a payload type.
 * In TLV format, this is followed by a value field.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !A!       Attribute Type        !    AF=0  Attribute Length     !
 * !F!                             !    AF=1  Attribute Value      !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * .                   AF=0  Attribute Value                       .
 * .                   AF=1  Not Transmitted                       .
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */

/* Oakley Attributes */
static field_desc isaat_fields_oakley[] = {
    { ft_af_enum, 16/BITS_PER_BYTE, "af+type", &oakley_attr_names },
    { ft_lv, 16/BITS_PER_BYTE, "length/value", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_oakley_attribute_desc = {
    "ISAKMP Oakley attribute",
    isaat_fields_oakley, sizeof(struct isakmp_attribute) };

/* IPsec DOI Attributes */
static field_desc isaat_fields_ipsec[] = {
    { ft_af_enum, 16/BITS_PER_BYTE, "af+type", &ipsec_attr_names },
    { ft_lv, 16/BITS_PER_BYTE, "length/value", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_ipsec_attribute_desc = {
    "ISAKMP IPsec DOI attribute",
    isaat_fields_ipsec, sizeof(struct isakmp_attribute) };

/* XAUTH Attributes */
static field_desc isaat_fields_xauth[] = {
    { ft_af_loose_enum, 16/BITS_PER_BYTE, "ModeCfg attr type", &modecfg_attr_names },
    { ft_lv, 16/BITS_PER_BYTE, "length/value", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_xauth_attribute_desc = {
    "ISAKMP ModeCfg attribute",
    isaat_fields_xauth, sizeof(struct isakmp_attribute) };

/* ISAKMP Security Association Payload
 * layout from RFC 2408 "ISAKMP" section 3.4
 * A variable length Situation follows.
 * Previous next payload: ISAKMP_NEXT_SA
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !              Domain of Interpretation  (DOI)                  !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                           Situation                           ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isasa_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 32/BITS_PER_BYTE, "DOI", &doi_names },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_sa_desc = { "ISAKMP Security Association Payload", isasa_fields, sizeof(struct isakmp_sa) };

static field_desc ipsec_sit_field[] = {
    { ft_set, 32/BITS_PER_BYTE, "IPsec DOI SIT", &sit_bit_names },
    { ft_end, 0, NULL, NULL }
};

struct_desc ipsec_sit_desc = { "IPsec DOI SIT", ipsec_sit_field, sizeof(u_int32_t) };

/* ISAKMP Proposal Payload
 * layout from RFC 2408 "ISAKMP" section 3.5
 * A variable length SPI follows.
 * Previous next payload: ISAKMP_NEXT_P
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !  Proposal #   !  Protocol-Id  !    SPI Size   !# of Transforms!
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                        SPI (variable)                         !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isap_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_nat, 8/BITS_PER_BYTE, "proposal number", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "protocol ID", &protocol_names },
    { ft_nat, 8/BITS_PER_BYTE, "SPI size", NULL },
    { ft_nat, 8/BITS_PER_BYTE, "number of transforms", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_proposal_desc = { "ISAKMP Proposal Payload", isap_fields, sizeof(struct isakmp_proposal) };

/* ISAKMP Transform Payload
 * layout from RFC 2408 "ISAKMP" section 3.6
 * Variable length SA Attributes follow.
 * Previous next payload: ISAKMP_NEXT_T
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !  Transform #  !  Transform-Id !           RESERVED2           !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                        SA Attributes                          ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */

/* PROTO_ISAKMP */
static field_desc isat_fields_isakmp[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "transform ID", &isakmp_transformid_names },
    { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_isakmp_transform_desc = {
    "ISAKMP Transform Payload (ISAKMP)",
    isat_fields_isakmp, sizeof(struct isakmp_transform) };

/* PROTO_IPSEC_AH */
static field_desc isat_fields_ah[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "transform ID", &ah_transformid_names },
    { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_ah_transform_desc = {
    "ISAKMP Transform Payload (AH)",
    isat_fields_ah, sizeof(struct isakmp_transform) };

/* PROTO_IPSEC_ESP */
static field_desc isat_fields_esp[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "transform ID", &esp_transformid_names },
    { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_esp_transform_desc = {
    "ISAKMP Transform Payload (ESP)",
    isat_fields_esp, sizeof(struct isakmp_transform) };

/* PROTO_IPCOMP */
static field_desc isat_fields_ipcomp[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "transform ID", &ipcomp_transformid_names },
    { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_ipcomp_transform_desc = {
    "ISAKMP Transform Payload (COMP)",
    isat_fields_ipcomp, sizeof(struct isakmp_transform) };


/* ISAKMP Key Exchange Payload: no fixed fields beyond the generic ones.
 * layout from RFC 2408 "ISAKMP" section 3.7
 * Variable Key Exchange Data follow the generic fields.
 * Previous next payload: ISAKMP_NEXT_KE
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                       Key Exchange Data                       ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
struct_desc isakmp_keyex_desc = { "ISAKMP Key Exchange Payload", isag_fields, sizeof(struct isakmp_generic) };

/* ISAKMP Identification Payload
 * layout from RFC 2408 "ISAKMP" section 3.8
 * See "struct identity" declared later.
 * Variable length Identification Data follow.
 * Previous next payload: ISAKMP_NEXT_ID
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !   ID Type     !             DOI Specific ID Data              !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                   Identification Data                         ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isaid_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "ID type", &ident_names },  /* ??? depends on DOI? */
    { ft_nat, 8/BITS_PER_BYTE, "DOI specific A", NULL },    /* ??? depends on DOI? */
    { ft_nat, 16/BITS_PER_BYTE, "DOI specific B", NULL },   /* ??? depends on DOI? */
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_identification_desc = { "ISAKMP Identification Payload", isaid_fields, sizeof(struct isakmp_id) };

/* IPSEC Identification Payload Content
 * layout from RFC 2407 "IPsec DOI" section 4.6.2
 * See struct isakmp_id declared earlier.
 * Note: Hashing skips the ISAKMP generic payload header
 * Variable length Identification Data follow.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !  Next Payload !   RESERVED    !        Payload Length         !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !   ID Type     !  Protocol ID  !             Port              !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ~                     Identification Data                       ~
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isaiid_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "ID type", &ident_names },
    { ft_nat, 8/BITS_PER_BYTE, "Protocol ID", NULL }, /* ??? UDP/TCP or 0? */
    { ft_nat, 16/BITS_PER_BYTE, "port", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_ipsec_identification_desc = { "ISAKMP Identification Payload (IPsec DOI)", isaiid_fields, sizeof(struct isakmp_ipsec_id) };

/* ISAKMP Certificate Payload: oddball fixed field beyond the generic ones.
 * layout from RFC 2408 "ISAKMP" section 3.9
 * Variable length Certificate Data follow the generic fields.
 * Previous next payload: ISAKMP_NEXT_CERT.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Cert Encoding !                                               !
 * +-+-+-+-+-+-+-+-+                                               !
 * ~                       Certificate Data                        ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isacert_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "cert encoding", &cert_type_names },
    { ft_end, 0, NULL, NULL }
};

/* Note: the size field of isakmp_ipsec_certificate_desc cannot be
 * sizeof(struct isakmp_cert) because that will rounded up for padding.
 */
 struct_desc isakmp_ipsec_certificate_desc = { "ISAKMP Certificate Payload", isacert_fields, ISAKMP_CERT_SIZE };

/* ISAKMP Certificate Request Payload: oddball field beyond the generic ones.
 * layout from RFC 2408 "ISAKMP" section 3.10
 * Variable length Certificate Types and Certificate Authorities follow.
 * Previous next payload: ISAKMP_NEXT_CR.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !  Cert. Type   !                                               !
 * +-+-+-+-+-+-+-+-+                                               !
 * ~                    Certificate Authority                      ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isacr_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "cert type", &cert_type_names },
    { ft_end, 0, NULL, NULL }
};

/* Note: the size field of isakmp_ipsec_cert_req_desc cannot be
 * sizeof(struct isakmp_cr) because that will rounded up for padding.
 */
struct_desc isakmp_ipsec_cert_req_desc = { "ISAKMP Certificate RequestPayload", isacr_fields, ISAKMP_CR_SIZE };

/* ISAKMP Hash Payload: no fixed fields beyond the generic ones.
 * layout from RFC 2408 "ISAKMP" section 3.11
 * Variable length Hash Data follow.
 * Previous next payload: ISAKMP_NEXT_HASH.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                           Hash Data                           ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
struct_desc isakmp_hash_desc = { "ISAKMP Hash Payload", isag_fields, sizeof(struct isakmp_generic) };

/* ISAKMP Signature Payload: no fixed fields beyond the generic ones.
 * layout from RFC 2408 "ISAKMP" section 3.12
 * Variable length Signature Data follow.
 * Previous next payload: ISAKMP_NEXT_SIG.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                         Signature Data                        ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
struct_desc isakmp_signature_desc = { "ISAKMP Signature Payload", isag_fields, sizeof(struct isakmp_generic) };

/* ISAKMP Nonce Payload: no fixed fields beyond the generic ones.
 * layout from RFC 2408 "ISAKMP" section 3.13
 * Variable length Nonce Data follow.
 * Previous next payload: ISAKMP_NEXT_NONCE.
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                            Nonce Data                         ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
struct_desc isakmp_nonce_desc = { "ISAKMP Nonce Payload", isag_fields, sizeof(struct isakmp_generic) };

/* ISAKMP Notification Payload
 * layout from RFC 2408 "ISAKMP" section 3.14
 * This is followed by a variable length SPI
 * and then possibly by variable length Notification Data.
 * Previous next payload: ISAKMP_NEXT_N
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !              Domain of Interpretation  (DOI)                  !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !  Protocol-ID  !   SPI Size    !      Notify Message Type      !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                Security Parameter Index (SPI)                 ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                       Notification Data                       ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isan_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 32/BITS_PER_BYTE, "DOI", &doi_names },
    { ft_nat, 8/BITS_PER_BYTE, "protocol ID", NULL }, /* ??? really enum: ISAKMP, IPSEC, ESP, ... */
    { ft_nat, 8/BITS_PER_BYTE, "SPI size", NULL },
    { ft_enum, 16/BITS_PER_BYTE, "Notify Message Type", &ipsec_notification_names },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_notification_desc = { "ISAKMP Notification Payload", isan_fields, sizeof(struct isakmp_notification) };

/* ISAKMP Delete Payload
 * layout from RFC 2408 "ISAKMP" section 3.15
 * This is followed by a variable length SPI.
 * Previous next payload: ISAKMP_NEXT_D
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !              Domain of Interpretation  (DOI)                  !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !  Protocol-Id  !   SPI Size    !           # of SPIs           !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~               Security Parameter Index(es) (SPI)              ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isad_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 32/BITS_PER_BYTE, "DOI", &doi_names },
    { ft_nat, 8/BITS_PER_BYTE, "protocol ID", NULL }, /* ??? really enum: ISAKMP, IPSEC */
    { ft_nat, 8/BITS_PER_BYTE, "SPI size", NULL },
    { ft_nat, 16/BITS_PER_BYTE, "number of SPIs", NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_delete_desc = { "ISAKMP Delete Payload", isad_fields, sizeof(struct isakmp_delete) };

/* ISAKMP Vendor ID Payload
 * layout from RFC 2408 "ISAKMP" section 3.15
 * This is followed by a variable length VID.
 * Previous next payload: ISAKMP_NEXT_VID
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                                                               !
 * ~                        Vendor ID (VID)                        ~
 * !                                                               !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
struct_desc isakmp_vendor_id_desc = { "ISAKMP Vendor ID Payload", isag_fields, sizeof(struct isakmp_generic) };

/* MODECFG */
/*
 * From draft-dukes-ike-mode-cfg
3.2. Attribute Payload 
                           1                   2                   3 
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
     ! Next Payload  !   RESERVED    !         Payload Length        ! 
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
     !     Type      !   RESERVED    !           Identifier          ! 
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
     !                                                               ! 
     ~                           Attributes                          ~ 
     !                                                               ! 
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
*/    
static field_desc isaattr_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "Attr Msg Type", &attr_msg_type_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_nat, 16/BITS_PER_BYTE, "Identifier", NULL },
    { ft_end, 0, NULL, NULL }
};

/* MODECFG */
/* From draft-dukes-ike-mode-cfg
3.2. Attribute Payload
                           1                   2                   3  
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     ! Next Payload  !   RESERVED    !         Payload Length        !
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     !     Type      !   RESERVED    !           Identifier          !
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     !                                                               !
     !                                                               !
     ~                           Attributes                          ~
     !                                                               !
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

struct_desc isakmp_attr_desc = { "ISAKMP Mode Attribute", isaattr_fields, sizeof(struct isakmp_mode_attr) };

/* ISAKMP NAT-Traversal NAT-D
 * layout from draft-ietf-ipsec-nat-t-ike-01.txt section 3.2
 *
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !                 HASH of the address and port                  !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
struct_desc isakmp_nat_d = { "ISAKMP NAT-D Payload", isag_fields, sizeof(struct isakmp_generic) };

/* ISAKMP NAT-Traversal NAT-OA
 * layout from draft-ietf-ipsec-nat-t-ike-01.txt section 4.2
 *
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * ! Next Payload  !   RESERVED    !         Payload Length        !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !   ID Type     !   RESERVED    !            RESERVED           !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * !         IPv4 (4 octets) or IPv6 address (16 octets)           !
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static field_desc isanat_oa_fields[] = {
    { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
    { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
    { ft_len, 16/BITS_PER_BYTE, "length", NULL },
    { ft_enum, 8/BITS_PER_BYTE, "ID type", &ident_names },
    { ft_mbz, 24/BITS_PER_BYTE, NULL, NULL },
    { ft_end, 0, NULL, NULL }
};

struct_desc isakmp_nat_oa = { "ISAKMP NAT-OA Payload", isanat_oa_fields, sizeof(struct isakmp_nat_oa) };

/* descriptor for each payload type
 *
 * There is a slight problem in that some payloads differ, depending
 * on the mode.  Since this is table only used for top-level payloads,
 * Proposal and Transform payloads need not be handled.
 * That leaves only Identification payloads as a problem.
 * We make all these entries NULL
 */
struct_desc *const payload_descs[ISAKMP_NEXT_ROOF] = {
    NULL,                     /* 0 ISAKMP_NEXT_NONE (No other payload following) */
    &isakmp_sa_desc,                /* 1 ISAKMP_NEXT_SA (Security Association) */
    NULL,                     /* 2 ISAKMP_NEXT_P (Proposal) */
    NULL,                     /* 3 ISAKMP_NEXT_T (Transform) */
    &isakmp_keyex_desc,             /* 4 ISAKMP_NEXT_KE (Key Exchange) */
    NULL,                     /* 5 ISAKMP_NEXT_ID (Identification) */
    &isakmp_ipsec_certificate_desc, /* 6 ISAKMP_NEXT_CERT (Certificate) */
    &isakmp_ipsec_cert_req_desc,    /* 7 ISAKMP_NEXT_CR (Certificate Request) */
    &isakmp_hash_desc,              /* 8 ISAKMP_NEXT_HASH (Hash) */
    &isakmp_signature_desc,         /* 9 ISAKMP_NEXT_SIG (Signature) */
    &isakmp_nonce_desc,             /* 10 ISAKMP_NEXT_NONCE (Nonce) */
    &isakmp_notification_desc,            /* 11 ISAKMP_NEXT_N (Notification) */
    &isakmp_delete_desc,            /* 12 ISAKMP_NEXT_D (Delete) */
    &isakmp_vendor_id_desc,         /* 13 ISAKMP_NEXT_VID (Vendor ID) */
    &isakmp_attr_desc,                  /* 14 ISAKMP_NEXT_ATTR (ModeCfg)  */
    NULL,                               /* 15 */
    NULL,                               /* 16 */
    NULL,                               /* 17 */
    NULL,                               /* 18 */
    NULL,                               /* 19 */
    &isakmp_nat_d,                      /* 20=130 ISAKMP_NEXT_NATD (NAT-D) */
    &isakmp_nat_oa,                     /* 21=131 ISAKMP_NEXT_NATOA (NAT-OA) */
};

void
init_pbs(pb_stream *pbs, u_int8_t *start, size_t len, const char *name)
{
    pbs->container = NULL;
    pbs->desc = NULL;
    pbs->name = name;
    pbs->start = pbs->cur = start;
    pbs->roof = start + len;
    pbs->lenfld = NULL;
    pbs->lenfld_desc = NULL;
}

#ifdef DEBUG

/* print a host struct
 *
 * This code assumes that the network and host structure
 * members have the same alignment and size!  This requires
 * that all padding be explicit.
 */
void
DBG_print_struct(const char *label, const void *struct_ptr
, struct_desc *sd, bool len_meaningful)
{
    bool immediate = FALSE;
    const u_int8_t *inp = struct_ptr;
    field_desc *fp;

    DBG_log("%s%s:", label, sd->name);

    for (fp = sd->fields; fp->field_type != ft_end; fp++)
    {
      int i = fp->size;
      u_int32_t n = 0;

      switch (fp->field_type)
      {
      case ft_mbz:      /* must be zero */
          inp += i;
          break;
      case ft_nat:      /* natural number (may be 0) */
      case ft_len:      /* length of this struct and any following crud */
      case ft_lv: /* length/value field of attribute */
      case ft_enum:     /* value from an enumeration */
      case ft_loose_enum:     /* value from an enumeration with only some names known */
      case ft_af_enum:  /* Attribute Format + value from an enumeration */
      case ft_af_loose_enum:  /* Attribute Format + value from an enumeration */
      case ft_set:      /* bits representing set */
          switch (i)
          {
          case 8/BITS_PER_BYTE:
            n = *(const u_int8_t *)inp;
            break;
          case 16/BITS_PER_BYTE:
            n = *(const u_int16_t *)inp;
            break;
          case 32/BITS_PER_BYTE:
            n = *(const u_int32_t *)inp;
            break;
          default:
            bad_case(i);
          }
          switch (fp->field_type)
          {
          case ft_len:  /* length of this struct and any following crud */
          case ft_lv:         /* length/value field of attribute */
            if (!immediate && !len_meaningful)
                break;
            /* FALL THROUGH */
          case ft_nat:  /* natural number (may be 0) */
            DBG_log("   %s: %lu", fp->name, (unsigned long)n);
            break;

          case ft_af_loose_enum: /* Attribute Format + value from an enumeration */
          case ft_af_enum:    /* Attribute Format + value from an enumeration */
            if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
                immediate = TRUE;
            /* FALL THROUGH */
          case ft_enum: /* value from an enumeration */
          case ft_loose_enum: /* value from an enumeration with only some names known */
            DBG_log("   %s: %s", fp->name, enum_show(fp->desc, n));
            break;
          case ft_set:  /* bits representing set */
            DBG_log("   %s: %s", fp->name, bitnamesof(fp->desc, n));
            break;
          default:
            bad_case(fp->field_type);
          }
          inp += i;
          break;

      case ft_raw:      /* bytes to be left in network-order */
          {
            char m[50]; /* arbitrary limit on name width in log */

            snprintf(m, sizeof(m), "   %s:", fp->name);
            DBG_dump(m, inp, i);
            inp += i;
          }
          break;
      default:
          bad_case(fp->field_type);
      }
    }
}

static void
DBG_prefix_print_struct(const pb_stream *pbs
                  , const char *label, const void *struct_ptr
                  , struct_desc *sd, bool len_meaningful)
{
    /* print out a title, with a prefix of asterisks to show
     * the nesting level.
     */
    char space[40];     /* arbitrary limit on label+flock-of-* */
    size_t len = strlen(label);

    if (sizeof(space) <= len)
    {
      DBG_print_struct(label, struct_ptr, sd, len_meaningful);
    }
    else
    {
      const pb_stream *p = pbs;
      char *pre = &space[sizeof(space) - (len + 1)];

      strcpy(pre, label);

      /* put at least one * out */
      for (;;)
      {
          if (pre <= space)
            break;
          *--pre = '*';
          if (p == NULL)
            break;
          p = p->container;
      }
      DBG_print_struct(pre, struct_ptr, sd, len_meaningful);
    }
}

#endif

/* "parse" a network struct into a host struct.
 *
 * This code assumes that the network and host structure
 * members have the same alignment and size!  This requires
 * that all padding be explicit.
 *
 * If obj_pbs is supplied, a new pb_stream is created for the
 * variable part of the structure (this depends on their
 * being one length field in the structure).  The cursor of this
 * new PBS is set to after the parsed part of the struct.
 *
 * This routine returns TRUE iff it succeeds.
 */

bool
in_struct(void *struct_ptr, struct_desc *sd
, pb_stream *ins, pb_stream *obj_pbs)
{
    err_t ugh = NULL;
    u_int8_t *cur = ins->cur;

    if (ins->roof - cur < (ptrdiff_t)sd->size)
    {
        ugh = builddiag("not enough room in input packet for %s"
                        " (remain=%d, sd->size=%d)"
                        , sd->name, ins->roof - cur, sd->size);

    }
    else
    {
      u_int8_t *roof = cur + sd->size;    /* may be changed by a length field */
      u_int8_t *outp = struct_ptr;
      bool immediate = FALSE;
      field_desc *fp;

      for (fp = sd->fields; ugh == NULL; fp++)
      {
          size_t i = fp->size;

          passert(ins->roof - cur >= (ptrdiff_t)i);
          passert(cur - ins->cur <= (ptrdiff_t)(sd->size - i));
          passert(outp - (cur - ins->cur) == struct_ptr);

#if 0
          DBG(DBG_PARSING, DBG_log("%d %s"
            , (int) (cur - ins->cur), fp->name == NULL? "" : fp->name));
#endif
          switch (fp->field_type)
          {
          case ft_mbz:  /* must be zero */
            for (; i != 0; i--)
            {
                if (*cur++ != 0)
                {
                  ugh = builddiag("byte %d of %s must be zero, but is not"
                      , (int) (cur - ins->cur), sd->name);
                  break;
                }
                *outp++ = '\0';     /* probably redundant */
            }
            break;

          case ft_nat:  /* natural number (may be 0) */
          case ft_len:  /* length of this struct and any following crud */
          case ft_lv:         /* length/value field of attribute */
          case ft_enum: /* value from an enumeration */
          case ft_loose_enum: /* value from an enumeration with only some names known */
          case ft_af_enum:    /* Attribute Format + value from an enumeration */
          case ft_af_loose_enum:    /* Attribute Format + value from an enumeration */
          case ft_set:  /* bits representing set */
          {
            u_int32_t n = 0;

            for (; i != 0; i--)
                n = (n << BITS_PER_BYTE) | *cur++;

            switch (fp->field_type)
            {
            case ft_len:      /* length of this struct and any following crud */
            case ft_lv: /* length/value field of attribute */
            {
                u_int32_t len = fp->field_type == ft_len? n
                  : immediate? sd->size : n + sd->size;

                if (len < sd->size)
                {
                  ugh = builddiag("%s of %s is smaller than minimum"
                      , fp->name, sd->name);
                }
                else if (pbs_left(ins) < len)
                {
                  ugh = builddiag("%s of %s is larger than can fit"
                      , fp->name, sd->name);
                }
                else
                {
                  roof = ins->cur + len;
                }
                break;
            }
            case ft_af_loose_enum:  /* Attribute Format + value from an enumeration */
                if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
                  immediate = TRUE;
                break;

            case ft_af_enum:  /* Attribute Format + value from an enumeration */
                if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
                  immediate = TRUE;
                /* FALL THROUGH */
            case ft_enum:     /* value from an enumeration */
                if (enum_name(fp->desc, n) == NULL)
                {
                  ugh = builddiag("%s of %s has an unknown value: %lu"
                      , fp->name, sd->name, (unsigned long)n);
                }
                /* FALL THROUGH */
            case ft_loose_enum:     /* value from an enumeration with only some names known */
                break;
            case ft_set:      /* bits representing set */
                if (!testset(fp->desc, n))
                {
                  ugh = builddiag("bitset %s of %s has unknown member(s): %s"
                      , fp->name, sd->name, bitnamesof(fp->desc, n));
                }
                break;
            default:
                  break;
            }
            i = fp->size;
            switch (i)
            {
            case 8/BITS_PER_BYTE:
                *(u_int8_t *)outp = n;
                break;
            case 16/BITS_PER_BYTE:
                *(u_int16_t *)outp = n;
                break;
            case 32/BITS_PER_BYTE:
                *(u_int32_t *)outp = n;
                break;
            default:
                bad_case(i);
            }
            outp += i;
            break;
          }

          case ft_raw:  /* bytes to be left in network-order */
            for (; i != 0; i--)
            {
                *outp++ = *cur++;
            }
            break;

          case ft_end:  /* end of field list */
            passert(cur == ins->cur + sd->size);
            if (obj_pbs != NULL)
            {
                init_pbs(obj_pbs, ins->cur, roof - ins->cur, sd->name);
                obj_pbs->container = ins;
                obj_pbs->desc = sd;
                obj_pbs->cur = cur;
            }
            ins->cur = roof;
            DBG(DBG_PARSING
                , DBG_prefix_print_struct(ins, "parse ", struct_ptr, sd, TRUE));
            return TRUE;

          default:
            bad_case(fp->field_type);
          }
      }
    }

    /* some failure got us here: report it */
    openswan_loglog(RC_LOG_SERIOUS, ugh);
    return FALSE;
}

bool
in_raw(void *bytes, size_t len, pb_stream *ins, const char *name)
{
    if (pbs_left(ins) < len)
    {
      openswan_loglog(RC_LOG_SERIOUS
                  , "not enough bytes left to get %s from %s"
                  , name, ins->name);
      return FALSE;
    }
    else
    {
      if (bytes == NULL)
      {
          DBG(DBG_PARSING
            , DBG_log("skipping %u raw bytes of %s (%s)"
                , (unsigned) len, ins->name, name);
              DBG_dump(name, ins->cur, len));
      }
      else
      {
          memcpy(bytes, ins->cur, len);
          DBG(DBG_PARSING
            , DBG_log("parsing %u raw bytes of %s into %s"
                , (unsigned) len, ins->name, name);
              DBG_dump(name, bytes, len));
      }
      ins->cur += len;
      return TRUE;
    }
}

/* "emit" a host struct into a network packet.
 *
 * This code assumes that the network and host structure
 * members have the same alignment and size!  This requires
 * that all padding be explicit.
 *
 * If obj_pbs is non-NULL, its pbs describes a new output stream set up
 * to contain the object.  The cursor will be left at the variable part.
 * This new stream must subsequently be finalized by close_output_pbs().
 *
 * The value of any field of type ft_len is computed, not taken
 * from the input struct.  The length is actually filled in when
 * the object's output stream is finalized.  If obj_pbs is NULL,
 * finalization is done by out_struct before it returns.
 *
 * This routine returns TRUE iff it succeeds.
 */

bool
out_struct(const void *struct_ptr, struct_desc *sd
         , pb_stream *outs, pb_stream *obj_pbs)
{
    err_t ugh = NULL;
    const u_int8_t *inp = struct_ptr;
    u_int8_t *cur = outs->cur;

    DBG(DBG_EMITTING
      , DBG_prefix_print_struct(outs, "emit ", struct_ptr, sd, obj_pbs==NULL));

    if (outs->roof - cur < (ptrdiff_t)sd->size)
    {
      ugh = builddiag("not enough room left in output packet to place %s"
          , sd->name);
    }
    else
    {
      bool immediate = FALSE;
      pb_stream obj;
      field_desc *fp;

      obj.lenfld = NULL;  /* until a length field is discovered */
      obj.lenfld_desc = NULL;

      for (fp = sd->fields; ugh == NULL; fp++)
      {
          size_t i = fp->size;

          passert(outs->roof - cur >= (ptrdiff_t)i);
          passert(cur - outs->cur <= (ptrdiff_t)(sd->size - i));
          passert(inp - (cur - outs->cur) == struct_ptr);

#if 0
          DBG(DBG_EMITTING, DBG_log("%d %s"
            , (int) (cur - outs->cur), fp->name == NULL? "" : fp->name);
#endif
          switch (fp->field_type)
          {
          case ft_mbz:  /* must be zero */
            inp += i;
            for (; i != 0; i--)
                *cur++ = '\0';
            break;
          case ft_nat:  /* natural number (may be 0) */
          case ft_len:  /* length of this struct and any following crud */
          case ft_lv:         /* length/value field of attribute */
          case ft_enum: /* value from an enumeration */
          case ft_loose_enum: /* value from an enumeration with only some names known */
          case ft_af_enum:    /* Attribute Format + value from an enumeration */
          case ft_af_loose_enum: /* Attribute Format + value from an enumeration */
          case ft_set:  /* bits representing set */
          {
            u_int32_t n = 0;

            switch (i)
            {
            case 8/BITS_PER_BYTE:
                n = *(const u_int8_t *)inp;
                break;
            case 16/BITS_PER_BYTE:
                n = *(const u_int16_t *)inp;
                break;
            case 32/BITS_PER_BYTE:
                n = *(const u_int32_t *)inp;
                break;
            default:
                bad_case(i);
            }

            switch (fp->field_type)
            {
            case ft_len:      /* length of this struct and any following crud */
            case ft_lv: /* length/value field of attribute */
                if (immediate)
                  break;      /* not a length */
                /* We can't check the length because it will likely
                 * be filled in after variable part is supplied.
                 * We do record where this is so that it can be
                 * filled in by a subsequent close_output_pbs().
                 */
                passert(obj.lenfld == NULL);    /* only one ft_len allowed */
                obj.lenfld = cur;
                obj.lenfld_desc = fp;
                break;
            case ft_af_loose_enum: /* Attribute Format + value from an enumeration */
                if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
                  immediate = TRUE;
                break;

            case ft_af_enum:  /* Attribute Format + value from an enumeration */
                if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
                  immediate = TRUE;
                /* FALL THROUGH */
            case ft_enum:     /* value from an enumeration */
                if (enum_name(fp->desc, n) == NULL)
                {
                  ugh = builddiag("%s of %s has an unknown value: %lu"
                      , fp->name, sd->name, (unsigned long)n);
                }
                /* FALL THROUGH */
            case ft_loose_enum:     /* value from an enumeration with only some names known */
                break;
            case ft_set:      /* bits representing set */
                if (!testset(fp->desc, n))
                {
                  ugh = builddiag("bitset %s of %s has unknown member(s): %s"
                      , fp->name, sd->name, bitnamesof(fp->desc, n));
                }
                break;
            default:
                break;
            }

            while (i-- != 0)
            {
                cur[i] = (u_int8_t)n;
                n >>= BITS_PER_BYTE;
            }
            inp += fp->size;
            cur += fp->size;
            break;
          }
          case ft_raw:  /* bytes to be left in network-order */
            for (; i != 0; i--)
                *cur++ = *inp++;
            break;
          case ft_end:  /* end of field list */
            passert(cur == outs->cur + sd->size);

            obj.container = outs;
            obj.desc = sd;
            obj.name = sd->name;
            obj.start = outs->cur;
            obj.cur = cur;
            obj.roof = outs->roof;  /* limit of possible */
            /* obj.lenfld and obj.lenfld_desc already set */

            if (obj_pbs == NULL)
            {
                close_output_pbs(&obj); /* fill in length field, if any */
            }
            else
            {
                /* We set outs->cur to outs->roof so that
                 * any attempt to output something into outs
                 * before obj is closed will trigger an error.
                 */
                outs->cur = outs->roof;

                *obj_pbs = obj;
            }
            return TRUE;

          default:
            bad_case(fp->field_type);
          }
      }
    }

    /* some failure got us here: report it */
    loglog(RC_LOG_SERIOUS, ugh);    /* ??? serious, but errno not relevant */
    return FALSE;
}

bool
out_modify_previous_np(u_int8_t np, pb_stream *outs)
{
      size_t len = (outs->cur - outs->start), offset;
      if (len < sizeof(struct isakmp_hdr)) {
            return FALSE;
      }
      else if (len == sizeof(struct isakmp_hdr)) {
            struct isakmp_hdr *hdr = (struct isakmp_hdr *)outs->start;
            hdr->isa_np = np;
            return TRUE;
      }
      else {
            struct isakmp_generic *hdr;
            for (offset = sizeof(struct isakmp_hdr); offset < len ;
                  offset += ntohs(hdr->isag_length)) {
                  if ((len - offset) < sizeof(struct isakmp_generic))
                        return FALSE;
                  hdr = (struct isakmp_generic *)(outs->start+offset);
                  if ((len - offset) < ntohs(hdr->isag_length))
                        return FALSE;
                  if ((len - offset) == ntohs(hdr->isag_length)) {
                        hdr->isag_np = np;
                        return TRUE;
                  }
            }
      }
      return FALSE;
}

bool
out_generic(u_int8_t np, struct_desc *sd
, pb_stream *outs, pb_stream *obj_pbs)
{
    struct isakmp_generic gen;

    passert(sd->fields == isakmp_generic_desc.fields);
    gen.isag_np = np;
    return out_struct(&gen, sd, outs, obj_pbs);
}

bool
out_generic_raw(u_int8_t np, struct_desc *sd
, pb_stream *outs, const void *bytes, size_t len, const char *name)
{
    pb_stream pbs;

    if (!out_generic(np, sd, outs, &pbs)
    || !out_raw(bytes, len, &pbs, name))
      return FALSE;
    close_output_pbs(&pbs);
    return TRUE;
}

bool
out_raw(const void *bytes, size_t len, pb_stream *outs, const char *name)
{
    if (pbs_left(outs) < len)
    {
      loglog(RC_LOG_SERIOUS, "not enough room left to place %lu bytes of %s in %s"
          , (unsigned long) len, name, outs->name);
      return FALSE;
    }
    else
    {
      DBG(DBG_EMITTING
          , DBG_log("emitting %u raw bytes of %s into %s"
            , (unsigned) len, name, outs->name);
            DBG_dump(name, bytes, len));
      memcpy(outs->cur, bytes, len);
      outs->cur += len;
      return TRUE;
    }
}

bool
out_zero(size_t len, pb_stream *outs, const char *name)
{
    if (pbs_left(outs) < len)
    {
      loglog(RC_LOG_SERIOUS, "not enough room left to place %s in %s", name, outs->name);
      return FALSE;
    }
    else
    {
      DBG(DBG_EMITTING, DBG_log("emitting %u zero bytes of %s into %s"
          , (unsigned) len, name, outs->name));
      memset(outs->cur, 0x00, len);
      outs->cur += len;
      return TRUE;
    }
}

/* Record current length.
 * Note: currently, this may be repeated any number of times;
 * the last one wins.
 */
void
close_output_pbs(pb_stream *pbs)
{
    if (pbs->lenfld != NULL)
    {
      u_int32_t len = pbs_offset(pbs);
      int i = pbs->lenfld_desc->size;

      if (pbs->lenfld_desc->field_type == ft_lv)
          len -= sizeof(struct isakmp_attribute);
      DBG(DBG_EMITTING, DBG_log("emitting length of %s: %lu"
          , pbs->name, (unsigned long) len));
      while (i-- != 0)
      {
          pbs->lenfld[i] = (u_int8_t)len;
          len >>= BITS_PER_BYTE;
      }
    }
    if (pbs->container != NULL)
      pbs->container->cur = pbs->cur;     /* pass space utilization up */
}

Generated by  Doxygen 1.6.0   Back to index