/* Do not modify this file. Changes will be overwritten.                      */
/* Generated automatically by the ASN.1 to Wireshark dissector compiler       */
/* packet-atn-ulcs.c                                                          */
/* asn2wrs.py -u -L -p atn-ulcs -c ./atn-ulcs.cnf -s ./packet-atn-ulcs-template -D . -O ../.. atn-ulcs.asn */

/* packet-atn-ulcs.c
 * By Mathias Guettler <guettler@web.de>
 * Copyright 2013
 *
 * Routines for ATN upper layer
 * protocol packet disassembly

 * ATN upper layers are embedded within OSI Layer 4 (COTP).
 *
 * ATN upper layers contain:
 * Session Layer (NUL protocol option)
 * Presentation Layer (NUL protocol option)
 * ATN upper Layer/Application (ACSE PDU or PDV-list PDU)

 * ATN applications protocols (i.e. CM or CPDLC) are contained within
 * ACSE user-information or PDV presentation data.

 * details see:
 * http://en.wikipedia.org/wiki/CPDLC
 * http://members.optusnet.com.au/~cjr/introduction.htm

 * standards:
 * http://legacy.icao.int/anb/panels/acp/repository.cfm

 * note:
 * We are dealing with ATN/ULCS aka ICAO Doc 9705 Ed2 here
 * (don't think there is an ULCS equivalent for "FANS-1/A ").

 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/*
 developer comments:
why not using existing ses, pres and acse dissectors ?
    ATN upper layers are derived from OSI standards for session,
    presentation and application but the encoding differs
    (it's PER instead of BER encoding to save bandwith).
    Session and presentation use the "null" encoding option,
    meaning that they are only present at connection establishment
    and ommitted otherwise.
    Instead of adapting existing dissectors it seemed simpler and cleaner
    to implement everything the new atn-ulcs dissector.

why using conversations ?
    PER encoded user data is ambigous; the same encoding may apply to a CM or
    CPDLC PDU. The workaround is to decode on a transport connection basis.
    I use my own version of conversations to identify
    the transport connection the PDU belongs to for the standard functions
    from "conversation.h" didn't work out.

what is the use of AARQ/AARE data ?
    Converstions should be maintained on the COTP layer in a standard way
    for there are usually more packets available than in the layers above.
    In the worst case my dissector is called from a DT packet which
    has destination references but no source reference.
    I have to guess the reference used the other way round
    (curently I am using ACSE PDU'S used during OSI connection establishment for that).
    The idea is that each ACSE AARQ is answered by ACSE AARE and having this sequence
    I have all the source/destination references for this transport connection.
    I use AARQ/AARE data to store the source/destination reference of AARQ as well
    as the optional ae-qualifier which tells me the application and
    the dissector I have to use.
    This approach donesn't work well when there are interleaving AARQ/AARE sequences for
    the same aircraft.

which ATN standard is supported ?
    The dissector has been tested with ICAO doc9705 Edition2 compliant traffic.
    No ATN Secutity is supported.
    note:
    The ATN upper layers are derived from OSI standards (ICAO DOC 9705)
    while ATN/IPS (ICAO DOC 9896) which is entirely based on IPV6.

*/

/*
 known defects/deficiencies:

- user-information within AARE is sometines not decoded due to an unset flag
    (the field is optional). As far as I can tell asn2wrs is right here,
    but on the other hand I know that in all of this cases user-information
    is present and is processed by the ATN end system.
    Maybe a true ATN expert may help me out here.

  - The conversation handling is based on src/dst addresses as well as
    source or destination references depending on the TP4 packet type.
    This means that after some time these references get reused for
    new conversations. This almost certain happens for traces longer
    than one day rendering this dissector unsuitable for captures exceeding
    this one day.

*/

#include "config.h"

#ifndef _MSC_VER
#include <stdint.h>
#endif


#include <epan/packet.h>
#include <epan/address.h>
#include <epan/conversation.h>
#include <epan/osi-utils.h>
#include "packet-ber.h"
#include "packet-per.h"
#include "packet-atn-ulcs.h"

#define ATN_ACSE_PROTO "ICAO Doc9705 ULCS ACSE (ISO 8649/8650-1:1996)"
#define ATN_ULCS_PROTO "ICAO Doc9705 ULCS"

void proto_register_atn_ulcs(void);
void proto_reg_handoff_atn_ulcs(void);

static heur_dissector_list_t atn_ulcs_heur_subdissector_list;

/* presentation subdissectors i.e. CM, CPDLC */
static dissector_handle_t atn_cm_handle = NULL;
static dissector_handle_t atn_cpdlc_handle = NULL;

static int proto_atn_ulcs          = -1;
static guint32 ulcs_context_value = 0;
static const char *object_identifier_id;

static wmem_tree_t *aarq_data_tree = NULL;
static wmem_tree_t *atn_conversation_tree = NULL;


static proto_tree *root_tree = NULL;

/* forward declarations for functions generated from asn1 */
static int dissect_atn_ulcs_T_externalt_encoding_single_asn1_type(
    tvbuff_t *tvb _U_,
    int offset _U_,
    asn1_ctx_t *actx _U_,
    proto_tree *tree _U_,
    int hf_index
    _U_);

static int dissect_atn_ulcs_T_externalt_encoding_octet_aligned(
    tvbuff_t *tvb _U_,
    int offset _U_,
    asn1_ctx_t *actx _U_,
    proto_tree *tree _U_,
    int hf_index _U_);

static int dissect_atn_ulcs_T_externalt_encoding_arbitrary(
    tvbuff_t *tvb _U_,
    int offset _U_,
    asn1_ctx_t *actx _U_,
    proto_tree *tree _U_,
    int hf_index _U_);

static int dissect_ACSE_apdu_PDU(
    tvbuff_t *tvb _U_,
    packet_info *pinfo _U_,
    proto_tree *tree _U_,
    void *data _U_);

guint32 dissect_per_object_descriptor_t(
    tvbuff_t *tvb,
    guint32 offset,
    asn1_ctx_t *actx,
    proto_tree *tree,
    int hf_index,
    tvbuff_t **value_tvb);

static gint dissect_atn_ulcs(
    tvbuff_t *tvb,
    packet_info *pinfo,
    proto_tree  *tree,
    void *data _U_);

static int hf_atn_ulcs_Fully_encoded_data_PDU = -1;  /* Fully_encoded_data */
static int hf_atn_ulcs_ACSE_apdu_PDU = -1;        /* ACSE_apdu */
static int hf_atn_ulcs_Fully_encoded_data_item = -1;  /* PDV_list */
static int hf_atn_ulcs_transfer_syntax_name = -1;  /* Transfer_syntax_name */
static int hf_atn_ulcs_presentation_context_identifier = -1;  /* Presentation_context_identifier */
static int hf_atn_ulcs_presentation_data_values = -1;  /* T_presentation_data_values */
static int hf_atn_ulcs_pdv_list_presentation_data_values_single_asn1_type = -1;  /* T_pdv_list_presentation_data_values_single_asn1_type */
static int hf_atn_ulcs_octet_aligned = -1;        /* OCTET_STRING */
static int hf_atn_ulcs_pdv_list_presentation_data_values_arbitrary = -1;  /* T_pdv_list_presentation_data_values_arbitrary */
static int hf_atn_ulcs_direct_reference = -1;     /* OBJECT_IDENTIFIER */
static int hf_atn_ulcs_indirect_reference = -1;   /* INTEGER */
static int hf_atn_ulcs_data_value_descriptor = -1;  /* T_data_value_descriptor */
static int hf_atn_ulcs_encoding = -1;             /* T_encoding */
static int hf_atn_ulcs_externalt_encoding_single_asn1_type = -1;  /* T_externalt_encoding_single_asn1_type */
static int hf_atn_ulcs_externalt_encoding_octet_aligned = -1;  /* T_externalt_encoding_octet_aligned */
static int hf_atn_ulcs_externalt_encoding_arbitrary = -1;  /* T_externalt_encoding_arbitrary */
static int hf_atn_ulcs_aarq = -1;                 /* AARQ_apdu */
static int hf_atn_ulcs_aare = -1;                 /* AARE_apdu */
static int hf_atn_ulcs_rlrq = -1;                 /* RLRQ_apdu */
static int hf_atn_ulcs_rlre = -1;                 /* RLRE_apdu */
static int hf_atn_ulcs_abrt = -1;                 /* ABRT_apdu */
static int hf_atn_ulcs_aarq_apdu_protocol_version = -1;  /* T_aarq_apdu_protocol_version */
static int hf_atn_ulcs_application_context_name = -1;  /* Application_context_name */
static int hf_atn_ulcs_called_AP_title = -1;      /* AP_title */
static int hf_atn_ulcs_called_AE_qualifier = -1;  /* AE_qualifier */
static int hf_atn_ulcs_called_AP_invocation_identifier = -1;  /* AP_invocation_identifier */
static int hf_atn_ulcs_called_AE_invocation_identifier = -1;  /* AE_invocation_identifier */
static int hf_atn_ulcs_calling_AP_title = -1;     /* AP_title */
static int hf_atn_ulcs_calling_AE_qualifier = -1;  /* AE_qualifier */
static int hf_atn_ulcs_calling_AP_invocation_identifier = -1;  /* AP_invocation_identifier */
static int hf_atn_ulcs_calling_AE_invocation_identifier = -1;  /* AE_invocation_identifier */
static int hf_atn_ulcs_sender_acse_requirements = -1;  /* ACSE_requirements */
static int hf_atn_ulcs_mechanism_name = -1;       /* Mechanism_name */
static int hf_atn_ulcs_calling_authentication_value = -1;  /* Authentication_value */
static int hf_atn_ulcs_application_context_name_list = -1;  /* Application_context_name_list */
static int hf_atn_ulcs_implementation_information = -1;  /* Implementation_data */
static int hf_atn_ulcs_user_information = -1;     /* Association_information */
static int hf_atn_ulcs_aare_apdu_protocol_version = -1;  /* T_aare_apdu_protocol_version */
static int hf_atn_ulcs_result = -1;               /* Associate_result */
static int hf_atn_ulcs_result_source_diagnostic = -1;  /* Associate_source_diagnostic */
static int hf_atn_ulcs_responding_AP_title = -1;  /* AP_title */
static int hf_atn_ulcs_responding_AE_qualifier = -1;  /* AE_qualifier */
static int hf_atn_ulcs_responding_AP_invocation_identifier = -1;  /* AP_invocation_identifier */
static int hf_atn_ulcs_responding_AE_invocation_identifier = -1;  /* AE_invocation_identifier */
static int hf_atn_ulcs_responder_acse_requirements = -1;  /* ACSE_requirements */
static int hf_atn_ulcs_responding_authentication_value = -1;  /* Authentication_value */
static int hf_atn_ulcs_rlrq_apdu_request_reason = -1;  /* Release_request_reason */
static int hf_atn_ulcs_rlre_apdu_response_reason = -1;  /* Release_response_reason */
static int hf_atn_ulcs_abort_source = -1;         /* ABRT_source */
static int hf_atn_ulcs_abort_diagnostic = -1;     /* ABRT_diagnostic */
static int hf_atn_ulcs_Application_context_name_list_item = -1;  /* Application_context_name */
static int hf_atn_ulcs_ap_title_form2 = -1;       /* AP_title_form2 */
static int hf_atn_ulcs_ap_title_form1 = -1;       /* AP_title_form1 */
static int hf_atn_ulcs_ae_qualifier_form2 = -1;   /* AE_qualifier_form2 */
static int hf_atn_ulcs_ae_qualifier_form1 = -1;   /* AE_qualifier_form1 */
static int hf_atn_ulcs_acse_service_user = -1;    /* T_acse_service_user */
static int hf_atn_ulcs_acse_service_provider = -1;  /* T_acse_service_provider */
static int hf_atn_ulcs_Association_information_item = -1;  /* EXTERNALt */
static int hf_atn_ulcs_charstring = -1;           /* OCTET_STRING */
static int hf_atn_ulcs_bitstring = -1;            /* BIT_STRING */
static int hf_atn_ulcs_external = -1;             /* EXTERNAL */
static int hf_atn_ulcs_other = -1;                /* T_other */
static int hf_atn_ulcs_other_mechanism_name = -1;  /* OBJECT_IDENTIFIER */
static int hf_atn_ulcs_other_mechanism_value = -1;  /* T_other_mechanism_value */
static int hf_atn_ulcs_rdnSequence = -1;          /* RDNSequence */
static int hf_atn_ulcs_RDNSequence_item = -1;     /* RelativeDistinguishedName */
static int hf_atn_ulcs_RelativeDistinguishedName_item = -1;  /* AttributeTypeAndValue */
static int hf_atn_ulcs_null = -1;                 /* NULL */
/* named bits */
static int hf_atn_ulcs_T_aarq_apdu_protocol_version_version1 = -1;
static int hf_atn_ulcs_T_aare_apdu_protocol_version_version1 = -1;
static int hf_atn_ulcs_ACSE_requirements_authentication = -1;
static int hf_atn_ulcs_ACSE_requirements_application_context_negotiation = -1;

