/******************************************************************************/
// @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-06-04 17:42:02 +0200 (Fri, 04 Jun 2010) $
// $Rev: 3297 $
/******************************************************************************/

module DHCPv4_Functions {

  import from DHCPv4_TypeDefs all;
  import from Parameters all;
  import from IP_PTC_Component all;
  import from CommonIP all;
  
  group DHCPv4_Templates {
  
  template (value) DHCPv4Message cds_DHCPv4Offer (charstring p_DhcpServerAddr)
    modifies cs_DHCPv4_RspBase :=
  {
    op := tsc_OPCODE_BOOTREPLY,
    options := cds_DHCPv4_Options_Offer (p_DhcpServerAddr)
  };

  template (value) DHCPMessagev4Options cds_DHCPv4_Options_Offer (charstring p_DhcpServerAddr)
    modifies  cs_DHCPv4_Options_Base:=
  {
    messageType := {
      code := DHCPv4Options_CODE_MessageType,
      len := 1,
      msgType := DHCPv4_MsgType_OFFER_E
    },
    serverIdentifier :=  {
      code := DHCPv4Options_CODE_ServerIdentifier,
      len := 4,
      val := f_Convert_IPv4Addr2OctString ( p_DhcpServerAddr )
    }
  };

  template (value) DHCPv4Message cds_DHCPv4ACK (charstring p_DhcpServerAddr)
    modifies cs_DHCPv4_RspBase:=
  {
    op := tsc_OPCODE_BOOTREPLY,
    options := cds_DHCPv4_Options_ACK (p_DhcpServerAddr)
  };

  template (value) DHCPMessagev4Options cds_DHCPv4_Options_ACK (charstring p_DhcpServerAddr)
    modifies  cs_DHCPv4_Options_Base :=
  {
    messageType := {
      code := DHCPv4Options_CODE_MessageType,
      len := 1,
      msgType := DHCPv4_MsgType_ACK_E
    },
    serverIdentifier :=  {
      code := DHCPv4Options_CODE_ServerIdentifier,
      len := 4,
      val := f_Convert_IPv4Addr2OctString ( p_DhcpServerAddr )
    }
  };
  } // group DHCPv4_Templates

  group DHCP_BaseTemplates {

  template (value) DHCPv4Message cs_DHCPv4_RspBase (charstring p_DhcpServerAddr)  :=
  {
    op := tsc_OPCODE_BOOTREPLY,
    htype := '00'O, // value in request to be used
    hlen  := 0, // value in request to be used
    hops  := 0,
    xid   := '00000000'O, // value in request to be used
    secs  := 0,
    flags := {'0'B, '000000000000000'B}, // No broadcast
    ciaddr := '00000000'O,
    yiaddr := f_Convert_IPv4Addr2OctString(px_IPv4_Address),  // UE address
    siaddr := f_Convert_IPv4Addr2OctString(p_DhcpServerAddr), // Server address
    giaddr := '00000000'O,
    chaddr := '00000000000000000000000000000000'O,
    sname := tsc_Dummy32 & tsc_Dummy32,
    file := tsc_Dummy32 & tsc_Dummy32 & tsc_Dummy32 & tsc_Dummy32,
    magicCookie := tsc_DHCPv4_MagicCookie, // RFC 2131 clause 3
    options := cs_DHCPv4_Options_Base,
    endOption := tsc_DHCPv4_EndOption,
    padding := omit
  };


  template (value) DHCPMessagev4Options cs_DHCPv4_Options_Base :=
  {
    subnetMask := omit,
    timeOffset  := omit,
    router := omit,
    timeServer := omit,
    nameServer := omit,
    domainNameServer := omit,
    logServer := omit,
    cookieServer := omit,
    lprServer := omit,
    impressServer := omit,
    rlocserv := omit,
    hostName := omit,
    bootFileSize := omit,
    meritDumpFile := omit,
    domainName := omit,
    swapServer := omit,
    rootPath := omit,
    extensions_path := omit,
    ip_forwarding := omit,
    nonLocalSourceRouting := omit,
    policyFilter := omit,
    maximumDatagramReassemblySize := omit,
    default_ip_ttl := omit,
    pathMTU_AgingTimeout := omit,
    pathMTU_PlateauTable := omit,
    interfaceMTU := omit,
    allSubnetsLocal := omit,
    broadcastAddress := omit,
    performMaskDiscovery := omit,
    maskSupplier := omit,
    performRouterDiscovery := omit,
    routerSolicitationAddress := omit,
    staticRoute := omit,
    trailerEncapsulation := omit,
    arpCacheTimeout := omit,
    ethernetEncapsulation := omit,
    tcpDefaultTTL := omit,
    tcpKeepAliveInterval := omit,
    tcpKeepAliveGarbage := omit,
    nisDomain := omit,
    nisServers := omit,
    ntpServers := omit,
    vendorSpecificInfo := omit,
    netbiosServers := omit,
    netbiosDDServers := omit,
    netbiosNodeType := omit,
    netbiosScope := omit,
    xWindowFont_server := omit,
    xWindowDisplayManagers := omit,
    requestedIPAddress := omit,
    ipAddressLeaseTime := omit,
    optionOverload := omit,
    messageType := omit,
    serverIdentifier := omit,
    parameterRequestList := omit,
    message97 := omit,
    maxDHCPSize := omit,
    renewalT1 := omit,
    rebindingT2 := omit,
    classIdentifier := omit,
    clientIdentifier := omit,
    sipServers := omit,
    rapidCommit := omit
  };
  } // group DHCP_BaseTemplates

