/******************************************************************************/
// @copyright   Copyright Notification
//   No part may be reproduced except as authorized by written permission.
//   The copyright and the foregoing restriction extend to reproduction in all media.
//   (c) 2023, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC).
//   All rights reserved.
// @version: IWD_23wk37
// $Date: 2022-09-21 20:24:35 +0200 (Wed, 21 Sep 2022) $
// $Rev: 34317 $
/******************************************************************************/

module CommonIP {

  import from CommonDefs all;
  import from Parameters all;

  const integer tsc_IMS_MediaPort_M1 := 60350;                                         /* RTP port Speech  @sic R5s130495 Change 19 sic@
                                                                                        * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS) */
  const integer tsc_IMS_MediaPort_M2 := 60352;                                         /* RTP port Video   @sic R5s130495 Change 19 sic@
                                                                                        * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS) */
  const integer tsc_IMS_MediaPort_M3 := 60354;                                         /* RTP port Text    @sic R5s130495 Change 19 sic@
                                                                                        * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT) */


  const UInt16_Type tsc_PortNumberHTTP := 80;                    /* @status    APPROVED (IMS) */
  const UInt16_Type tsc_PortNumberHTTPS := 443;

  const UInt8_Type tsc_IP_Protocol_ICMP := 1;                     /* @status    APPROVED (ENDC, LTE, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT) */
  const UInt8_Type tsc_IP_Protocol_UDP  := 17;                    /* @status    APPROVED (LTE, LTE_A_PRO, NBIOT, NR5GC, UTRAN) */
  const UInt8_Type tsc_ICMP_Type_EchoReply := 0;                  /* @status    APPROVED (ENDC, LTE, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT) */
  const UInt8_Type tsc_ICMPv6_Type_EchoReply := 129;              /* @status    APPROVED (ENDC, LTE, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT) */
  const UInt8_Type tsc_ICMP_Type_EchoRequest := 8;                /* @status    APPROVED (ENDC, LTE, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT) */
  const UInt8_Type tsc_ICMPv6_Type_EchoRequest := 128;            /* @status    APPROVED (ENDC, LTE, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT) */
  const UInt8_Type tsc_IP_Protocol_TCP   := 6;                    /* @status    APPROVED (LTE, NBIOT, NR5GC, UTRAN) */
  const UInt8_Type tsc_IP_Protocol_IPSec := 50;                   /* @status    APPROVED (LTE, NBIOT, NR5GC, UTRAN) */
  const UInt8_Type tsc_IP_Protocol_ICMPv6 := 58;                  /* @status    APPROVED (ENDC, LTE, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT) */
  const integer tsc_IMS_PortNumber_5060 := 5060;                  /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS, UTRAN) */
  
  const O2_Type tsc_MulticastPrefix_FF02 := 'FF02'O;              /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN) */

  const octetstring tsc_IP_AnyData := '00112233445566778899AABBCCDDEEFF'O;  //* @status    APPROVED (ENDC, LTE, LTE_A_PRO, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT, UTRAN) */

  type record PDN_AddressInfo_Type {                              /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */
    charstring UE_IPAddressIPv4,
    charstring UE_IPAddressIPv6,
    charstring Remote_IPAddressIPv4,    /* @sic R5s130681 additional change sic@ */
    charstring Remote_IPAddressIPv6,    /* @sic R5s130681 additional change sic@ */
    charstring DHCP_ServerAddress,
    charstring ICMPv6_ServerAddress,
    charstring PCSCF_IPAddressIPv4,
    charstring PCSCF_IPAddressIPv6,
    charstring DNS_ServerAddressIPv4,
    charstring DNS_ServerAddressIPv6,
    charstring XCAP_ServerAddressIPv4,  /* @sic R5s141051 change 8 sic@ */
    charstring XCAP_ServerAddressIPv6,  /* @sic R5s141051 change 8 sic@ */
    charstring ProSe_ServerAddressIPv4, /* @sic R5-160765 sic@ */
    charstring ProSe_ServerAddressIPv6  /* @sic R5-160765 sic@ */
  };
  
  const PDN_AddressInfo_Type tsc_PDN1_AddressInfo := {          /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */
    UE_IPAddressIPv4 := px_IPv4_Address1_UE,
    UE_IPAddressIPv6 := px_IPv6_Address1_UE,
    Remote_IPAddressIPv4 := px_IPv4_Address1_RemoteUE,
    Remote_IPAddressIPv6 := px_IPv6_Address1_RemoteUE,
    DHCP_ServerAddress := px_IPv4_Address1_NW,
    ICMPv6_ServerAddress := px_IPv6_Address1_NW,
    PCSCF_IPAddressIPv4 := px_IPv4_Address1_NW,
    PCSCF_IPAddressIPv6 := px_IPv6_Address1_NW,
    DNS_ServerAddressIPv4 := px_IPv4_Address1_NW,
    DNS_ServerAddressIPv6 := px_IPv6_Address1_NW,
    XCAP_ServerAddressIPv4 := px_IPv4_Address1_NW, /* @sic R5s141051 change 8 sic@ */
    XCAP_ServerAddressIPv6 := px_IPv6_Address1_NW, /* @sic R5s141051 change 8 sic@ */
    ProSe_ServerAddressIPv4 := px_IPv4_Address1_NW, /* @sic R5-160765 sic@ */
    ProSe_ServerAddressIPv6 := px_IPv6_Address1_NW  /* @sic R5-160765 sic@ */
  };
  const PDN_AddressInfo_Type tsc_PDN2_AddressInfo := {          /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */
    UE_IPAddressIPv4 := px_IPv4_Address2_UE,
    UE_IPAddressIPv6 := px_IPv6_Address2_UE,
    Remote_IPAddressIPv4 := px_IPv4_Address2_RemoteUE,
    Remote_IPAddressIPv6 := px_IPv6_Address2_RemoteUE,
    DHCP_ServerAddress := px_IPv4_Address2_NW,
    ICMPv6_ServerAddress := px_IPv6_Address2_NW,
    PCSCF_IPAddressIPv4 := px_IPv4_Address2_NW,
    PCSCF_IPAddressIPv6 := px_IPv6_Address2_NW,
    DNS_ServerAddressIPv4 := px_IPv4_Address2_NW,
    DNS_ServerAddressIPv6 := px_IPv6_Address2_NW,
    XCAP_ServerAddressIPv4 := px_IPv4_Address2_NW, /* @sic R5s141051 change 8 sic@ */
    XCAP_ServerAddressIPv6 := px_IPv6_Address2_NW, /* @sic R5s141051 change 8 sic@ */
    ProSe_ServerAddressIPv4 := px_IPv4_Address2_NW, /* @sic R5-160765 sic@ */
    ProSe_ServerAddressIPv6 := px_IPv6_Address2_NW  /* @sic R5-160765 sic@ */
  };

  // @sic R5s191074 sic@
  const PDN_AddressInfo_Type tsc_PDN3_AddressInfo := {         /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)  To Do: make this equal to new PIXITs for address 3 */
    UE_IPAddressIPv4 := px_IPv4_Address3_UE,
    UE_IPAddressIPv6 := px_IPv6_Address3_UE,
    Remote_IPAddressIPv4 := px_IPv4_Address3_RemoteUE,
    Remote_IPAddressIPv6 := px_IPv6_Address3_RemoteUE,
    DHCP_ServerAddress := px_IPv4_Address3_NW,
    ICMPv6_ServerAddress := px_IPv6_Address3_NW,
    PCSCF_IPAddressIPv4 := px_IPv4_Address3_NW,
    PCSCF_IPAddressIPv6 := px_IPv6_Address3_NW,
    DNS_ServerAddressIPv4 := px_IPv4_Address3_NW,
    DNS_ServerAddressIPv6 := px_IPv6_Address3_NW,
    XCAP_ServerAddressIPv4 := px_IPv4_Address3_NW,
    XCAP_ServerAddressIPv6 := px_IPv6_Address3_NW,
    ProSe_ServerAddressIPv4 := px_IPv4_Address3_NW,
    ProSe_ServerAddressIPv6 := px_IPv6_Address3_NW
  };

  // @sic R5-221467 sic@
  const PDN_AddressInfo_Type tsc_PDN4_AddressInfo := {          /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN) */
    UE_IPAddressIPv4 := px_IPv4_Address4_UE,
    UE_IPAddressIPv6 := px_IPv6_Address4_UE,
    Remote_IPAddressIPv4 := px_IPv4_Address3_RemoteUE, // no new PIXIT defined for this as not expecting to be used
    Remote_IPAddressIPv6 := px_IPv6_Address3_RemoteUE, // no new PIXIT defined for this as not expecting to be used
    DHCP_ServerAddress := px_IPv4_Address4_NW,
    ICMPv6_ServerAddress := px_IPv6_Address4_NW,
    PCSCF_IPAddressIPv4 := px_IPv4_Address4_NW,
    PCSCF_IPAddressIPv6 := px_IPv6_Address4_NW,
    DNS_ServerAddressIPv4 := px_IPv4_Address4_NW,
    DNS_ServerAddressIPv6 := px_IPv6_Address4_NW,
    XCAP_ServerAddressIPv4 := px_IPv4_Address4_NW,
    XCAP_ServerAddressIPv6 := px_IPv6_Address4_NW,
    ProSe_ServerAddressIPv4 := px_IPv4_Address4_NW,
    ProSe_ServerAddressIPv6 := px_IPv6_Address4_NW
  };

  // @sic R5-221467 sic@
  const PDN_AddressInfo_Type tsc_PDN5_AddressInfo := {        /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN) */
    UE_IPAddressIPv4 := px_IPv4_Address5_UE,
    UE_IPAddressIPv6 := px_IPv6_Address5_UE,
    Remote_IPAddressIPv4 := px_IPv4_Address3_RemoteUE, // no new PIXIT defined for this as not expecting to be used
    Remote_IPAddressIPv6 := px_IPv6_Address3_RemoteUE, // no new PIXIT defined for this as not expecting to be used
    DHCP_ServerAddress := px_IPv4_Address5_NW,
    ICMPv6_ServerAddress := px_IPv6_Address5_NW,
    PCSCF_IPAddressIPv4 := px_IPv4_Address5_NW,
    PCSCF_IPAddressIPv6 := px_IPv6_Address5_NW,
    DNS_ServerAddressIPv4 := px_IPv4_Address5_NW,
    DNS_ServerAddressIPv6 := px_IPv6_Address5_NW,
    XCAP_ServerAddressIPv4 := px_IPv4_Address5_NW,
    XCAP_ServerAddressIPv6 := px_IPv6_Address5_NW,
    ProSe_ServerAddressIPv4 := px_IPv4_Address5_NW,
    ProSe_ServerAddressIPv6 := px_IPv6_Address5_NW
  };

  // @sic R5-221467 sic@
  const PDN_AddressInfo_Type tsc_PDN6_AddressInfo := {       /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN) */
    UE_IPAddressIPv4 := px_IPv4_Address6_UE,
    UE_IPAddressIPv6 := px_IPv6_Address6_UE,
    Remote_IPAddressIPv4 := px_IPv4_Address3_RemoteUE, // no new PIXIT defined for this as not expecting to be used
    Remote_IPAddressIPv6 := px_IPv6_Address3_RemoteUE, // no new PIXIT defined for this as not expecting to be used
    DHCP_ServerAddress := px_IPv4_Address6_NW,
    ICMPv6_ServerAddress := px_IPv6_Address6_NW,
    PCSCF_IPAddressIPv4 := px_IPv4_Address6_NW,
    PCSCF_IPAddressIPv6 := px_IPv6_Address6_NW,
    DNS_ServerAddressIPv4 := px_IPv4_Address6_NW,
    DNS_ServerAddressIPv6 := px_IPv6_Address6_NW,
    XCAP_ServerAddressIPv4 := px_IPv4_Address6_NW,
    XCAP_ServerAddressIPv6 := px_IPv6_Address6_NW,
    ProSe_ServerAddressIPv4 := px_IPv4_Address6_NW,
    ProSe_ServerAddressIPv6 := px_IPv6_Address6_NW
  };

  // @sic R5s191074, R5-221467 sic@
  const PDN_AddressInfo_Type tsc_PDN_AddressInfo[6] := { tsc_PDN1_AddressInfo, tsc_PDN2_AddressInfo, tsc_PDN3_AddressInfo, tsc_PDN4_AddressInfo, tsc_PDN5_AddressInfo, tsc_PDN6_AddressInfo};  /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN) */

  type record WLAN_AddressInfo_Type {  /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN) */
    charstring UE_IPAddressIPv4,
    charstring UE_IPAddressIPv6,
    charstring DNS_ServerAddressIPv4,
    charstring DNS_ServerAddressIPv6,
    charstring EPDG_ServerAddressIPv4,
    charstring EPDG_ServerAddressIPv6,
    charstring EmergencyEPDG_ServerAddressIPv4,
    charstring EmergencyEPDG_ServerAddressIPv6,
    charstring Non3GPP_XCAP_ServerAddressIPv4,
    charstring Non3GPP_XCAP_ServerAddressIPv6
  };
 
  /*
   * @desc      return index of the IP address or IMS server corresponding to the given PdnIndex
   * @param     p_PdnIndex
   * @return    integer
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_PDN_PdnIndex2Integer(PDN_Index_Type p_PdnIndex) return integer
  { /* PDN_2a to support change of the IP address for IMS test case 19.5.1 */
    /* @sic R5s150031: fl_IP_ServerIndex -> f_PDN_PdnIndex2Integer sic@ */
    var integer v_Index;
    select (p_PdnIndex) {
      case (PDN_1, ePDG_IMS1) {
        v_Index := 0;
      }
      case (PDN_2, PDN_2a, PDN_Internet, nrPDN_Internet, ePDG_IMS2, ePDG_XCAP, ePDG_Internet) { // @sic R5s191074 sic@
        v_Index := 1;
      }
      case (PDN_3) { // @sic R5s191074 sic@
        v_Index := 2;
      }
      case (PDN_4) { // @sic R5-221467 sic@
        v_Index := 3;
      }
      case (PDN_5) { // @sic R5-221467 sic@
        v_Index := 4;
      }
      case (PDN_6) { // @sic R5-221467 sic@
        v_Index := 5;
      }
      case else {
        FatalError(__FILE__, __LINE__, "Invalid PDN index");
      }
    }
    return v_Index;
  }

  /*
   * @desc      return index of the IMS server or IMS PTC
   * @param     p_PdnIndex
   * @return    integer
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_PDN_PdnIndex2ImsIndex(PDN_Index_Type p_PdnIndex) return integer
  {
    var integer v_Index := f_PDN_PdnIndex2Integer(p_PdnIndex);

    if (v_Index >= tsc_NoOfIMSPDNs) {
      FatalError(__FILE__, __LINE__, "Invalid PDN index");
    }
    return v_Index;
  }

  /*
   * @desc      return PDN_AddressInfo corresponding to the given PdnIndex
   * @param     p_PdnIndex
   * @return    PDN_AddressInfo_Type
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_PDN_AddressInfo_Get(PDN_Index_Type p_PdnIndex) return PDN_AddressInfo_Type
  { /* PDN_2a to support change of the IP address for IMS test case 19.5.1 */
    var PDN_AddressInfo_Type v_PDN_AddressInfo;
    var integer v_Index := f_PDN_PdnIndex2Integer(p_PdnIndex);

    v_PDN_AddressInfo := tsc_PDN_AddressInfo[v_Index];
    if (p_PdnIndex == PDN_2a) {
      v_PDN_AddressInfo.UE_IPAddressIPv4 := px_IPv4_Address2a_UE;   /* @sic implementation of IMS TC_19_5_1 sic@ */
      v_PDN_AddressInfo.UE_IPAddressIPv6 := px_IPv6_Address2a_UE;   /* @sic implementation of IMS TC_19_5_1 sic@ */
    }
    return v_PDN_AddressInfo;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Check whether given string is IPv4 address
   * @param     p_IpAddress
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_IpAddressIsIPv4(charstring p_IpAddress) return boolean
  {
    var template charstring v_Pattern := pattern "[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+";
    return match(p_IpAddress, v_Pattern);
  };

  //----------------------------------------------------------------------------
  /*
   * @desc      Check whether given string is IPv6 address
   * @param     p_IpAddress
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_IpAddressIsIPv6(charstring p_IpAddress) return boolean
  {
    var Char1List_Type v_SplitCharList := {":"};
    var CharStringList_Type v_FieldList := f_StringSplit(p_IpAddress, v_SplitCharList);
    var integer v_FieldCnt := lengthof(v_FieldList);
    var charstring v_Field;
    var charstring v_Pattern;
    var integer v_FieldLength;
    var integer i;
    
    if ((v_FieldCnt == 1) or (v_FieldCnt > 8)) { return false; }
    
    for (i:=0; i<v_FieldCnt; i:=i+1) {
      v_Field := v_FieldList[i];
      v_FieldLength := lengthof(v_Field);
      v_Pattern := "[0-9a-fA-F]#(" & int2str(v_FieldLength) & ")";  // @sic R5s150668 NOTE: "@nocase" to be used on long term sic@

      if (v_FieldLength > 4) { return false; }
      if (not match(v_Field, pattern v_Pattern)) { return false; }
    }
    return true;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Return IP address to be used as host name: for IPv6 the IP address gets surrounded with "[" and "]"
   * @param     p_IpAddress
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_IpAddressAsHostname(charstring p_IpAddress) return charstring
  {
    var charstring v_Hostname;
    
    if (f_IpAddressIsIPv6(p_IpAddress)) {
      v_Hostname := "[" & p_IpAddress & "]";
    } else {
      v_Hostname := p_IpAddress;
    }
    return v_Hostname;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      convert IP address in dot notation into octetstring;
   *            e.g. "10.11.12.13" results in '0A0B0C0D'O
   * @param     p_IPv4AddrChar
   * @return    O4_Type
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN)
   */
  function f_Convert_IPv4Addr2OctString(charstring p_IPv4AddrChar) return O4_Type
  {
    var Char1List_Type v_SplitCharList := {"."};
    var CharStringList_Type v_FieldList := f_StringSplit(p_IPv4AddrChar, v_SplitCharList);
    var octetstring v_IPv4AddrOct := ''O;
    var integer v_FieldCnt := lengthof(v_FieldList);
    var integer v_IntVal;
    var integer i;

    if (v_FieldCnt != 4) {
      FatalError(__FILE__, __LINE__, "invalid IP Address");
    }
    for (i:=0; i<v_FieldCnt; i:=i+1) {
      v_IntVal := str2int(v_FieldList[i]);
      v_IPv4AddrOct := v_IPv4AddrOct & int2oct(v_IntVal, 1);
    }
    return v_IPv4AddrOct;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Convert a text representation of a IPv6 address into hexadecimal format
   *            into an octetstring of length 16 according to RFC3513.
   *            E.g. "2001::1" results in '20010000000000000000000000000001'O
   * @param     p_IPv6AddrChar
   * @return    O16_Type
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN)
   */
  function f_Convert_IPv6Addr2OctString(charstring p_IPv6AddrChar) return O16_Type
  {
    var Char1List_Type v_SplitCharList := {":"};
    var CharStringList_Type v_FieldList := f_StringSplit(p_IPv6AddrChar, v_SplitCharList);
    var octetstring v_IPv6AddrOct := ''O;
    var integer v_FieldCnt := lengthof(v_FieldList);
    var boolean v_EmptyBlocksExpanded := false;          /* set to true when "::" has been expanded => subsequent empty fields will just be replaced by '0000' */
    var charstring v_Field;
    var integer v_NoOfEmptyBlocks;
    var integer i;
    var integer k;

    if (v_FieldCnt > 8) {
      FatalError(__FILE__, __LINE__, "invalid IP Address");
    }
    for (i:=0; i<v_FieldCnt; i:=i+1) {
      v_Field := v_FieldList[i];
      select (lengthof(v_Field)) {
        case (0) { // empty block
          if (not v_EmptyBlocksExpanded) {                /* e.g. for "2001:0ba0::" we have two empty blocks at the end of the address but only one of them shall be expanded; @sic R5s110645 change 5 sic@ */
            v_EmptyBlocksExpanded := true;
            v_NoOfEmptyBlocks := 8 - (v_FieldCnt - 1);    /* "-1" because one empty block is already contained in v_FieldCnt */
            for (k:=0; k < v_NoOfEmptyBlocks; k:=k+1) {
              v_IPv6AddrOct := v_IPv6AddrOct & '0000'O;
            }
          } else {                                        /* "::" has already been expanded */
            v_IPv6AddrOct := v_IPv6AddrOct & '0000'O;
          }
        }
        case (1) {
          v_IPv6AddrOct := v_IPv6AddrOct & str2oct("000" & v_Field);
        }
        case (2) {
          v_IPv6AddrOct := v_IPv6AddrOct & str2oct("00" & v_Field);
        }
        case (3) {
          v_IPv6AddrOct := v_IPv6AddrOct & str2oct("0" & v_Field);
        }
        case (4) {
          v_IPv6AddrOct := v_IPv6AddrOct & str2oct(v_Field);

        }
        case else {
          FatalError(__FILE__, __LINE__, "invalid IP Address");
        }
      }
    }
    return v_IPv6AddrOct;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      campare two IP addresses taking in to account different presentations of IPv6 addresses
   * @param     p_Addr1
   * @param     p_Addr2
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_IPAddresses_Compare(charstring p_Addr1,
                                 charstring p_Addr2) return boolean
  {
    var boolean v_Addr1IsV6  := f_IpAddressIsIPv6(p_Addr1);
    var boolean v_Addr2IsV6  := f_IpAddressIsIPv6(p_Addr2);

    if (v_Addr1IsV6 and v_Addr2IsV6) {
      return (f_Convert_IPv6Addr2OctString(p_Addr1) == f_Convert_IPv6Addr2OctString(p_Addr2));
    } else if (not v_Addr1IsV6 and not v_Addr2IsV6) {
      return (p_Addr1 == p_Addr2);
    } else {
      return false;
    }
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      convert IPv6 address given as octet string to charstring (readable format)
   * @param     p_IPv6AddrOct
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_Convert_OctString2IPv6Addr(O16_Type p_IPv6AddrOct) return charstring
  { /* @sic R5s110603 change 2 sic@ */
    var charstring v_IPv6Addr := "";
    var integer v_AddrLength := lengthof(p_IPv6AddrOct);
    var integer i := 0;

    while (i < v_AddrLength) {
      v_IPv6Addr := v_IPv6Addr & oct2str(substr(p_IPv6AddrOct, i, 2));
      i:=i+2;
      if (i < v_AddrLength) {
        v_IPv6Addr := v_IPv6Addr & ":";
      }
    }
    return v_IPv6Addr;
  }

  /*
   * @desc      convert IPv4 address given as octet string to charstring (readable format)
   * @param     p_IPv4AddrOct
   * @return    charstring
   * @status    APPROVED (ENDC, NR5GC, NR5GC_IRAT)
   */
  function f_Convert_OctString2IPv4Addr(O4_Type p_IPv4AddrOct) return charstring
  {
    var charstring v_IPv6Addr := "";
    var integer v_AddrLength := lengthof(p_IPv4AddrOct);
    var integer i := 0;

    while (i < v_AddrLength) {
      v_IPv6Addr := v_IPv6Addr & int2str(oct2int(substr(p_IPv4AddrOct, i, 1)));
      i:=i+1;
      if (i < v_AddrLength) {
        v_IPv6Addr := v_IPv6Addr & ".";
      }
    }
    return v_IPv6Addr;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Calculate of the IP checksum acc. RFC 1071
   * @param     p_OctetString
   * @return    O2_Type
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_IpChecksum(octetstring p_OctetString) return O2_Type
  { /* calculate header checksum; within the given octetstring the header checksum shall be set to '0000'O */
    var integer i;
    var octetstring v_Word;
    var integer v_DataLength := lengthof(p_OctetString);
    var integer v_Sum := 0;

    if (v_DataLength mod 2 != 0) {
      p_OctetString := p_OctetString & '00'O;
    }
    // accumulate all words of the IP header
    for (i:=0; i < v_DataLength; i := i+2) {
      v_Word := p_OctetString[i] & p_OctetString[i+1];
      v_Sum := v_Sum + oct2int(v_Word);
    }
    // deal with the
    while (v_Sum > tsc_UInt16Max) {
      v_Sum := (v_Sum mod (tsc_UInt16Max+1)) + (v_Sum / (tsc_UInt16Max+1));
    }
    // get complement
    v_Sum := tsc_UInt16Max - v_Sum;
    return int2oct(v_Sum, 2);
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Check whether an address is a IPv6 multicast address
   * @param     p_IPv6Addr
   * @return    boolean     ..  true if the address is a multicast address
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_IPv6AddrIsMulticast(charstring p_IPv6Addr) return boolean
  {
    var O2_Type v_Prefix;
    v_Prefix := str2oct(substr(p_IPv6Addr, 0, 4));
    if (v_Prefix == tsc_MulticastPrefix_FF02) { // RFC 2375
      return true;
    }
    return false;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      convert (global) IPv6 adddress into link-local address
   * @param     p_IPv6Addr        .. global IPv6 address
   * @return    charstring        .. link-local address
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_IPv6AddrGetLinkLocalAddr(charstring p_IPv6Addr) return charstring
  {
    var O16_Type v_IPv6AddrOctets := f_Convert_IPv6Addr2OctString(p_IPv6Addr);
    var O8_Type  v_InterfaceId    := substr(v_IPv6AddrOctets, 8, 8);             // interface id assuming a /64 prefix
    
    return f_Convert_OctString2IPv6Addr('FE80000000000000'O & v_InterfaceId);
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Return ICMP PDU including correct checksum
   * @param     p_SrcAddress
   * @param     p_DstAddress
   * @param     p_IcmpPDU
   * @return    octetstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, UTRAN)
   */
  function f_ICMPv6_UpdateCRCChecksum(charstring p_SrcAddress,
                                      charstring p_DstAddress,
                                      octetstring p_IcmpPDU) return octetstring
  {
    var octetstring v_PseudoHeader;
    var octetstring v_IcmpPDU := p_IcmpPDU;
    var O2_Type v_Checksum;
    // Build pseudo headers for ICMPv6, see RFC 4443 clause 2.3
    // Pseudo headers are specified in RFC 2460 clause 8.1
    v_PseudoHeader :=
      f_Convert_IPv6Addr2OctString(p_SrcAddress) &
        f_Convert_IPv6Addr2OctString(p_DstAddress) &
        int2oct (lengthof(p_IcmpPDU), 4) & '0000003A'O;
      
      //According to RFC 4443, section 2.3,
      //For computing the checksum, the checksum field is first set to zero.
      p_IcmpPDU[2] := '00'O;  /* @sic R5s190281 sic@ */
      p_IcmpPDU[3] := '00'O;  /* @sic R5s190281 sic@ */

      // Now compute checksum
      v_Checksum := f_IpChecksum (v_PseudoHeader & p_IcmpPDU);
      
      // See RFC 4443 clause 2.1
      v_IcmpPDU[2] := v_Checksum[0];
      v_IcmpPDU[3] := v_Checksum[1];
      return v_IcmpPDU;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      create an IPv6 packet (currently with typical settings for an UDP packet)
   *            For IPv6 packet format, see RFC 2460
   * @param     p_TrafficClass      (default value: '00'O)
   * @param     p_FlowLabel         (default value: 0)
   * @param     p_Protocol
   * @param     p_HopCount          (default value: 64)
   * @param     p_SourceAddr
   * @param     p_DestAddr
   * @param     p_IPPayload
   * @return    octetstring
   * @status    APPROVED (ENDC, LTE, LTE_A_PRO, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT, UTRAN)
   */
  function f_IPv6Packet_Create(O1_Type p_TrafficClass := '00'O,
                               UInt20_Type p_FlowLabel := 0,
                               UInt8_Type p_Protocol,
                               UInt8_Type p_HopCount := 64,
                               charstring p_SourceAddr,
                               charstring p_DestAddr,
                               octetstring p_IPPayload)
    return octetstring
  { /* @sic R5s170286: p_HopCount sic@ */
    var hexstring v_Version := '6'H;
    var hexstring v_TrafficClass := oct2hex (p_TrafficClass);
    var hexstring v_FlowLabel := int2hex (p_FlowLabel, 5);
    var O16_Type v_SourceAddrStr := f_Convert_IPv6Addr2OctString(p_SourceAddr);
    var O16_Type v_DestAddrStr := f_Convert_IPv6Addr2OctString(p_DestAddr);
    var octetstring v_OctetString;
    
    // IP header
    v_OctetString := hex2oct(v_Version & v_TrafficClass & v_FlowLabel);  // Version, Traffic Class and Flow Label to be revised
    v_OctetString := v_OctetString & int2oct (lengthof(p_IPPayload), 2); // Payload Length
    v_OctetString := v_OctetString & int2oct(p_Protocol, 1);   // Next header
    v_OctetString := v_OctetString & int2oct(p_HopCount, 1);   // Hop Limit @sic R5s170286 sic@
    v_OctetString := v_OctetString & v_SourceAddrStr;
    v_OctetString := v_OctetString & v_DestAddrStr;
    v_OctetString := v_OctetString & p_IPPayload;
    return v_OctetString;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      return MNC and MCC derived from IMSI acc. to 23.003
   * @param     p_IMSI
   * @param     p_LengthOfMNC       (default value: 2)
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_DomainName_MNC_MCC_DerivedFromIMSI(hexstring p_IMSI,
                                                integer p_LengthOfMNC := 2) return charstring
  {
    var charstring v_IMSI := hex2str(p_IMSI);
    var charstring v_MCC := substr(v_IMSI, 0, 3);
    var charstring v_MNC := substr(v_IMSI, 3, p_LengthOfMNC); // @sic R5s140356: index = 3 sic@

    if (p_LengthOfMNC == 2) {
      v_MNC := "0" & v_MNC;
    }

    return "mnc" & v_MNC & "." & "mcc" & v_MCC;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      build up a domain name derived from IMSI acc. to 23.003 clause 13.2
   * @param     p_FirstLabel
   * @param     p_IMSI
   * @param     p_LengthOfMNC       (default value: 2)
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_DomainName_3gppnetwork_DerivedFromIMSI(charstring p_FirstLabel,
                                                    hexstring p_IMSI,
                                                    integer p_LengthOfMNC := 2) return charstring
  { /* NOTE: the length of the MNC in general cannot be derived from the IMSI (some countries have MNCs with 2 and 3 digits; see e.g "Annex to ITU Operational Bulletin No. 992 - 15.XI.2011")
     *       instead the length of the MNC is stored in the EF-AD file on the SIM card (see 31.102 cl. 4.2.18) and acc. to 31.121 the MNC has 2 digits. */
    /* @sic R5-155363: usage of f_DomainName_MNC_MCC_DerivedFromIMSI sic@ */
    var charstring v_MncMcc := f_DomainName_MNC_MCC_DerivedFromIMSI(p_IMSI, p_LengthOfMNC);
    var CharStringList_Type v_LableList := { p_FirstLabel, v_MncMcc, "3gppnetwork.org" };

    return f_StringJoin(v_LableList, ".");
  }

  /*
   * @desc      build up home network domain name derived from IMSI acc. to 23.003 clause 13.2
   * @param     p_IMSI
   * @param     p_LengthOfMNC       (default value: 2)
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_DomainName_IMS_DerivedFromIMSI(hexstring p_IMSI,
                                               integer p_LengthOfMNC := 2) return charstring
  {
    return f_DomainName_3gppnetwork_DerivedFromIMSI("ims", p_IMSI, p_LengthOfMNC);
  }

  /*
   * @desc      build up Private User Identity derived from IMSI acc. to 23.003 clause 13.3
   * @param     p_IMSI
   * @param     p_LengthOfMNC       (default value: 2)
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_IMS_PrivateUserIdentityDerivedFromIMSI(hexstring p_IMSI,
                                                    integer p_LengthOfMNC := 2) return charstring
  {
    var charstring v_IMSI := hex2str(p_IMSI);
    return v_IMSI & "@" & f_DomainName_IMS_DerivedFromIMSI(p_IMSI, p_LengthOfMNC);
  }

  /*
   * @desc      build up Public User Identity derived from IMSI acc. to 23.003 clause 13.4B
   * @param     p_IMSI
   * @param     p_LengthOfMNC       (default value: 2)
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NR5GC, NR5GC_IRAT, POS)
   */
  function f_IMS_TemporaryPublicUserIdentity(hexstring p_IMSI,
                                             integer p_LengthOfMNC := 2) return charstring
  {
    return "sip:" & f_IMS_PrivateUserIdentityDerivedFromIMSI(p_IMSI, p_LengthOfMNC);
  }
}
