/******************************************************************************/
// @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.
//   Trademark 2010, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC).
//   All rights reserved.
// @version: IWD_10wk37
// $Date: 2010-08-17 11:56:44 +0200 (Tue, 17 Aug 2010) $
// $Rev: 3671 $
/******************************************************************************/

module ICMPv6_Functions {
  import from Parameters all;
  import from ICMPv6_TypeDefs all;
  import from IP_PTC_Component all;
  import from CommonIP all;
  // import from Parameters all;
  
  group ICMPv6_Templates {
  
  template (value) ICMPv6Message cs_RouterAdvertisement  :=
  {
    routerAdvMsg := {
      icmpType := tsc_ICMPv6_RtAdvMsg, // RFC 4861
      icmpCode := 0,
      checksum := '0000'O, // FFS to be computed by the codec
      curHopLimit := 64,
      managedConfigFlag := '0'B, // Not managed, i.e. stateless autoconfiguration
      otherConfigFlag := '0'B, // FFS DHCPv6 not available
      reserved := '000000'B,
      routerLifetime := oct2int('FFFF'O), // largest posible value
      reachableTime := 0, // unspecified
      retransTimer := 0, // unspecified
      rtAdvOptions := {
        srcLinkLayerAddr := omit,
        mtuOption := omit,
        prefixInfoList := {
          {
            icmpType := tsc_ICMPv6_PrefixInfo,
            optionLength := 3, // 24 = 3 * 8 octets is the length  for the complete option
            prefixLength := 64, // 64 bits prefix
            linkFlag := '1'B, // on-link flag set
            autoConfigFlag := '1'B, // autonmous address-autconfiguration set
            reserved1 := '000000'B,
            validLifetime := oct2int ('FFFFFFFF'O), // infinity
            preferredLifetime := oct2int('FFFFFFFF'O), // infinity
            reserved2 := 0,
            // And here the 64 bits prefix
            prefix := f_Convert_IPv6Addr2OctString(px_IPv6_Address)
          }
        }, // prefixInfoList
        advertisementInterval := omit,
        homeAgentInfo := omit,
        otherOption := omit
      } // options
    }
  }; // template
  
  } // group Templates
  
/*
 * @desc Compute IP checksum according to ICMPv6 RFC 4443 clause 2.3.
 * @param p_Data which consists of  pseudo header preappended to an ICMPv6 message body
 * @return  The checksum
 Example C implementation in RFC1071
    {
     register long sum = 0;
      while( count > 1 )  {
             sum += * (unsigned short) addr++;
             count -= 2;
     }
     if( count > 0 )
             sum += * (unsigned char *) addr;
     while (sum>>16)
         sum = (sum & 0xffff) + (sum >> 16);
     checksum = ~sum;
   }
 */
 /* Use implementation in CommonIP, to be removed once CommonIP is verified
function f_IPChecksum (octetstring p_Data) return O2_Type
{
  var integer i := 0, v_Sum := 0;
  var integer v_Count := lengthof(p_Data);
  var integer uL, uH;
  var O2_Type v_cksum;
  var bitstring v_bitstr, v_bitstrH, v_bitstrL;
  while (v_Count > 1) {
   // Add 16 bit integers into a 32 bit accumulator
   uL := oct2int (p_Data[i]);
   uH := oct2int (p_Data[i+1]);
   v_Sum := v_Sum + uL + (uH * 256);
   v_Count := v_Count - 2; i := i + 2;
  } // while
  if (v_Count == 1) { // take care of last odd byte
   uL := oct2int (p_Data[i]);
   v_Sum := v_Sum + uL
  }
  // C implementation
  //  Sum = (Sum >> 16) + (Sum & 0xFFFF);
  //  Sum += (Sum >> 16);
  //  return ~Sum;
  
  v_bitstr := int2bit(v_Sum,32);
  v_bitstrH := substr(v_bitstr,0,16);
  v_bitstrL := substr(v_bitstr,16,32);
  uL := bit2int(v_bitstrL);
  uH := bit2int(v_bitstrH);
  v_Sum := uH + uL; // Sum = (Sum >> 16) + (Sum & 0xFFFF);
  v_bitstr := int2bit(v_Sum,32);
  v_bitstrH := substr(v_bitstr,0,16);
  uH := bit2int(v_bitstrH);
  v_Sum := v_Sum  + uH; // Sum += (Sum >> 16);
  v_cksum := int2oct(v_Sum,2);
  return (not4b(v_cksum)); // return ~Sum;
}
*/


  //****************************************************************************
  // Encoding/Decoding
  //----------------------------------------------------------------------------
  /**
   * @desc Decode a ICMPv6 message
   * @return 0 - success, 1 - error
   */
  function fl_ICMPv6_Decode(octetstring p_Data,
                            out ICMPv6Message p_DHCPMessage) return integer
  {
    var bitstring v_DataBitstring;
    v_DataBitstring := oct2bit(p_Data);
    
    if (decvalue(v_DataBitstring, p_DHCPMessage) != 0) {
      f_IP_PTC_Error(__FILE__, __LINE__, "ICMP message cannot be decoded");
      return 1; // Error
    }
    return 0;
  }
  
  /**
   * @desc Encode a ICMPv6 message
   */
  function fl_ICMPv6_Encode(template (value) ICMPv6Message p_ICMPMessage) return octetstring
  {
    var bitstring v_DataBitstring := encvalue(p_ICMPMessage);
    return bit2oct(v_DataBitstring);
  }
  
  //****************************************************************************
  // Main function
  /**
   * @desc Get the ICMPv6 response to be sent to the UE
   * @par p_ReqData - Encoded ICMPv6 request sent by the UE
   * @par p_ServerAddr - IP address of server
   * @return encoded ICMPv6 response, or ''O (empty string) in case of error
   * Base references are RFC 4861 and RFC 4862 for IPv6 stateless autoconfiguration
   * This function is still under development:
   *  - Currently only Router Solicitation message is handled.
   */
  function f_ICMPv6_GetResponse(octetstring p_ReqData) return octetstring
  {
    var ICMPv6Message v_Request;
    var template (value) ICMPv6Message v_Response;
    
    // Decode incoming DHCP message
    if (fl_ICMPv6_Decode(p_ReqData, v_Request) == 1){
      return ''O; // Error case
    }
    
    // Handle Router Solicitation message
    if ( ischosen(v_Request.routerSolMsg) ) {
      // Return  Router Advertisement
      v_Response := cs_RouterAdvertisement;
      // Modify some parameters with received values
      // FFS
      return fl_ICMPv6_Encode(v_Response);
    } // Router Solicitation message
    
    // Handle Neighbor Solicitation (for Duplicate Address Detection)
    if ( ischosen(v_Request.nbrSolMsg) ) {
      // FFS Message is ignored
      return ''O; // Error
    }
    
    // Other cases are not yet handled
    return ''O; // Error
  }
}