static gint ett_atn_ulcs_Fully_encoded_data = -1;
static gint ett_atn_ulcs_PDV_list = -1;
static gint ett_atn_ulcs_T_presentation_data_values = -1;
static gint ett_atn_ulcs_EXTERNALt = -1;
static gint ett_atn_ulcs_T_encoding = -1;
static gint ett_atn_ulcs_ACSE_apdu = -1;
static gint ett_atn_ulcs_AARQ_apdu = -1;
static gint ett_atn_ulcs_T_aarq_apdu_protocol_version = -1;
static gint ett_atn_ulcs_AARE_apdu = -1;
static gint ett_atn_ulcs_T_aare_apdu_protocol_version = -1;
static gint ett_atn_ulcs_RLRQ_apdu = -1;
static gint ett_atn_ulcs_RLRE_apdu = -1;
static gint ett_atn_ulcs_ABRT_apdu = -1;
static gint ett_atn_ulcs_ACSE_requirements = -1;
static gint ett_atn_ulcs_Application_context_name_list = -1;
static gint ett_atn_ulcs_AP_title = -1;
static gint ett_atn_ulcs_AE_qualifier = -1;
static gint ett_atn_ulcs_Associate_source_diagnostic = -1;
static gint ett_atn_ulcs_Association_information = -1;
static gint ett_atn_ulcs_Authentication_value = -1;
static gint ett_atn_ulcs_T_other = -1;
static gint ett_atn_ulcs_Name = -1;
static gint ett_atn_ulcs_RDNSequence = -1;
static gint ett_atn_ulcs_RelativeDistinguishedName = -1;
static gint ett_atn_ulcs_AttributeTypeAndValue = -1;
static gint ett_atn_ulcs = -1;
static gint ett_atn_acse = -1;