  //****************************************************************************
  // Encoding/Decoding
  //----------------------------------------------------------------------------
  /**
   * @desc Decode a DHCPv4 message
   * @return 0 - success, 1 - error
   */
  function fl_DHCPv4_Decode(octetstring p_Data, out DHCPv4Message 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__, "DHCP message cannot be decoded");
      return 1; // Error
    }
    return 0;
  }

  /**
   * @desc Encode a DHCPv4 message
   */
  function fl_DHCPv4_Encode(template (value) DHCPv4Message p_DHCPMessage) return octetstring
  {
    var bitstring v_DataBitstring := encvalue(p_DHCPMessage);
    return bit2oct(v_DataBitstring);
  }

  //****************************************************************************
  // Main function
  /**
    * @desc Get the DHCPv4 response to be sent to the UE
    * @par p_ReqData - Encoded DHCP request sent by the UE
    * @par p_DhcpServerAddr - IP address of server
    * @return encoded DHCP response, or ''O (empty string) in case of error
    */
  function f_DHCP_GetResponse(octetstring p_ReqData,
                              charstring p_DhcpServerAddr) return octetstring
  {
    var DHCPv4Message v_Request;
    var template (value) DHCPv4Message v_Response;

    // Decode incoming DHCP message
    if (fl_DHCPv4_Decode(p_ReqData, v_Request) == 1){
      return ''O; // Error case
    }
    // Consistency check
    if (v_Request.magicCookie != tsc_DHCPv4_MagicCookie) {
      f_IP_PTC_Error(__FILE__, __LINE__, "Inconsistent magic cookie");
      return ''O; // Error case
    }

    // Handle DHCP Discover without Rapid Commit option
    if ((v_Request.op == tsc_OPCODE_BOOTREQUEST )
        and (v_Request.options.messageType.msgType == DHCPv4_MsgType_DISCOVER_E)
        and (not ispresent(v_Request.options.rapidCommit)))
      {
        // Return a DHCP Offer message
        v_Response := cds_DHCPv4Offer(p_DhcpServerAddr);
        // Modify some parameters with received values
        v_Response.htype :=  v_Request.htype;
        v_Response.hlen := v_Request.hlen;
        v_Response.xid :=  v_Request.xid;
        return fl_DHCPv4_Encode(v_Response);
      } // DHCP Discover without Rapid Commit option

    // Handle DHCP Discover without Rapid Commit option
    if ((v_Request.op == tsc_OPCODE_BOOTREQUEST )
        and (v_Request.options.messageType.msgType == DHCPv4_MsgType_DISCOVER_E)
        and (ispresent(v_Request.options.rapidCommit)))
      {
        // Return a DHCP ACK message
        v_Response := cds_DHCPv4ACK(p_DhcpServerAddr);
        v_Response.htype :=  v_Request.htype;
        v_Response.hlen := v_Request.hlen;
        v_Response.xid :=  v_Request.xid;
        return fl_DHCPv4_Encode(v_Response);
      } // DHCP Discover with Rapid Commit option
    
    // Handle DHCP Request message
    if ((v_Request.op == tsc_OPCODE_BOOTREQUEST )
      and (v_Request.options.messageType.msgType == DHCPv4_MsgType_REQUEST_E))
      {
        // Return a DHCP ACK message
        v_Response := cds_DHCPv4ACK(p_DhcpServerAddr);
        v_Response.htype :=  v_Request.htype;
        v_Response.hlen := v_Request.hlen;
        v_Response.xid :=  v_Request.xid;
        return fl_DHCPv4_Encode(v_Response);
      } // DHCP Request message

    // Other cases are not yet handled
    return ''O; // Error
  }

}