static int
dissect_atn_ulcs_Transfer_syntax_name(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_object_identifier(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}


static const value_string atn_ulcs_Presentation_context_identifier_vals[] = {
  {   1, "acse-apdu" },
  {   2, "reserved" },
  {   3, "user-ase-apdu" },
  { 0, NULL }
};


static int
dissect_atn_ulcs_Presentation_context_identifier(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

    offset = dissect_per_constrained_integer(
        tvb,
        offset,
        actx,
        tree,
        hf_index,
        1U,
        127U,
        &ulcs_context_value,
        TRUE);


  return offset;
}



static int
dissect_atn_ulcs_T_pdv_list_presentation_data_values_single_asn1_type(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_open_type(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}



static int
dissect_atn_ulcs_OCTET_STRING(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_octet_string(tvb, offset, actx, tree, hf_index,
                                       NO_BOUND, NO_BOUND, FALSE, NULL);

  return offset;
}



static int
dissect_atn_ulcs_T_pdv_list_presentation_data_values_arbitrary(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

    packet_info * pinfo = actx->pinfo;
    tvbuff_t *tvb_usr = NULL;
    proto_tree *atn_ulcs_tree = NULL;
    atn_conversation_t *atn_cv = NULL;
    heur_dtbl_entry_t *hdtbl_entry;

    /* extract bitstring into new tvb buffer */
    offset = dissect_per_bit_string(
        tvb,
        offset,
        actx,
        tree,
        hf_index,
        NO_BOUND,
        NO_BOUND,
        FALSE,
        NULL,
        0,
        &tvb_usr,
        NULL);

    if (tvb_usr) {
      /* call appropiate dissector for bitstring data */
      switch(ulcs_context_value){
          case  1: /* ACSE PDU*/
              atn_ulcs_tree = proto_tree_add_subtree(
                  root_tree, tvb, offset, 0,
                  ett_atn_acse, NULL, ATN_ACSE_PROTO );

              dissect_ACSE_apdu_PDU(
                  tvb_new_subset_remaining(tvb_usr, 0),
                  pinfo,
                  atn_ulcs_tree, NULL);
              break;
            case  3: /* USER data; call subdissector for CM, CPDLC ...  */

                /* using dstref for PDV-list only occurrs in DT */
                atn_cv = find_atn_conversation(
                    &pinfo->dst,
                    pinfo->clnp_dstref,
                    &pinfo->src);

                if(atn_cv) {
                    switch(atn_cv->ae_qualifier){
                        case cma: /* contact management */
                            call_dissector_with_data(
                                atn_cm_handle,
                                tvb_new_subset_remaining(tvb_usr, 0),
                                pinfo,
                                root_tree,
                                NULL);
                            break;
                        case cpdlc: /* plain old cpdlc */
                        case pmcpdlc: /* protected mode cpdlc */
                            call_dissector_with_data(
                                atn_cpdlc_handle,
                                tvb_new_subset_remaining(tvb_usr, 0),
                                pinfo,
                                root_tree,
                                NULL);
                            break;
                        default: /* unknown or unhandled datalink application */
                            dissector_try_heuristic(
                                atn_ulcs_heur_subdissector_list,
                                tvb_new_subset_remaining(tvb_usr,0),
                                actx->pinfo,
                                root_tree,
                                &hdtbl_entry,
                                NULL);
                            break;
                    }
                }
                else{
                    dissector_try_heuristic(
                        atn_ulcs_heur_subdissector_list,
                        tvb_new_subset_remaining(tvb_usr,0),
                        actx->pinfo,
                        root_tree,
                        &hdtbl_entry,
                        NULL);
                }
                break;
            default:
                break;
      } /* switch(ulcs_context_value) */
    }


  return offset;
}


static const value_string atn_ulcs_T_presentation_data_values_vals[] = {
  {   0, "single-ASN1-type" },
  {   1, "octet-aligned" },
  {   2, "arbitrary" },
  { 0, NULL }
};

static const per_choice_t T_presentation_data_values_choice[] = {
  {   0, &hf_atn_ulcs_pdv_list_presentation_data_values_single_asn1_type, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_pdv_list_presentation_data_values_single_asn1_type },
  {   1, &hf_atn_ulcs_octet_aligned, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_OCTET_STRING },
  {   2, &hf_atn_ulcs_pdv_list_presentation_data_values_arbitrary, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_pdv_list_presentation_data_values_arbitrary },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_T_presentation_data_values(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_T_presentation_data_values, T_presentation_data_values_choice,
                                 NULL);

  return offset;
}


static const per_sequence_t PDV_list_sequence[] = {
  { &hf_atn_ulcs_transfer_syntax_name, ASN1_NO_EXTENSIONS     , ASN1_OPTIONAL    , dissect_atn_ulcs_Transfer_syntax_name },
  { &hf_atn_ulcs_presentation_context_identifier, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_Presentation_context_identifier },
  { &hf_atn_ulcs_presentation_data_values, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_T_presentation_data_values },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_PDV_list(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_PDV_list, PDV_list_sequence);

  return offset;
}


static const per_sequence_t Fully_encoded_data_sequence_of[1] = {
  { &hf_atn_ulcs_Fully_encoded_data_item, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_PDV_list },
};

static int
dissect_atn_ulcs_Fully_encoded_data(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_constrained_sequence_of(tvb, offset, actx, tree, hf_index,
                                                  ett_atn_ulcs_Fully_encoded_data, Fully_encoded_data_sequence_of,
                                                  1, 1, TRUE);

  return offset;
}



static int
dissect_atn_ulcs_OBJECT_IDENTIFIER(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_object_identifier(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}



static int
dissect_atn_ulcs_INTEGER(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_integer(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}



static int
dissect_atn_ulcs_T_data_value_descriptor(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

  offset = dissect_per_octet_string(
      tvb,
      offset,
      actx,
      tree,
      hf_index,
      -1,
      -1,
      FALSE,
      &actx->external.data_value_descriptor);
  actx->external.data_value_descr_present = TRUE;


  return offset;
}



static int
dissect_atn_ulcs_T_externalt_encoding_single_asn1_type(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

    offset = dissect_per_open_type(tvb, offset, actx, tree, hf_index, NULL);



  return offset;
}



static int
dissect_atn_ulcs_T_externalt_encoding_octet_aligned(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

    offset = dissect_per_octet_string(tvb, offset, actx, tree, hf_index,
                                       NO_BOUND, NO_BOUND, FALSE, NULL);



  return offset;
}



static int
dissect_atn_ulcs_T_externalt_encoding_arbitrary(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  tvbuff_t *tvb_usr = NULL;
  packet_info * pinfo = actx->pinfo;
  atn_conversation_t *atn_cv = NULL;
  heur_dtbl_entry_t *hdtbl_entry;

  /* decode bit-string user data within ACSE  */
  offset = dissect_per_bit_string(
    tvb,
    offset,
    actx,
    tree, hf_index,
    NO_BOUND,
    NO_BOUND,
    FALSE,
    NULL,
    0,
    &tvb_usr,
    NULL);

  if (tvb_usr) {
    /* DT: dstref present, srcref is always zero */
    if((pinfo->clnp_dstref) && (!pinfo->clnp_srcref)){

      atn_cv = find_atn_conversation(
          &pinfo->dst,
          pinfo->clnp_dstref,
          &pinfo->src);
    }
    /* CR: srcref present, dstref always zero */
    if((pinfo->clnp_srcref) && (!pinfo->clnp_dstref)){

      atn_cv = find_atn_conversation(
          &pinfo->src,
          pinfo->clnp_srcref,
          &pinfo->dst);
    }
    /* CC: srcref and dstref present */
    if((pinfo->clnp_srcref) && (pinfo->clnp_dstref)){

      atn_cv = find_atn_conversation(
          &pinfo->src,
          pinfo->clnp_srcref,
          &pinfo->dst);
    }

    if(atn_cv) {
        switch(atn_cv->ae_qualifier){
          case cma: /* contact management */

              call_dissector_with_data(
                    atn_cm_handle,
                    tvb_new_subset_remaining(tvb_usr, 0),
                    pinfo,
                    root_tree,
                    NULL);
              break;
          case cpdlc: /* plain old cpdlc */
          case pmcpdlc: /* protected mode cpdlc */

              call_dissector_with_data(
                    atn_cpdlc_handle,
                    tvb_new_subset_remaining(tvb_usr, 0),
                    pinfo,
                    root_tree,
                    NULL);
              break;
          default: /* unknown or unhandled datalink application */

              dissector_try_heuristic(
                  atn_ulcs_heur_subdissector_list,
                  tvb_new_subset_remaining(tvb_usr,0),
                  actx->pinfo,
                  root_tree,
                  &hdtbl_entry,
                  NULL);
                break;
          }
    }else {

      dissector_try_heuristic(
              atn_ulcs_heur_subdissector_list,
              tvb_new_subset_remaining(tvb_usr,0),
              actx->pinfo,
              root_tree,
              &hdtbl_entry,
              NULL);
    }
  }

  offset += tvb_reported_length_remaining(tvb, offset);


  return offset;
}


static const value_string atn_ulcs_T_encoding_vals[] = {
  {   0, "single-ASN1-type" },
  {   1, "octet-aligned" },
  {   2, "arbitrary" },
  { 0, NULL }
};

static const per_choice_t T_encoding_choice[] = {
  {   0, &hf_atn_ulcs_externalt_encoding_single_asn1_type, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_externalt_encoding_single_asn1_type },
  {   1, &hf_atn_ulcs_externalt_encoding_octet_aligned, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_externalt_encoding_octet_aligned },
  {   2, &hf_atn_ulcs_externalt_encoding_arbitrary, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_externalt_encoding_arbitrary },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_T_encoding(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_T_encoding, T_encoding_choice,
                                 NULL);

  return offset;
}


static const per_sequence_t EXTERNALt_sequence[] = {
  { &hf_atn_ulcs_direct_reference, ASN1_NO_EXTENSIONS     , ASN1_OPTIONAL    , dissect_atn_ulcs_OBJECT_IDENTIFIER },
  { &hf_atn_ulcs_indirect_reference, ASN1_NO_EXTENSIONS     , ASN1_OPTIONAL    , dissect_atn_ulcs_INTEGER },
  { &hf_atn_ulcs_data_value_descriptor, ASN1_NO_EXTENSIONS     , ASN1_OPTIONAL    , dissect_atn_ulcs_T_data_value_descriptor },
  { &hf_atn_ulcs_encoding   , ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_T_encoding },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_EXTERNALt(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_EXTERNALt, EXTERNALt_sequence);

  return offset;
}


static int * const T_aarq_apdu_protocol_version_bits[] = {
  &hf_atn_ulcs_T_aarq_apdu_protocol_version_version1,
  NULL
};

static int
dissect_atn_ulcs_T_aarq_apdu_protocol_version(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_bit_string(tvb, offset, actx, tree, hf_index,
                                     NO_BOUND, NO_BOUND, FALSE, T_aarq_apdu_protocol_version_bits, 1, NULL, NULL);

  return offset;
}



static int
dissect_atn_ulcs_Application_context_name(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_object_identifier(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}



static int
dissect_atn_ulcs_AP_title_form2(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_object_identifier(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}



static int
dissect_atn_ulcs_NULL(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_null(tvb, offset, actx, tree, hf_index);

  return offset;
}


static const per_sequence_t AttributeTypeAndValue_sequence[] = {
  { &hf_atn_ulcs_null       , ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_NULL },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_AttributeTypeAndValue(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_AttributeTypeAndValue, AttributeTypeAndValue_sequence);

  return offset;
}


static const per_sequence_t RelativeDistinguishedName_set_of[1] = {
  { &hf_atn_ulcs_RelativeDistinguishedName_item, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_AttributeTypeAndValue },
};

static int
dissect_atn_ulcs_RelativeDistinguishedName(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

  /*
   * Dissect the first null item only, similar to RDNSequence.
   */
  offset = dissect_per_constrained_set_of(tvb, offset, actx, tree, hf_index,
                                             ett_atn_ulcs_RelativeDistinguishedName, RelativeDistinguishedName_set_of,
                                             1, 1, FALSE);


  return offset;
}


static const per_sequence_t RDNSequence_sequence_of[1] = {
  { &hf_atn_ulcs_RDNSequence_item, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_RelativeDistinguishedName },
};

static int
dissect_atn_ulcs_RDNSequence(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

  /*
   * atn-ulcs.asn currently defines
   *
   * RDNSequence  ::= SEQUENCE OF RelativeDistinguishedName
   * RelativeDistinguishedName ::= SET SIZE (1 .. MAX) OF AttributeTypeAndValue
   * AttributeTypeAndValue ::= SEQUENCE { null NULL}
   *
   * which makes it easy to spam the dissection tree with null items. Dissect
   * the first item only.
   */
  offset = dissect_per_constrained_sequence_of(tvb, offset, actx, tree, hf_index,
                                      ett_atn_ulcs_RDNSequence, RDNSequence_sequence_of,
                                      1, 1, FALSE);


  return offset;
}


static const value_string atn_ulcs_Name_vals[] = {
  {   0, "rdnSequence" },
  { 0, NULL }
};

static const per_choice_t Name_choice[] = {
  {   0, &hf_atn_ulcs_rdnSequence, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_RDNSequence },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_Name(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_Name, Name_choice,
                                 NULL);

  return offset;
}



static int
dissect_atn_ulcs_AP_title_form1(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_atn_ulcs_Name(tvb, offset, actx, tree, hf_index);

  return offset;
}


static const value_string atn_ulcs_AP_title_vals[] = {
  {   0, "ap-title-form2" },
  {   1, "ap-title-form1" },
  { 0, NULL }
};

static const per_choice_t AP_title_choice[] = {
  {   0, &hf_atn_ulcs_ap_title_form2, ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_AP_title_form2 },
  {   1, &hf_atn_ulcs_ap_title_form1, ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_AP_title_form1 },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_AP_title(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_AP_title, AP_title_choice,
                                 NULL);

  return offset;
}



static int
dissect_atn_ulcs_AE_qualifier_form2(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
    packet_info * pinfo = actx->pinfo;
    atn_conversation_t *atn_cv = NULL;
    guint32 ae_qualifier = 0;

    /* dissect  ae-qualifier */
    offset = dissect_per_integer(
        tvb,
        offset,
        actx,
        tree,
        hf_index,
        &ae_qualifier);


    /*note: */
    /* the field "calling-AE-qualifier" is optional, */
    /* which means that we can exploit it only if it is present. */
    /* We still depend on heuristical decoding of CM, CPDLC PDU's otherwise. */

    /* AARQ/DT: dstref present, srcref is always zero */
    if((pinfo->clnp_dstref) && (!pinfo->clnp_srcref)){
        atn_cv = find_atn_conversation(&pinfo->dst,
            pinfo->clnp_dstref,
            &pinfo->src );
    }

    /* AARQ/CR: srcref present, dstref is always zero */
    if((!pinfo->clnp_dstref) && (pinfo->clnp_srcref)){
        atn_cv = find_atn_conversation(&pinfo->src,
            pinfo->clnp_srcref,
            &pinfo->dst );
  }

  if(atn_cv){
      atn_cv->ae_qualifier = ae_qualifier;
  }

  return offset;
}



static int
dissect_atn_ulcs_AE_qualifier_form1(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_atn_ulcs_RelativeDistinguishedName(tvb, offset, actx, tree, hf_index);

  return offset;
}


static const value_string atn_ulcs_AE_qualifier_vals[] = {
  {   0, "ae-qualifier-form2" },
  {   1, "ae-qualifier-form1" },
  { 0, NULL }
};

static const per_choice_t AE_qualifier_choice[] = {
  {   0, &hf_atn_ulcs_ae_qualifier_form2, ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_AE_qualifier_form2 },
  {   1, &hf_atn_ulcs_ae_qualifier_form1, ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_AE_qualifier_form1 },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_AE_qualifier(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_AE_qualifier, AE_qualifier_choice,
                                 NULL);

  return offset;
}



static int
dissect_atn_ulcs_AP_invocation_identifier(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_integer(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}



static int
dissect_atn_ulcs_AE_invocation_identifier(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_integer(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}


static int * const ACSE_requirements_bits[] = {
  &hf_atn_ulcs_ACSE_requirements_authentication,
  &hf_atn_ulcs_ACSE_requirements_application_context_negotiation,
  NULL
};

static int
dissect_atn_ulcs_ACSE_requirements(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_bit_string(tvb, offset, actx, tree, hf_index,
                                     NO_BOUND, NO_BOUND, FALSE, ACSE_requirements_bits, 2, NULL, NULL);

  return offset;
}



static int
dissect_atn_ulcs_Mechanism_name(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

    offset = dissect_per_object_identifier(
        tvb,
        offset,
        actx,
        tree,
        hf_index,
        NULL);


  offset = dissect_per_object_identifier(
      tvb,
      offset,
      actx,
      tree,
      hf_index,
      NULL);


  return offset;
}



static int
dissect_atn_ulcs_BIT_STRING(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_bit_string(tvb, offset, actx, tree, hf_index,
                                     NO_BOUND, NO_BOUND, FALSE, NULL, 0, NULL, NULL);

  return offset;
}



static int
dissect_atn_ulcs_EXTERNAL(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_external_type(tvb, offset, actx, tree, hf_index, NULL);

  return offset;
}



static int
dissect_atn_ulcs_T_other_mechanism_value(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

    offset=call_ber_oid_callback(
        object_identifier_id,
        tvb,
        offset,
        actx->pinfo,
        tree, NULL);


    offset=call_ber_oid_callback(
        object_identifier_id,
        tvb,
        offset,
        actx->pinfo,
        tree, NULL);


  return offset;
}


static const per_sequence_t T_other_sequence[] = {
  { &hf_atn_ulcs_other_mechanism_name, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_OBJECT_IDENTIFIER },
  { &hf_atn_ulcs_other_mechanism_value, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_T_other_mechanism_value },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_T_other(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_T_other, T_other_sequence);

  return offset;
}


static const value_string atn_ulcs_Authentication_value_vals[] = {
  {   0, "charstring" },
  {   1, "bitstring" },
  {   2, "external" },
  {   3, "other" },
  { 0, NULL }
};

static const per_choice_t Authentication_value_choice[] = {
  {   0, &hf_atn_ulcs_charstring , ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_OCTET_STRING },
  {   1, &hf_atn_ulcs_bitstring  , ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_BIT_STRING },
  {   2, &hf_atn_ulcs_external   , ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_EXTERNAL },
  {   3, &hf_atn_ulcs_other      , ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_other },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_Authentication_value(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_Authentication_value, Authentication_value_choice,
                                 NULL);

  return offset;
}


static const per_sequence_t Application_context_name_list_sequence_of[1] = {
  { &hf_atn_ulcs_Application_context_name_list_item, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_Application_context_name },
};

static int
dissect_atn_ulcs_Application_context_name_list(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence_of(tvb, offset, actx, tree, hf_index,
                                      ett_atn_ulcs_Application_context_name_list, Application_context_name_list_sequence_of);

  return offset;
}



static int
dissect_atn_ulcs_Implementation_data(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_octet_string(tvb, offset, actx, tree, hf_index,
                                       NO_BOUND, NO_BOUND, FALSE, NULL);

  return offset;
}


static const per_sequence_t Association_information_sequence_of[1] = {
  { &hf_atn_ulcs_Association_information_item, ASN1_NO_EXTENSIONS     , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_EXTERNALt },
};

static int
dissect_atn_ulcs_Association_information(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_constrained_sequence_of(tvb, offset, actx, tree, hf_index,
                                                  ett_atn_ulcs_Association_information, Association_information_sequence_of,
                                                  1, 1, TRUE);

  return offset;
}


static const per_sequence_t AARQ_apdu_sequence[] = {
  { &hf_atn_ulcs_aarq_apdu_protocol_version, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_T_aarq_apdu_protocol_version },
  { &hf_atn_ulcs_application_context_name, ASN1_EXTENSION_ROOT    , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_Application_context_name },
  { &hf_atn_ulcs_called_AP_title, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AP_title },
  { &hf_atn_ulcs_called_AE_qualifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AE_qualifier },
  { &hf_atn_ulcs_called_AP_invocation_identifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AP_invocation_identifier },
  { &hf_atn_ulcs_called_AE_invocation_identifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AE_invocation_identifier },
  { &hf_atn_ulcs_calling_AP_title, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AP_title },
  { &hf_atn_ulcs_calling_AE_qualifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AE_qualifier },
  { &hf_atn_ulcs_calling_AP_invocation_identifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AP_invocation_identifier },
  { &hf_atn_ulcs_calling_AE_invocation_identifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AE_invocation_identifier },
  { &hf_atn_ulcs_sender_acse_requirements, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_ACSE_requirements },
  { &hf_atn_ulcs_mechanism_name, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Mechanism_name },
  { &hf_atn_ulcs_calling_authentication_value, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Authentication_value },
  { &hf_atn_ulcs_application_context_name_list, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Application_context_name_list },
  { &hf_atn_ulcs_implementation_information, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Implementation_data },
  { &hf_atn_ulcs_user_information, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Association_information },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_AARQ_apdu(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
    packet_info * pinfo = actx->pinfo;
    aarq_data_t *aarq_data = NULL;
    atn_conversation_t *atn_cv = NULL;
    guint32 aircraft_24_bit_address = 0;

    /* AARQ/DT: dstref present, srcref is always zero */
    if((pinfo->clnp_dstref) && (!pinfo->clnp_srcref)){

        atn_cv = find_atn_conversation(
            &pinfo->dst,
            pinfo->clnp_dstref,
            &pinfo->src );
        if(!atn_cv){
            atn_cv = wmem_new(wmem_file_scope(), atn_conversation_t);
            atn_cv->ae_qualifier = unknown;
            create_atn_conversation(&pinfo->dst,
                pinfo->clnp_dstref,
                &pinfo->src ,
                atn_cv);
        }
    }

  /* AARQ/CR: srcref present, dstref is always zero */
    if((!pinfo->clnp_dstref) && (pinfo->clnp_srcref)){
        atn_cv = find_atn_conversation(&pinfo->src,
            pinfo->clnp_srcref,
            &pinfo->dst );
        if(!atn_cv){
            atn_cv = wmem_new(wmem_file_scope(), atn_conversation_t);
            atn_cv->ae_qualifier = unknown;
            create_atn_conversation(&pinfo->src,
                pinfo->clnp_srcref,
                &pinfo->dst ,
                atn_cv);
        }
    }

    /* conversation is to be created prior to decoding */
    /* of "AE-qualifier-form2" which takes place here: */
      offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_AARQ_apdu, AARQ_apdu_sequence);



    /* save AARQ packet data to create a conversation  */
    /* when decoding the following AARE PDU */
    /* ATN applications CM and CPDLC are air/ground applications */
    /* so there is always an aircraft (with its 24-bit address) */
    /* and a ground facility. */
    /* the assumption is that there is only one open AARQ/AARE */
    /* dialog per aircraft at a time. */
    /* the aircraft's 24-bit address is used as a key to each AARQ */
    /* data. AARQ data is used to create a conversation with */
    /* air and ground endpoints (based on NSAP's and transport references) */
    /* when decoding AARE.*/
    /* note: */
    /* it may be more robust to create the conversation */
    /* in the "ositp" dissector an to merely use the conversation here */
    aircraft_24_bit_address =
        get_aircraft_24_bit_address_from_nsap(pinfo);

    /* search for aarq entry */
    aarq_data = (aarq_data_t *) wmem_tree_lookup32(
        aarq_data_tree,
        aircraft_24_bit_address);

    if(!aarq_data){  /* aarq data not found, create new record */

        /* alloc aarq data */
        aarq_data = wmem_new(wmem_file_scope(), aarq_data_t);
        aarq_data-> aarq_pending = FALSE;

        /* insert aarq data */
        wmem_tree_insert32(aarq_data_tree ,aircraft_24_bit_address,(void*)aarq_data);
    }

    /* check for pending AARQ/AARE sequences */
    /* if "aarq_data-> aarq_pending" is set this means that there is already one  */
    /* AARQ/AARE sequence pending (is unwise to overwrite AARE/AARQ) */
    if (aarq_data-> aarq_pending == FALSE ) {

      /* init aarq data */
      memset(aarq_data,0,sizeof(aarq_data_t));

      aarq_data->cv = atn_cv;
      aarq_data-> aarq_pending = TRUE;
    }


  return offset;
}


static int * const T_aare_apdu_protocol_version_bits[] = {
  &hf_atn_ulcs_T_aare_apdu_protocol_version_version1,
  NULL
};

static int
dissect_atn_ulcs_T_aare_apdu_protocol_version(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_bit_string(tvb, offset, actx, tree, hf_index,
                                     NO_BOUND, NO_BOUND, FALSE, T_aare_apdu_protocol_version_bits, 1, NULL, NULL);

  return offset;
}


static const value_string atn_ulcs_Associate_result_vals[] = {
  {   0, "accepted" },
  {   1, "rejected-permanent" },
  {   2, "rejected-transient" },
  { 0, NULL }
};


static int
dissect_atn_ulcs_Associate_result(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
 /* extension present: last param set to true. asn2wrs didn't take notice of that */
 offset = dissect_per_constrained_integer(tvb, offset, actx, tree, hf_index,
                                                            0U, 2U, NULL, TRUE);

  return offset;
}


static const value_string atn_ulcs_T_acse_service_user_vals[] = {
  {   0, "null" },
  {   1, "no-reason-given" },
  {   2, "application-context-name-not-supported" },
  {   3, "calling-AP-title-not-recognized" },
  {   4, "calling-AP-invocation-identifier-not-recognized" },
  {   5, "calling-AE-qualifier-not-recognized" },
  {   6, "calling-AE-invocation-identifier-not-recognized" },
  {   7, "called-AP-title-not-recognized" },
  {   8, "called-AP-invocation-identifier-not-recognized" },
  {   9, "called-AE-qualifier-not-recognized" },
  {  10, "called-AE-invocation-identifier-not-recognized" },
  {  11, "authentication-mechanism-name-not-recognized" },
  {  12, "authentication-mechanism-name-required" },
  {  13, "authentication-failure" },
  {  14, "authentication-required" },
  { 0, NULL }
};


static int
dissect_atn_ulcs_T_acse_service_user(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_constrained_integer(tvb, offset, actx, tree, hf_index,
                                                            0U, 14U, NULL, TRUE);

  return offset;
}


static const value_string atn_ulcs_T_acse_service_provider_vals[] = {
  {   0, "null" },
  {   1, "no-reason-given" },
  {   2, "no-common-acse-version" },
  { 0, NULL }
};


static int
dissect_atn_ulcs_T_acse_service_provider(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_constrained_integer(tvb, offset, actx, tree, hf_index,
                                                            0U, 2U, NULL, TRUE);

  return offset;
}


static const value_string atn_ulcs_Associate_source_diagnostic_vals[] = {
  {   1, "acse-service-user" },
  {   2, "acse-service-provider" },
  { 0, NULL }
};

static const per_choice_t Associate_source_diagnostic_choice[] = {
  {   1, &hf_atn_ulcs_acse_service_user, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_acse_service_user },
  {   2, &hf_atn_ulcs_acse_service_provider, ASN1_NO_EXTENSIONS     , dissect_atn_ulcs_T_acse_service_provider },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_Associate_source_diagnostic(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_Associate_source_diagnostic, Associate_source_diagnostic_choice,
                                 NULL);

  return offset;
}


static const per_sequence_t AARE_apdu_sequence[] = {
  { &hf_atn_ulcs_aare_apdu_protocol_version, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_T_aare_apdu_protocol_version },
  { &hf_atn_ulcs_application_context_name, ASN1_EXTENSION_ROOT    , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_Application_context_name },
  { &hf_atn_ulcs_result     , ASN1_EXTENSION_ROOT    , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_Associate_result },
  { &hf_atn_ulcs_result_source_diagnostic, ASN1_EXTENSION_ROOT    , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_Associate_source_diagnostic },
  { &hf_atn_ulcs_responding_AP_title, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AP_title },
  { &hf_atn_ulcs_responding_AE_qualifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AE_qualifier },
  { &hf_atn_ulcs_responding_AP_invocation_identifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AP_invocation_identifier },
  { &hf_atn_ulcs_responding_AE_invocation_identifier, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_AE_invocation_identifier },
  { &hf_atn_ulcs_responder_acse_requirements, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_ACSE_requirements },
  { &hf_atn_ulcs_mechanism_name, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Mechanism_name },
  { &hf_atn_ulcs_responding_authentication_value, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Authentication_value },
  { &hf_atn_ulcs_application_context_name_list, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Application_context_name_list },
  { &hf_atn_ulcs_implementation_information, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Implementation_data },
  { &hf_atn_ulcs_user_information, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Association_information },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_AARE_apdu(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  packet_info * pinfo = actx->pinfo;
  guint32 aircraft_24_bit_address = 0 ;
  atn_conversation_t *atn_cv = NULL;
  aarq_data_t *aarq_data = NULL;

  /* get AARQ data and use it to create a new conversation, */
  /* the conversation is used along with  */
  /* AARQ's "calling ae qualifier" to determine the */
  /* type of air/ground application of each subsequent frame.*/
  /* we use this information to invoke the correct application dissector. */
  /* note: */
  /* heuristical decoding of ASN1 will not work for all cases, */
  /* for there may be CM PDU's which will exactly look like CPDLC PDU'S */

  /* get 24-bit icao address */
  aircraft_24_bit_address = get_aircraft_24_bit_address_from_nsap(pinfo);

  /* search for aarq entry */
  aarq_data = (aarq_data_t *) wmem_tree_lookup32(
      aarq_data_tree,
      aircraft_24_bit_address);

  /* no aarq data present, do nothing  */
  /* without both ends of the conversation and without */
  /* the "calling ae-qualifier there is no point in setting up "*/
  /* a conversation */
  if(!aarq_data) {

    return offset;
  }

  /* AARE/DT: dstref present, srcref is always zero */
  if((pinfo->clnp_dstref) && (!pinfo->clnp_srcref)){

    atn_cv = find_atn_conversation(&pinfo->dst,
                          pinfo->clnp_dstref,
                          &pinfo->src );

    if(!atn_cv){ /* conversation not fond */

      /* DT has only dstref - create new conversation */
      create_atn_conversation(&pinfo->dst,
                              pinfo->clnp_dstref,
                              &pinfo->src ,
                              aarq_data->cv);
    }
  }

  /* AARE/CC: srcref and dstref present  */
  if((pinfo->clnp_dstref) && (pinfo->clnp_srcref)){

    atn_cv = find_atn_conversation(
        &pinfo->src,
        pinfo->clnp_srcref,
        &pinfo->dst);

    if(atn_cv){ /* conversation found. */

      /* create new conversation for dstref */
      create_atn_conversation(&pinfo->dst,
                              pinfo->clnp_dstref,
                              &pinfo->src ,
                              aarq_data->cv);

    }else { /* no conversation found  */
      /* as CC contains srcref *and* dstref we use both to create new records  */
      create_atn_conversation(&pinfo->src,
                              pinfo->clnp_srcref,
                              &pinfo->dst ,
                              aarq_data->cv);
      create_atn_conversation(&pinfo->dst,
                              pinfo->clnp_dstref,
                              &pinfo->src ,
                              aarq_data->cv);
    }
  }

  /* clear aarq data */
  memset(aarq_data,0,sizeof(aarq_data_t));
  aarq_data-> aarq_pending  =  FALSE;

    offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_AARE_apdu, AARE_apdu_sequence);




  return offset;
}


static const value_string atn_ulcs_Release_request_reason_vals[] = {
  {   0, "normal" },
  {   1, "urgent" },
  {  30, "user-defined" },
  { 0, NULL }
};


static int
dissect_atn_ulcs_Release_request_reason(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
 /* extension present: last param set to true. asn2wrs didn't take notice of that */
 offset = dissect_per_constrained_integer(tvb, offset, actx, tree, hf_index,
                                                            0U, 30U, NULL, TRUE);

  return offset;
}


static const per_sequence_t RLRQ_apdu_sequence[] = {
  { &hf_atn_ulcs_rlrq_apdu_request_reason, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Release_request_reason },
  { &hf_atn_ulcs_user_information, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Association_information },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_RLRQ_apdu(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_RLRQ_apdu, RLRQ_apdu_sequence);

  return offset;
}


static const value_string atn_ulcs_Release_response_reason_vals[] = {
  {   0, "normal" },
  {   1, "not-finished" },
  {  30, "user-defined" },
  { 0, NULL }
};


static int
dissect_atn_ulcs_Release_response_reason(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {

 /* extension present: last param set to true. asn2wrs didn't take notice of that */
 offset = dissect_per_constrained_integer(
    tvb,
    offset,
    actx,
    tree,
    hf_index,
    0U,
    30U,
    NULL,
    TRUE);


  return offset;
}


static const per_sequence_t RLRE_apdu_sequence[] = {
  { &hf_atn_ulcs_rlre_apdu_response_reason, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Release_response_reason },
  { &hf_atn_ulcs_user_information, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Association_information },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_RLRE_apdu(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_RLRE_apdu, RLRE_apdu_sequence);

  return offset;
}


static const value_string atn_ulcs_ABRT_source_vals[] = {
  {   0, "acse-service-user" },
  {   1, "acse-service-provider" },
  { 0, NULL }
};


static int
dissect_atn_ulcs_ABRT_source(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_constrained_integer(tvb, offset, actx, tree, hf_index,
                                                            0U, 1U, NULL, TRUE);

  return offset;
}


static const value_string atn_ulcs_ABRT_diagnostic_vals[] = {
  {   1, "no-reason-given" },
  {   2, "protocol-error" },
  {   3, "authentication-mechanism-name-not-recognized" },
  {   4, "authentication-mechanism-name-required" },
  {   5, "authentication-failure" },
  {   6, "authentication-required" },
  { 0, NULL }
};

static uint32_t ABRT_diagnostic_value_map[6+0] = {1, 2, 3, 4, 5, 6};

static int
dissect_atn_ulcs_ABRT_diagnostic(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index,
                                     6, NULL, TRUE, 0, ABRT_diagnostic_value_map);

  return offset;
}


static const per_sequence_t ABRT_apdu_sequence[] = {
  { &hf_atn_ulcs_abort_source, ASN1_EXTENSION_ROOT    , ASN1_NOT_OPTIONAL, dissect_atn_ulcs_ABRT_source },
  { &hf_atn_ulcs_abort_diagnostic, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_ABRT_diagnostic },
  { &hf_atn_ulcs_user_information, ASN1_EXTENSION_ROOT    , ASN1_OPTIONAL    , dissect_atn_ulcs_Association_information },
  { NULL, 0, 0, NULL }
};

static int
dissect_atn_ulcs_ABRT_apdu(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index,
                                   ett_atn_ulcs_ABRT_apdu, ABRT_apdu_sequence);

  return offset;
}


static const value_string atn_ulcs_ACSE_apdu_vals[] = {
  {   0, "aarq" },
  {   1, "aare" },
  {   2, "rlrq" },
  {   3, "rlre" },
  {   4, "abrt" },
  { 0, NULL }
};

static const per_choice_t ACSE_apdu_choice[] = {
  {   0, &hf_atn_ulcs_aarq       , ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_AARQ_apdu },
  {   1, &hf_atn_ulcs_aare       , ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_AARE_apdu },
  {   2, &hf_atn_ulcs_rlrq       , ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_RLRQ_apdu },
  {   3, &hf_atn_ulcs_rlre       , ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_RLRE_apdu },
  {   4, &hf_atn_ulcs_abrt       , ASN1_EXTENSION_ROOT    , dissect_atn_ulcs_ABRT_apdu },
  { 0, NULL, 0, NULL }
};

static int
dissect_atn_ulcs_ACSE_apdu(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
  offset = dissect_per_choice(tvb, offset, actx, tree, hf_index,
                                 ett_atn_ulcs_ACSE_apdu, ACSE_apdu_choice,
                                 NULL);

  return offset;
}

/*--- PDUs ---*/

static int dissect_Fully_encoded_data_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_) {
  int offset = 0;
  asn1_ctx_t asn1_ctx;
  asn1_ctx_init(&asn1_ctx, ASN1_ENC_PER, FALSE, pinfo);
  offset = dissect_atn_ulcs_Fully_encoded_data(tvb, offset, &asn1_ctx, tree, hf_atn_ulcs_Fully_encoded_data_PDU);
  offset += 7; offset >>= 3;
  return offset;
}
static int dissect_ACSE_apdu_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_) {
  int offset = 0;
  asn1_ctx_t asn1_ctx;
  asn1_ctx_init(&asn1_ctx, ASN1_ENC_PER, FALSE, pinfo);
  offset = dissect_atn_ulcs_ACSE_apdu(tvb, offset, &asn1_ctx, tree, hf_atn_ulcs_ACSE_apdu_PDU);
  offset += 7; offset >>= 3;
  return offset;
}


#if 0
/* re-implementing external data: packet-per.c */
static const value_string per_External_encoding_vals[] = {
{   0, "single-ASN1-type" },
{   1, "octet-aligned" },
{   2, "arbitrary" },
{ 0, NULL }
};

/* re-implementing external data: packet-per.c */
static const per_choice_t External_encoding_choice[] =
{
    {   0,
        &hf_atn_ulcs_externalt_encoding_single_asn1_type,
        ASN1_NO_EXTENSIONS,
        dissect_atn_ulcs_T_externalt_encoding_single_asn1_type
    },
    {   1,
        &hf_atn_ulcs_externalt_encoding_octet_aligned,
        ASN1_NO_EXTENSIONS,
        dissect_atn_ulcs_T_externalt_encoding_octet_aligned
    },
    {   2,
        &hf_atn_ulcs_externalt_encoding_arbitrary,
        ASN1_NO_EXTENSIONS,
        dissect_atn_ulcs_T_externalt_encoding_arbitrary
    },
    {   0,
        NULL,
        0,
        NULL
    }
};
#endif

/* ATN Session layer */
#define SES_PDU_TYPE_MASK     0xf8
#define SES_PARAM_IND_MASK    0x04
#define SES_PARAM_B2_MASK     0x02
#define SES_PARAM_B1_MASK     0x01

static int hf_atn_ses_type = -1;
static int hf_atn_ses_param_ind = -1;
static int hf_atn_ses_param_b1 = -1;
static int hf_atn_ses_param_b2 = -1;

static gint ett_atn_ses = -1;

#define ATN_SES_PROTO "ICAO Doc9705 ULCS Session (ISO 8326/8327-1:1994)"

const value_string atn_ses_param_ind[] =
{
    {0, "No Parameter Indication "},
    {1, "Parameter Indication "},
    {0, NULL }
};

const value_string srf_b2[] =
{
    {0, "Transport Connection is kept"},
    {1, "Transport Connection is released" },
    {0, NULL }
};

const value_string srf_b1[] =
{
    {0, "Transport Connection is transient"},
    {1, "Transport Connection is persistent"},
    {0, NULL }
};

#define SES_ATN_SCN       0xe8
#define SES_ATN_SCNC      0xf8
#define SES_ATN_SAC       0xf0
#define SES_ATN_SACC      0xd8
#define SES_ATN_SRF       0xe0
#define SES_ATN_SRFC      0xa0

const value_string atn_ses_type[] =
{
    { 0x1d, "Short Connect (SCN) SPDU" },
    { 0x1f, "Short Connect Accept (SAC) SPDU" },
    { 0x1e, "Short Connect Accept Continue (SACC) SPDU" },
    { 0x1c, "Short Refuse (SRF) SPDU" },
    { 0x14, "Short Refuse Continue (SRFC) SPDU" },
    {0, NULL }
};

/* ATN Presentation layer */
#define ATN_PRES_PROTO "ICAO Doc9705 ULCS Presentation (ISO 8822/8823-1:1994)"

static int hf_atn_pres_err   = -1;
static int hf_atn_pres_pdu_type = -1;
static gint ett_atn_pres    = -1;

#define ATN_SES_PRES_MASK 0xf803
#define PRES_CPR_ER_MASK    0x70

/* type determined by SPDU and PPDU */
const value_string atn_pres_vals[] =
{
    { 0xe802, "Short Presentation Connect PPDU (CP) " },
    { 0xf802, "Short Presentation Connect PPDU (CP) " },
    { 0xf002, "Short Presentation Connect Accept PPDU (CPA)" },
    { 0xd802, "Short Presentation Connect Accept PPDU (CPA)" },
    { 0xe002, "Short Presentation Connect Reject PPDU (CPR)" },
    { 0xa002, "Short Presentation Connect Reject PPDU (CPR)" },
    {0,         NULL }
};

/* Short Presentation Connect Reject PPDU's 0yyy 00zz */
const value_string atn_pres_err[] =
{
    { 0x00, "Presentation-user" },
    { 0x01, "Reason not specified (transient)"},
    { 0x02, "Temporary congestion (transient)"},
    { 0x03, "Local limit exceeded (transient)"},
    { 0x04, "Called presentation-address unknown (permanent)"},
    { 0x05, "Protocol version not supported (permanent)"},
    { 0x06, "Default context not supported (permanent)"},
    { 0x07, "User data not readable (permanent)"},
    { 0,          NULL }
};

#if 0
/* re-implementing external data: packet-per.c */
static int  atn_ulcs_Externalt_encoding(
    tvbuff_t *tvb _U_,
    int offset _U_,
    asn1_ctx_t *actx _U_,
    proto_tree *tree _U_,
    int hf_index _U_)
{
    offset = dissect_per_choice(
        tvb,
        offset,
        actx,
        tree,
        hf_index,
        ett_atn_ulcs_EXTERNALt,
        External_encoding_choice,
        &actx->external.encoding);

    return offset;
}

/* re-implementing external data: packet-per.c */
static guint32  atn_per_external_type(
    tvbuff_t *tvb _U_,
    guint32 offset,
    asn1_ctx_t *actx,
    proto_tree *tree _U_,
    int hf_index _U_,
    per_type_fn type_cb)
{
    memset(&actx->external, '\0', sizeof(actx->external));
    actx->external.hf_index = -1;
    actx->external.encoding = -1;

    actx->external.u.per.type_cb = type_cb;
    offset = atn_ulcs_Externalt_encoding(
        tvb,
        offset,
        actx,
        tree,
        hf_index);

    memset(
        &actx->external,
        '\0',
        sizeof(actx->external));

    actx->external.hf_index = -1;
    actx->external.encoding = -1;

    return offset;
}
#endif

/* determine 24-bit aircraft address(ARS) */
/* from 20-byte ATN NSAP. */
guint32 get_aircraft_24_bit_address_from_nsap(
    packet_info *pinfo)
{
    const guint8* addr = NULL;
    guint32 ars =0;
    guint32 adr_prefix =0;

    /* check NSAP address type*/
    if( (pinfo->src.type != get_osi_address_type()) ||
        (pinfo->dst.type != get_osi_address_type())) {
        return ars; }

    /* 20 octets address length required */
    /* for ATN */
    if( (pinfo->src.len != 20) ||
        (pinfo->dst.len != 20)) {
        return ars; }

    /* first try source address */
    /* if the src address originates */
    /* from an aircraft it's downlink */

    /* convert addr into 32-bit integer */
    addr = (const guint8 *)pinfo->src.data;
    adr_prefix =
        ((addr[0]<<24) |
        (addr[1]<<16) |
        (addr[2]<<8) |
        addr[3] );

    /* according to ICAO doc9507 Ed2 SV5  */
    /* clause 5.4.3.8.1.5 and  5.4.3.8.1.3 */
    /* mobile addresses contain "c1" of "41" */
    /* in the VER subfield of the NSAP */
    if((adr_prefix == 0x470027c1) ||
        (adr_prefix == 0x47002741)) {
      /* ICAO doc9507 Ed2 SV5 5.4.3.8.4.4 */
      /* states that the ARS subfield containes */
      /* the  24-bitaddress of the aircraft */
        ars = ((addr[8])<<16) |
            ((addr[9])<<8) |
            (addr[10]);
    }

    /* try destination address */
    /* if the src address originates */
    /* from an aircraft it's downlink */

    /* convert addr into 32-bit integer */
    addr = (const guint8 *)pinfo->dst.data;
    adr_prefix = ((addr[0]<<24) |
        (addr[1]<<16) |
        (addr[2]<<8) |
        addr[3] );

    /* according to ICAO doc9507 Ed2 SV5  */
    /* clause 5.4.3.8.1.5 and  5.4.3.8.1.3 */
    /* mobile addresses contain "c1" of "41" */
    /* in the VER subfield of the NSAP */
    if((adr_prefix == 0x470027c1) ||
        (adr_prefix == 0x47002741)) {
      /* ICAO doc9507 Ed2 SV5 5.4.3.8.4.4 */
      /* states that the ARS subfield containes */
      /* the  24-bitaddress of the aircraft */
      ars = ((addr[8])<<16) |
            ((addr[9])<<8) |
            (addr[10]);
    }
    return ars;
}

/* determine whether a PDU is uplink or downlink */
/* by checking for known aircraft  address prefices*/
int check_heur_msg_type(packet_info *pinfo  _U_)
{
    int t = no_msg;
    const guint8* addr = NULL;
    guint32 adr_prefix =0;

    /* check NSAP address type*/
    if( (pinfo->src.type != get_osi_address_type()) || (pinfo->dst.type != get_osi_address_type())) {
        return t; }

    /* check NSAP address length; 20 octets address length required */
    if( (pinfo->src.len != 20) || (pinfo->dst.len != 20)) {
        return t; }

    addr = (const guint8 *)pinfo->src.data;

    /* convert address to 32-bit integer  */
    adr_prefix = ((addr[0]<<24) | (addr[1]<<16) | (addr[2]<<8) | addr[3] );

    /* According to the published ATN NSAP adddressing scheme */
    /* in ICAO doc9705 Ed2 SV5 5.4.3.8.1.3 and 5.4.3.8.1.5  */
    /* the "VER" field shall be 0x41 ("all Mobile AINSC") or */
    /* 0xc1 ("all Mobile ATSC") for mobile stations (aka aircraft).*/
    if((adr_prefix == 0x470027c1) || (adr_prefix == 0x47002741)) {
        t = dm; /* source is an aircraft: it's a downlink PDU */
    }

    addr = (const guint8 *)pinfo->dst.data;

    /* convert address to 32-bit integer  */
    adr_prefix = ((addr[0]<<24) | (addr[1]<<16) | (addr[2]<<8) | addr[3] );

    /* According to the published ATN NSAP adddressing scheme */
    /* in ICAO doc9705 Ed2 SV5 5.4.3.8.1.3 and 5.4.3.8.1.5  */
    /* the "VER" field shall be 0x41 ("all Mobile AINSC") or */
    /* 0xc1 ("all Mobile ATSC") for mobile stations (aka aircraft).*/
    if((adr_prefix == 0x470027c1) || (adr_prefix == 0x47002741)) {
        t = um; /* destination is aircraft: uplink PDU */
    }

    return t;
}

/* conversation may be used by other dissectors  */
wmem_tree_t *get_atn_conversation_tree(void){
    return atn_conversation_tree;
}


/* find a atn conversation tree node by an endpoint  */
/* an endpoint is identified by atn src and dst addresses */
/* and srcref or dstref (depends on the transport packet type) */
/* IMHO it's a hack - conversations should be maintained */
/* at transport layer (cotp) but this isn't working yet. */
atn_conversation_t * find_atn_conversation(
    address *address1,
    guint16 clnp_ref1,
    address *address2 )
{
    atn_conversation_t *cv = NULL;
    guint32 key = 0;
    guint32 tmp = 0;

    tmp = add_address_to_hash( tmp, address1);
    key = (tmp << 16) | clnp_ref1 ;

    tmp = add_address_to_hash( tmp, address2);
    key = (tmp << 24) | key ;

    /* search for atn conversation */
    cv = (atn_conversation_t *)
        wmem_tree_lookup32(get_atn_conversation_tree(),key);

    return cv;
}

/* create a atn conversation tree node  */
/* conversation data is to be allocated externally */
/* a conversation may be referenced from both endpoints */
atn_conversation_t * create_atn_conversation(
    address *address1,
    guint16 clnp_ref1,
    address *address2,
    atn_conversation_t *conversation)
{
    atn_conversation_t *cv = NULL;
    guint32 key = 0;
    guint32 tmp = 0;

    tmp = add_address_to_hash( tmp, address1);
    key = (tmp << 16) | clnp_ref1 ;

    tmp = add_address_to_hash( tmp, address2);
    key = (tmp << 24) | key ;

    /* search for aircraft entry */
    cv = (atn_conversation_t *)
    wmem_tree_lookup32(
        get_atn_conversation_tree(),
        key);

    /* tree node  already present  */
    if(cv) {
      return NULL; }

    /* insert conversation data in tree*/
    wmem_tree_insert32(
        get_atn_conversation_tree(),
        key,
        (void*)conversation);

    return conversation;
}

static int
dissect_atn_ulcs(
    tvbuff_t *tvb,
    packet_info *pinfo,
    proto_tree *tree,
    void *data _U_)
{
    int offset = 0;
    proto_item *ti = NULL;
    proto_tree *atn_ulcs_tree = NULL;
    guint8 value_pres = 0;
    guint8 value_ses = 0;
    guint16 value_ses_pres = 0;

    root_tree = tree;

    /* data pointer */
    /* decode as PDV-list */
    if ( (int)(intptr_t)  data == FALSE )
    {
        ti = proto_tree_add_item(
            tree,
            proto_atn_ulcs,
            tvb,
            0,
            0 ,
            ENC_NA);

        atn_ulcs_tree = proto_item_add_subtree(
            ti,
            ett_atn_ulcs);

        dissect_Fully_encoded_data_PDU(
            tvb,
            pinfo,
            atn_ulcs_tree, NULL);

        return offset +
          tvb_reported_length_remaining(tvb, offset ) ;
    }

    /* decode as SPDU, PPDU and ACSE PDU */
    if ( (int)(intptr_t)  data == TRUE )
    {
        /* get session and presentation PDU's */
        value_ses_pres = tvb_get_ntohs(tvb, offset);

        /* SPDU: dissect session layer */
        atn_ulcs_tree = proto_tree_add_subtree(
            tree, tvb, offset, 0,
            ett_atn_ses, NULL, ATN_SES_PROTO );

        /* get SPDU (1 octet) */
        value_ses = tvb_get_guint8(tvb, offset);

        /* SPDU type/identifier  */
        proto_tree_add_item(atn_ulcs_tree,
            hf_atn_ses_type,
            tvb,
            offset,
            1,
            ENC_BIG_ENDIAN );

        /* SPDU parameters may be present in Short Refuse */
        /* or Short Refuse Continue SPDU's */
        switch(value_ses & SES_PDU_TYPE_MASK){
            case SES_ATN_SRF:
            case SES_ATN_SRFC:

                /* SPDU parameter presence */
                proto_tree_add_item(atn_ulcs_tree,
                    hf_atn_ses_param_ind,
                    tvb,
                    offset,
                    1,
                    ENC_BIG_ENDIAN );

                /* parameter B2 */
                proto_tree_add_item(atn_ulcs_tree,
                    hf_atn_ses_param_b2,
                    tvb,
                    offset,
                    1,
                    ENC_BIG_ENDIAN );

                /* parameter B1 */
                proto_tree_add_item(atn_ulcs_tree,
                    hf_atn_ses_param_b1,
                    tvb,
                    offset,
                    1,
                    ENC_BIG_ENDIAN );

              break;
            default:
              break;
        }
        offset++;

        /* PPDU: dissect presentation layer */
        atn_ulcs_tree = proto_tree_add_subtree(
            tree, tvb, offset, 0,
            ett_atn_pres, NULL, ATN_PRES_PROTO );

        value_pres = tvb_get_guint8(tvb, offset);

        /* need session context to identify PPDU type */
        /* note: */
        proto_tree_add_uint_format(atn_ulcs_tree, hf_atn_pres_pdu_type,
            tvb,
            offset,
            1,
            value_ses_pres,
            "%s (0x%02x)",
            val_to_str_const( value_ses_pres & ATN_SES_PRES_MASK, atn_pres_vals, "?"),
            value_pres);

        /* PPDU errorcode in case of SRF/CPR */
        switch(value_ses & SES_PDU_TYPE_MASK){
            case SES_ATN_SRF:
            case SES_ATN_SRFC:
                proto_tree_add_item(
                    atn_ulcs_tree,
                    hf_atn_pres_err,
                    tvb,
                    offset,
                    1,
                    ENC_BIG_ENDIAN );
                break;
            default:
                break;
        }

        offset++;

        /* ACSE PDU: dissect application layer */
        atn_ulcs_tree = proto_tree_add_subtree(
            tree, tvb, offset, 0,
            ett_atn_acse, NULL, ATN_ACSE_PROTO );

        dissect_ACSE_apdu_PDU(
            tvb_new_subset_remaining(tvb, offset),
            pinfo,
            atn_ulcs_tree, NULL);

        return offset +
            tvb_reported_length_remaining(tvb, offset );
    }
    return offset;
}

static gboolean dissect_atn_ulcs_heur(
    tvbuff_t *tvb,
    packet_info *pinfo,
    proto_tree *tree,
    void *data _U_)
{
    /* do we have enough data*/
    /* at least session + presentation data or pdv-list */
    if (tvb_captured_length(tvb) < 2){
        return FALSE; }

    /* check for session/presentation/ACSE PDU's  */
    /* SPDU and PPDU are one octet each */
    switch( tvb_get_ntohs(tvb, 0) & 0xf8ff ){
        case 0xe802: /* SCN + CP*/
        case 0xf802: /* SCNC + CP */
        case 0xf002: /* SAC + CPA */
        case 0xd802: /* SACC + CPA */
        case 0xe002: /* SRF + CPR + R0 */
        case 0xe012: /* SRF + CPR + R1 */
        case 0xe022: /* SRF + CPR + R2 */
        case 0xe032: /* SRF + CPR + R3 */
        case 0xe042: /* SRF + CPR + R4 */
        case 0xe052: /* SRF + CPR + R5 */
        case 0xe062: /* SRF + CPR + R6 */
        case 0xe072: /* SRF + CPR + R7 */
        case 0xa002: /* SRFC + CPR + R0*/
        case 0xa012: /* SRFC + CPR + R1*/
        case 0xa022: /* SRFC + CPR + R2*/
        case 0xa032: /* SRFC + CPR + R3*/
        case 0xa042: /* SRFC + CPR + R4*/
        case 0xa052: /* SRFC + CPR + R5*/
        case 0xa062: /* SRFC + CPR + R6*/
        case 0xa072: /* SRFC + CPR + R7*/
            /* indicate to dissector routine */
            /* that a least SPDU, PPDU and */
            /* ACSE PDU is present */
            dissect_atn_ulcs(
                tvb,
                pinfo,
                tree,
                (void*) TRUE);
            return TRUE;
        default:  /* no SPDU */
            break;
    }

    /* try to detect "Fully-encoded-data" heuristically */
    /* the constants listed match the ASN.1 PER encoding */
    /* of PDV-List */
    switch(  tvb_get_ntohs(tvb, 0) & 0xfff0 ){
        case 0x0020: /* acse-apdu */
        case 0x00a0: /* user-ase-apdu */
        /* indicate to dissector routine */
        /* that a PDV-list PDU is present */
        /*  */
        /* PDV-list PDU may contain */
        /* application protocol data (CM, CPDLC) */
        /* or an ACSE PDU */
            dissect_atn_ulcs(tvb, pinfo, tree, (void*) FALSE);
            return TRUE;
            break;
        default:  /* no or unsupported PDU */
            break;
    }
    return FALSE;
}

void proto_register_atn_ulcs (void)
{
    static hf_register_info hf_atn_ulcs[] = {
    { &hf_atn_ulcs_Fully_encoded_data_PDU,
      { "Fully-encoded-data", "atn-ulcs.Fully_encoded_data",
        FT_UINT32, BASE_DEC, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_ACSE_apdu_PDU,
      { "ACSE-apdu", "atn-ulcs.ACSE_apdu",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_ACSE_apdu_vals), 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_Fully_encoded_data_item,
      { "PDV-list", "atn-ulcs.PDV_list_element",
        FT_NONE, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_transfer_syntax_name,
      { "transfer-syntax-name", "atn-ulcs.transfer_syntax_name",
        FT_OID, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_presentation_context_identifier,
      { "presentation-context-identifier", "atn-ulcs.presentation_context_identifier",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Presentation_context_identifier_vals), 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_presentation_data_values,
      { "presentation-data-values", "atn-ulcs.presentation_data_values",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_T_presentation_data_values_vals), 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_pdv_list_presentation_data_values_single_asn1_type,
      { "single-ASN1-type", "atn-ulcs.single_ASN1_type_element",
        FT_NONE, BASE_NONE, NULL, 0,
        "T_pdv_list_presentation_data_values_single_asn1_type", HFILL }},
    { &hf_atn_ulcs_octet_aligned,
      { "octet-aligned", "atn-ulcs.octet_aligned",
        FT_BYTES, BASE_NONE, NULL, 0,
        "OCTET_STRING", HFILL }},
    { &hf_atn_ulcs_pdv_list_presentation_data_values_arbitrary,
      { "arbitrary", "atn-ulcs.arbitrary",
        FT_BYTES, BASE_NONE, NULL, 0,
        "T_pdv_list_presentation_data_values_arbitrary", HFILL }},
    { &hf_atn_ulcs_direct_reference,
      { "direct-reference", "atn-ulcs.direct_reference",
        FT_OID, BASE_NONE, NULL, 0,
        "OBJECT_IDENTIFIER", HFILL }},
    { &hf_atn_ulcs_indirect_reference,
      { "indirect-reference", "atn-ulcs.indirect_reference",
        FT_INT32, BASE_DEC, NULL, 0,
        "INTEGER", HFILL }},
    { &hf_atn_ulcs_data_value_descriptor,
      { "data-value-descriptor", "atn-ulcs.data_value_descriptor",
        FT_STRING, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_encoding,
      { "encoding", "atn-ulcs.encoding",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_T_encoding_vals), 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_externalt_encoding_single_asn1_type,
      { "single-ASN1-type", "atn-ulcs.single_ASN1_type_element",
        FT_NONE, BASE_NONE, NULL, 0,
        "T_externalt_encoding_single_asn1_type", HFILL }},
    { &hf_atn_ulcs_externalt_encoding_octet_aligned,
      { "octet-aligned", "atn-ulcs.octet_aligned",
        FT_BYTES, BASE_NONE, NULL, 0,
        "T_externalt_encoding_octet_aligned", HFILL }},
    { &hf_atn_ulcs_externalt_encoding_arbitrary,
      { "arbitrary", "atn-ulcs.arbitrary",
        FT_BYTES, BASE_NONE, NULL, 0,
        "T_externalt_encoding_arbitrary", HFILL }},
    { &hf_atn_ulcs_aarq,
      { "aarq", "atn-ulcs.aarq_element",
        FT_NONE, BASE_NONE, NULL, 0,
        "AARQ_apdu", HFILL }},
    { &hf_atn_ulcs_aare,
      { "aare", "atn-ulcs.aare_element",
        FT_NONE, BASE_NONE, NULL, 0,
        "AARE_apdu", HFILL }},
    { &hf_atn_ulcs_rlrq,
      { "rlrq", "atn-ulcs.rlrq_element",
        FT_NONE, BASE_NONE, NULL, 0,
        "RLRQ_apdu", HFILL }},
    { &hf_atn_ulcs_rlre,
      { "rlre", "atn-ulcs.rlre_element",
        FT_NONE, BASE_NONE, NULL, 0,
        "RLRE_apdu", HFILL }},
    { &hf_atn_ulcs_abrt,
      { "abrt", "atn-ulcs.abrt_element",
        FT_NONE, BASE_NONE, NULL, 0,
        "ABRT_apdu", HFILL }},
    { &hf_atn_ulcs_aarq_apdu_protocol_version,
      { "protocol-version", "atn-ulcs.protocol_version",
        FT_BYTES, BASE_NONE, NULL, 0,
        "T_aarq_apdu_protocol_version", HFILL }},
    { &hf_atn_ulcs_application_context_name,
      { "application-context-name", "atn-ulcs.application_context_name",
        FT_OID, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_called_AP_title,
      { "called-AP-title", "atn-ulcs.called_AP_title",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_AP_title_vals), 0,
        "AP_title", HFILL }},
    { &hf_atn_ulcs_called_AE_qualifier,
      { "called-AE-qualifier", "atn-ulcs.called_AE_qualifier",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_AE_qualifier_vals), 0,
        "AE_qualifier", HFILL }},
    { &hf_atn_ulcs_called_AP_invocation_identifier,
      { "called-AP-invocation-identifier", "atn-ulcs.called_AP_invocation_identifier",
        FT_INT32, BASE_DEC, NULL, 0,
        "AP_invocation_identifier", HFILL }},
    { &hf_atn_ulcs_called_AE_invocation_identifier,
      { "called-AE-invocation-identifier", "atn-ulcs.called_AE_invocation_identifier",
        FT_INT32, BASE_DEC, NULL, 0,
        "AE_invocation_identifier", HFILL }},
    { &hf_atn_ulcs_calling_AP_title,
      { "calling-AP-title", "atn-ulcs.calling_AP_title",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_AP_title_vals), 0,
        "AP_title", HFILL }},
    { &hf_atn_ulcs_calling_AE_qualifier,
      { "calling-AE-qualifier", "atn-ulcs.calling_AE_qualifier",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_AE_qualifier_vals), 0,
        "AE_qualifier", HFILL }},
    { &hf_atn_ulcs_calling_AP_invocation_identifier,
      { "calling-AP-invocation-identifier", "atn-ulcs.calling_AP_invocation_identifier",
        FT_INT32, BASE_DEC, NULL, 0,
        "AP_invocation_identifier", HFILL }},
    { &hf_atn_ulcs_calling_AE_invocation_identifier,
      { "calling-AE-invocation-identifier", "atn-ulcs.calling_AE_invocation_identifier",
        FT_INT32, BASE_DEC, NULL, 0,
        "AE_invocation_identifier", HFILL }},
    { &hf_atn_ulcs_sender_acse_requirements,
      { "sender-acse-requirements", "atn-ulcs.sender_acse_requirements",
        FT_BYTES, BASE_NONE, NULL, 0,
        "ACSE_requirements", HFILL }},
    { &hf_atn_ulcs_mechanism_name,
      { "mechanism-name", "atn-ulcs.mechanism_name",
        FT_OID, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_calling_authentication_value,
      { "calling-authentication-value", "atn-ulcs.calling_authentication_value",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Authentication_value_vals), 0,
        "Authentication_value", HFILL }},
    { &hf_atn_ulcs_application_context_name_list,
      { "application-context-name-list", "atn-ulcs.application_context_name_list",
        FT_UINT32, BASE_DEC, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_implementation_information,
      { "implementation-information", "atn-ulcs.implementation_information",
        FT_BYTES, BASE_NONE, NULL, 0,
        "Implementation_data", HFILL }},
    { &hf_atn_ulcs_user_information,
      { "user-information", "atn-ulcs.user_information",
        FT_UINT32, BASE_DEC, NULL, 0,
        "Association_information", HFILL }},
    { &hf_atn_ulcs_aare_apdu_protocol_version,
      { "protocol-version", "atn-ulcs.protocol_version",
        FT_BYTES, BASE_NONE, NULL, 0,
        "T_aare_apdu_protocol_version", HFILL }},
    { &hf_atn_ulcs_result,
      { "result", "atn-ulcs.result",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Associate_result_vals), 0,
        "Associate_result", HFILL }},
    { &hf_atn_ulcs_result_source_diagnostic,
      { "result-source-diagnostic", "atn-ulcs.result_source_diagnostic",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Associate_source_diagnostic_vals), 0,
        "Associate_source_diagnostic", HFILL }},
    { &hf_atn_ulcs_responding_AP_title,
      { "responding-AP-title", "atn-ulcs.responding_AP_title",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_AP_title_vals), 0,
        "AP_title", HFILL }},
    { &hf_atn_ulcs_responding_AE_qualifier,
      { "responding-AE-qualifier", "atn-ulcs.responding_AE_qualifier",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_AE_qualifier_vals), 0,
        "AE_qualifier", HFILL }},
    { &hf_atn_ulcs_responding_AP_invocation_identifier,
      { "responding-AP-invocation-identifier", "atn-ulcs.responding_AP_invocation_identifier",
        FT_INT32, BASE_DEC, NULL, 0,
        "AP_invocation_identifier", HFILL }},
    { &hf_atn_ulcs_responding_AE_invocation_identifier,
      { "responding-AE-invocation-identifier", "atn-ulcs.responding_AE_invocation_identifier",
        FT_INT32, BASE_DEC, NULL, 0,
        "AE_invocation_identifier", HFILL }},
    { &hf_atn_ulcs_responder_acse_requirements,
      { "responder-acse-requirements", "atn-ulcs.responder_acse_requirements",
        FT_BYTES, BASE_NONE, NULL, 0,
        "ACSE_requirements", HFILL }},
    { &hf_atn_ulcs_responding_authentication_value,
      { "responding-authentication-value", "atn-ulcs.responding_authentication_value",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Authentication_value_vals), 0,
        "Authentication_value", HFILL }},
    { &hf_atn_ulcs_rlrq_apdu_request_reason,
      { "reason", "atn-ulcs.reason",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Release_request_reason_vals), 0,
        "Release_request_reason", HFILL }},
    { &hf_atn_ulcs_rlre_apdu_response_reason,
      { "reason", "atn-ulcs.reason",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Release_response_reason_vals), 0,
        "Release_response_reason", HFILL }},
    { &hf_atn_ulcs_abort_source,
      { "abort-source", "atn-ulcs.abort_source",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_ABRT_source_vals), 0,
        "ABRT_source", HFILL }},
    { &hf_atn_ulcs_abort_diagnostic,
      { "abort-diagnostic", "atn-ulcs.abort_diagnostic",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_ABRT_diagnostic_vals), 0,
        "ABRT_diagnostic", HFILL }},
    { &hf_atn_ulcs_Application_context_name_list_item,
      { "Application-context-name", "atn-ulcs.Application_context_name",
        FT_OID, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_ap_title_form2,
      { "ap-title-form2", "atn-ulcs.ap_title_form2",
        FT_OID, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_ap_title_form1,
      { "ap-title-form1", "atn-ulcs.ap_title_form1",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_Name_vals), 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_ae_qualifier_form2,
      { "ae-qualifier-form2", "atn-ulcs.ae_qualifier_form2",
        FT_INT32, BASE_DEC, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_ae_qualifier_form1,
      { "ae-qualifier-form1", "atn-ulcs.ae_qualifier_form1",
        FT_UINT32, BASE_DEC, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_acse_service_user,
      { "acse-service-user", "atn-ulcs.acse_service_user",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_T_acse_service_user_vals), 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_acse_service_provider,
      { "acse-service-provider", "atn-ulcs.acse_service_provider",
        FT_UINT32, BASE_DEC, VALS(atn_ulcs_T_acse_service_provider_vals), 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_Association_information_item,
      { "EXTERNALt", "atn-ulcs.EXTERNALt_element",
        FT_NONE, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_charstring,
      { "charstring", "atn-ulcs.charstring",
        FT_BYTES, BASE_NONE, NULL, 0,
        "OCTET_STRING", HFILL }},
    { &hf_atn_ulcs_bitstring,
      { "bitstring", "atn-ulcs.bitstring",
        FT_BYTES, BASE_NONE, NULL, 0,
        "BIT_STRING", HFILL }},
    { &hf_atn_ulcs_external,
      { "external", "atn-ulcs.external_element",
        FT_NONE, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_other,
      { "other", "atn-ulcs.other_element",
        FT_NONE, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_other_mechanism_name,
      { "other-mechanism-name", "atn-ulcs.other_mechanism_name",
        FT_OID, BASE_NONE, NULL, 0,
        "OBJECT_IDENTIFIER", HFILL }},
    { &hf_atn_ulcs_other_mechanism_value,
      { "other-mechanism-value", "atn-ulcs.other_mechanism_value_element",
        FT_NONE, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_rdnSequence,
      { "rdnSequence", "atn-ulcs.rdnSequence",
        FT_UINT32, BASE_DEC, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_RDNSequence_item,
      { "RelativeDistinguishedName", "atn-ulcs.RelativeDistinguishedName",
        FT_UINT32, BASE_DEC, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_RelativeDistinguishedName_item,
      { "AttributeTypeAndValue", "atn-ulcs.AttributeTypeAndValue_element",
        FT_NONE, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_null,
      { "null", "atn-ulcs.null_element",
        FT_NONE, BASE_NONE, NULL, 0,
        NULL, HFILL }},
    { &hf_atn_ulcs_T_aarq_apdu_protocol_version_version1,
      { "version1", "atn.ulcs.T.aarq.apdu.protocol.version.version1",
        FT_BOOLEAN, 8, NULL, 0x80,
        NULL, HFILL }},
    { &hf_atn_ulcs_T_aare_apdu_protocol_version_version1,
      { "version1", "atn.ulcs.T.aare.apdu.protocol.version.version1",
        FT_BOOLEAN, 8, NULL, 0x80,
        NULL, HFILL }},
    { &hf_atn_ulcs_ACSE_requirements_authentication,
      { "authentication", "atn.ulcs.ACSE.requirements.authentication",
        FT_BOOLEAN, 8, NULL, 0x80,
        NULL, HFILL }},
    { &hf_atn_ulcs_ACSE_requirements_application_context_negotiation,
      { "application-context-negotiation", "atn.ulcs.ACSE.requirements.application.context.negotiation",
        FT_BOOLEAN, 8, NULL, 0x40,
        NULL, HFILL }},
        {&hf_atn_ses_type,
        { "SPDU Type",
          "atn-ulcs.ses.type",
          FT_UINT8,
          BASE_HEX,
          VALS(atn_ses_type),
          0xf8,
          "Indicates presence of session parameters",
          HFILL}},
        {&hf_atn_ses_param_ind,
        { "SPDU Parameter Indication",
          "atn-ulcs.ses.parameter-indication",
          FT_UINT8,
          BASE_HEX,
          VALS(atn_ses_param_ind),
          SES_PARAM_IND_MASK,
          "Indicates presence of session parameters",
          HFILL}},
      {&hf_atn_ses_param_b1,
        { "SRF Parameter B1",
          "atn-ulcs.ses.srf-b1",
          FT_UINT8,
          BASE_HEX,
          VALS(srf_b1),
          0x01,
          "Determines if transport connection reject is transient or persistent",
          HFILL}},
      {&hf_atn_ses_param_b2,
        { "SRF Parameter B2",
          "atn-ulcs.ses.srf-b2",
          FT_UINT8,
          BASE_HEX,
          VALS(srf_b2),
          0x02,
          "Determines if transport connection is retained or released",
          HFILL}},
      { &hf_atn_pres_err,
        { "Error Code", "atn-ulcs.pres.cpr-error",
          FT_UINT8,
          BASE_HEX,
          VALS(atn_pres_err),
          PRES_CPR_ER_MASK,
          NULL,
          HFILL}},
      { &hf_atn_pres_pdu_type,
        { "PDU type", "atn-ulcs.pres.pdu_type",
          FT_UINT16,
          BASE_HEX,
          NULL,
          ATN_SES_PRES_MASK,
          NULL,
          HFILL}},
    };

    static gint *ett[] = {
    &ett_atn_ulcs_Fully_encoded_data,
    &ett_atn_ulcs_PDV_list,
    &ett_atn_ulcs_T_presentation_data_values,
    &ett_atn_ulcs_EXTERNALt,
    &ett_atn_ulcs_T_encoding,
    &ett_atn_ulcs_ACSE_apdu,
    &ett_atn_ulcs_AARQ_apdu,
    &ett_atn_ulcs_T_aarq_apdu_protocol_version,
    &ett_atn_ulcs_AARE_apdu,
    &ett_atn_ulcs_T_aare_apdu_protocol_version,
    &ett_atn_ulcs_RLRQ_apdu,
    &ett_atn_ulcs_RLRE_apdu,
    &ett_atn_ulcs_ABRT_apdu,
    &ett_atn_ulcs_ACSE_requirements,
    &ett_atn_ulcs_Application_context_name_list,
    &ett_atn_ulcs_AP_title,
    &ett_atn_ulcs_AE_qualifier,
    &ett_atn_ulcs_Associate_source_diagnostic,
    &ett_atn_ulcs_Association_information,
    &ett_atn_ulcs_Authentication_value,
    &ett_atn_ulcs_T_other,
    &ett_atn_ulcs_Name,
    &ett_atn_ulcs_RDNSequence,
    &ett_atn_ulcs_RelativeDistinguishedName,
    &ett_atn_ulcs_AttributeTypeAndValue,
        &ett_atn_ses,
        &ett_atn_pres,
        &ett_atn_acse,
        &ett_atn_ulcs
    };

    proto_atn_ulcs = proto_register_protocol (
        ATN_ULCS_PROTO ,
        "ATN-ULCS",
        "atn-ulcs");

    proto_register_field_array (
        proto_atn_ulcs,
        hf_atn_ulcs,
        array_length(hf_atn_ulcs));

    proto_register_subtree_array (
        ett,
        array_length (ett));

    register_dissector(
        "atn-ulcs",
        dissect_atn_ulcs,
        proto_atn_ulcs);

    /* initiate sub dissector list */
    atn_ulcs_heur_subdissector_list = register_heur_dissector_list("atn-ulcs", proto_atn_ulcs);

    /* init aare/aare data */
    aarq_data_tree = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());

    atn_conversation_tree = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
}

void proto_reg_handoff_atn_ulcs(void)
{
    atn_cm_handle = find_dissector_add_dependency("atn-cm", proto_atn_ulcs);
    atn_cpdlc_handle = find_dissector_add_dependency("atn-cpdlc", proto_atn_ulcs);

    /* add session dissector to cotp dissector list dissector list*/
    heur_dissector_add(
        "cotp",
        dissect_atn_ulcs_heur,
        "ATN-ULCS over COTP",
        "atn-ucls_cotp",
        proto_atn_ulcs, HEURISTIC_ENABLE);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
