/** @file

Copyright (c) 2004 - 2007, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

Module Name:
  pxe_bc_dhcp.c

Abstract:
  DHCP and PXE discovery protocol implementations.


**/

#include "Bc.h"

#include "PxeArch.h"

EFI_PXE_BASE_CODE_UDP_PORT DhcpServerPort        = DHCP_SERVER_PORT;
EFI_PXE_BASE_CODE_UDP_PORT DHCPClientPort        = DHCP_CLIENT_PORT;
EFI_PXE_BASE_CODE_UDP_PORT PseudoDhcpServerPort  = PXE_DISCOVERY_PORT;
#define PSEUDO_DHCP_CLIENT_PORT PseudoDhcpServerPort
EFI_IP_ADDRESS             BroadcastIP       = {{0xffffffff}};
EFI_IP_ADDRESS             DefaultSubnetMask = {{0xffffff00}};

typedef union {
  DHCPV4_OP_STRUCT          *OpPtr;
  PXE_OP_SERVER_LIST        *BootServersStr;
  PXE_SERVER_LIST           *BootServerList;
  PXE_BOOT_MENU_ENTRY       *BootMenuItem;
  PXE_OP_DISCOVERY_CONTROL  *DiscoveryControl;
  PXE_OP_BOOT_MENU          *BootMenu;
  PXE_OP_BOOT_ITEM          *BootItem;
  DHCPV4_OP_VENDOR_OPTIONS  *VendorOptions;
  DHCPV4_OP_OVERLOAD        *Overload;
  DHCPV4_OP_CLASS           *PxeClassStr;
  DHCPV4_OP_SUBNET_MASK     *SubnetMaskStr;
  DHCPV4_OP_MESSAGE_TYPE    *MessageType;
  UINT8                     *BytePtr;
} UNION_PTR;

#pragma pack(1)
//
// option structure for DHCPREQUEST at end of DISCOVER options
// and for DHCPDECLINE
//
const struct requestopendstr {
  DHCPV4_OP_REQUESTED_IP  OpReqIP;
  DHCPV4_OP_SERVER_IP     DhcServerIpPtr;
  UINT8                   End[1];
}
RequestOpEndStr = {
  {
    {
      OP_DHCP_REQ_IP_ADD,
      DHCPV4_OPTION_LENGTH(DHCPV4_OP_REQUESTED_IP)
    }
  },
  {
    {
      OP_DHCP_SERVER_IP,
      DHCPV4_OPTION_LENGTH(DHCPV4_OP_SERVER_IP)
    }
  },
  {
    OP_END
  }
};

#define DHCP_REQ_OPTIONS  (*(struct requestopendstr *) DHCPV4_OPTIONS_BUFFER.End)

PXE_OP_BOOT_ITEM                DefaultBootItem = {
  {
    VEND_PXE_BOOT_ITEM,
    DHCPV4_OPTION_LENGTH(PXE_OP_BOOT_ITEM)
  },
  0,
  0
};

//
// PXE discovery control default structure
//
PXE_OP_DISCOVERY_CONTROL DefaultDisCtl = {
  { VEND_PXE_DISCOVERY_CONTROL, DHCPV4_OPTION_LENGTH(PXE_OP_DISCOVERY_CONTROL) },
  0
};

//
// PXE credentials option structure
//
typedef struct {
  UINT8 c[4];
} PXE_CREDENTIAL;

typedef struct {
  DHCPV4_OP_HEADER  Header;
  PXE_CREDENTIAL    Credentials[1];
} PXE_OP_CREDENTIAL_TYPES;

//
// option structure for PXE discover (without credentials)
//
typedef struct {            // discoveropendstr {
  DHCPV4_OP_HEADER  Header; // vendor options
  PXE_OP_BOOT_ITEM  BootItem;
  UINT8             End[1]; // if credentials option, it starts here
} PXE_DISCOVER_OPTIONS;

#define DISCOVERoptions (*(PXE_DISCOVER_OPTIONS *) DHCPV4_OPTIONS_BUFFER.End)
#define DISCREDoptions  (*(PXE_OP_CREDENTIAL_TYPES *) DISCOVERoptions.End)

//
// common option beginning for all our DHCP messages except
// DHCPDECLINE and DHCPRELEASE
//
struct optionsstr {
  UINT8                       DhcpCookie[4];
  DHCPV4_OP_MESSAGE_TYPE      DhcpMessageType;
  DHCPV4_OP_MAX_MESSAGE_SIZE  DhcpMaxMessageSize;
  DHCPV4_OP_REQUESTED_OPTIONS DhcpRequestedOptions;
  DHCPV4_OP_PLATFORM_ID       DhcpPlatformId;
  DHCPV4_OP_NETWORK_INTERFACE DhcpNetworkInterface;
  DHCPV4_OP_ARCHITECTURE_TYPE DhcpClientArchitecture;
  DHCPV4_OP_CLASS_ID          DhcpClassIdentifier;
  UINT8                       End[1];
} DHCPOpStart;

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
OptionsStrucInit (
  VOID
  )
{
  DHCPOpStart.DhcpCookie[0] = 99;
  DHCPOpStart.DhcpCookie[1] = 130;
  DHCPOpStart.DhcpCookie[2] = 83;
  DHCPOpStart.DhcpCookie[3] = 99;
  DHCPOpStart.DhcpMessageType.Header.OpCode = OP_DHCP_MESSAGE_TYPE;
  DHCPOpStart.DhcpMessageType.Header.Length = 1;
  DHCPOpStart.DhcpMessageType.Type = DHCPDISCOVER;
  DHCPOpStart.DhcpMaxMessageSize.Header.OpCode = OP_DHCP_MAX_MESSAGE_SZ;
  DHCPOpStart.DhcpMaxMessageSize.Header.Length = 2;
  DHCPOpStart.DhcpMaxMessageSize.MaxSize[0] = MAX_DHCP_MSG_SZ >> 8;
  DHCPOpStart.DhcpMaxMessageSize.MaxSize[1] = MAX_DHCP_MSG_SZ & 0xff;
  DHCPOpStart.DhcpRequestedOptions.Header.OpCode = OP_DHCP_PARM_REQ_LIST;
  DHCPOpStart.DhcpRequestedOptions.Header.Length = sizeof (DHCPV4_REQUESTED_OPTIONS_DATA);
  DHCPOpStart.DhcpRequestedOptions.Data._OP_SUBNET_MASK = OP_SUBNET_MASK;                     /* 1 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_OFFSET = OP_TIME_OFFSET;                     /* 2 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_ROUTER_LIST = OP_ROUTER_LIST;                     /* 3 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_SERVERS = OP_TIME_SERVERS;                   /* 4 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_NAME_SERVERS = OP_NAME_SERVERS;                   /* 5 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DNS_SERVERS = OP_DNS_SERVERS;                     /* 6 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_HOST_NAME = OP_HOST_NAME;                         /* 12 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_BOOT_FILE_SZ = OP_BOOT_FILE_SZ;                   /* 13 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DOMAIN_NAME = OP_DOMAIN_NAME;                     /* 15 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_ROOT_PATH = OP_ROOT_PATH;                         /* 17 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_EXTENSION_PATH = OP_EXTENSION_PATH;               /* 18 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_MAX_DATAGRAM_SZ = OP_MAX_DATAGRAM_SZ;             /* 22 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DEFAULT_TTL = OP_DEFAULT_TTL;                     /* 23 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_BROADCAST_ADD = OP_BROADCAST_ADD;                 /* 28 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_DOMAIN_NAME = OP_NIS_DOMAIN_NAME;             /* 40 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_SERVERS = OP_NIS_SERVERS;                     /* 41 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_NTP_SERVERS = OP_NTP_SERVERS;                     /* 42 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_VENDOR_SPECIFIC = OP_VENDOR_SPECIFIC;             /* 43 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REQ_IP_ADD = OP_DHCP_REQ_IP_ADD;             /* 50 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_LEASE_TIME = OP_DHCP_LEASE_TIME;             /* 51 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_SERVER_IP = OP_DHCP_SERVER_IP;               /* 54 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_RENEWAL_TIME = OP_DHCP_RENEWAL_TIME;         /* 58 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REBINDING_TIME = OP_DHCP_REBINDING_TIME;     /* 59 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_CLASS_IDENTIFIER = OP_DHCP_CLASS_IDENTIFIER; /* 60 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_TFTP_SERVER_NAME = OP_DHCP_TFTP_SERVER_NAME; /* 66 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_BOOTFILE = OP_DHCP_BOOTFILE;                 /* 67 */
  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_PLATFORM_ID = OP_DHCP_PLATFORM_ID;           /* 97 */
  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption128 = 128;
  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption129 = 129;
  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption130 = 130;
  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption131 = 131;
  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption132 = 132;
  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption133 = 133, DHCPOpStart.DhcpRequestedOptions.Data.VendorOption134 = 134;
  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption135 = 135;
  DHCPOpStart.DhcpPlatformId.Header.OpCode              = OP_DHCP_PLATFORM_ID;
  DHCPOpStart.DhcpPlatformId.Header.Length              = DHCPV4_OPTION_LENGTH (DHCPV4_OP_PLATFORM_ID);
  DHCPOpStart.DhcpNetworkInterface.Header.OpCode        = OP_DHCP_NETWORK_ARCH;
  DHCPOpStart.DhcpNetworkInterface.Header.Length        = DHCPV4_OPTION_LENGTH (DHCPV4_OP_NETWORK_INTERFACE);
  DHCPOpStart.DhcpNetworkInterface.Type                 = 0;
  DHCPOpStart.DhcpNetworkInterface.MajorVersion         = 0;
  DHCPOpStart.DhcpNetworkInterface.MinorVersion         = 0;
  DHCPOpStart.DhcpClientArchitecture.Header.OpCode      = OP_DHCP_SYSTEM_ARCH;
  DHCPOpStart.DhcpClientArchitecture.Header.Length      = DHCPV4_OPTION_LENGTH (DHCPV4_OP_ARCHITECTURE_TYPE);
  DHCPOpStart.DhcpClientArchitecture.Type               = HTONS (SYS_ARCH);
  DHCPOpStart.DhcpClassIdentifier.Header.OpCode         = OP_DHCP_CLASS_IDENTIFIER;
  DHCPOpStart.DhcpClassIdentifier.Header.Length         = sizeof (DHCPV4_CLASS_ID_DATA);
  CopyMem (
    DHCPOpStart.DhcpClassIdentifier.Data.ClassIdentifier,
    "PXEClient:",
    sizeof ("PXEClient:")
    );
  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit2, "Arch:", sizeof ("Arch:"));
  CopyMem (
    DHCPOpStart.DhcpClassIdentifier.Data.ArchitectureType,
    "xxxxx",
    sizeof ("xxxxx")
    );
  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit3, ":", sizeof (":"));
  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.InterfaceName, "XXXX", sizeof ("XXXX"));
  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit4, ":", sizeof (":"));
  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMajor, "yyy", sizeof ("yyy"));
  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMinor, "xxx", sizeof ("xxx"));
  DHCPOpStart.End[0] = OP_END;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// DHCPDECLINE option structure
//
struct opdeclinestr {
  UINT8                   DhcpCookie[4];
  DHCPV4_OP_MESSAGE_TYPE  DhcpMessageType;
  struct requestopendstr  OpDeclineEnd;
};

#define DHCPDECLINEoptions  (*(struct opdeclinestr *) DHCPV4_TRANSMIT_BUFFER.options)

//
// DHCPRELEASE option structure
//
struct opreleasestr {
  UINT8                   DhcpCookie[4];
  DHCPV4_OP_MESSAGE_TYPE  DhcpMessageType;
  DHCPV4_OP_SERVER_IP     DhcServerIpPtr;
  UINT8                   End[1];
};

#define DHCPRELEASEoptions  (*(struct opreleasestr *) DHCPV4_TRANSMIT_BUFFER.options)

//
// array of PXE vendor options in which we are interested
// value 0 -> not of interest, else value is index into PXE OPTION array
// option values from 1 to MAX_OUR_PXE_OPT
//
UINT8  ourPXEopts[MAX_OUR_PXE_OPT] = {
  VEND_PXE_MTFTP_IP_IX,             // multicast IP address of bootfile for MTFTP listen
  VEND_PXE_MTFTP_CPORT_IX,          // UDP Port to monitor for MTFTP responses - Intel order
  VEND_PXE_MTFTP_SPORT_IX,          // Server UDP Port for MTFTP open - Intel order
  VEND_PXE_MTFTP_TMOUT_IX,          // Listen timeout - secs
  VEND_PXE_MTFTP_DELAY_IX,          // Transmission timeout - secs
  VEND_PXE_DISCOVERY_CONTROL_IX,    // bit field
  VEND_PXE_DISCOVERY_MCAST_ADDR_IX, // boot server discovery multicast address
  VEND_PXE_BOOT_SERVERS_IX,         // list of boot servers of form tp(2) cnt(1) ips[cnt]
  VEND_PXE_BOOT_MENU_IX,
  VEND_PXE_BOOT_PROMPT_IX,
  VEND_PXE_MCAST_ADDRS_ALLOC_IX,    // not used by client
  VEND_PXE_CREDENTIAL_TYPES_IX,
  VEND_13_IX,                       // not used by client
  VEND_14_IX,                       // not used by client
  VEND_15_IX,                       // not used by client
  VEND_16_IX,                       // not used by client
  VEND_17_IX,                       // not used by client
  VEND_18_IX,                       // not used by client
  VEND_19_IX,                       // not used by client
  VEND_20_IX,                       // not used by client
  VEND_21_IX,                       // not used by client
  VEND_22_IX,                       // not used by client
  VEND_23_IX,                       // not used by client
  VEND_24_IX,                       // not used by client
  VEND_25_IX,                       // not used by client
  VEND_26_IX,                       // not used by client
  VEND_27_IX,                       // not used by client
  VEND_28_IX,                       // not used by client
  VEND_29_IX,                       // not used by client
  VEND_30_IX,                       // not used by client
  VEND_31_IX,                       // not used by client
  VEND_32_IX,                       // not used by client
  VEND_33_IX,                       // not used by client
  VEND_34_IX,                       // not used by client
  VEND_35_IX,                       // not used by client
  VEND_36_IX,                       // not used by client
  VEND_37_IX,                       // not used by client
  VEND_38_IX,                       // not used by client
  VEND_39_IX,                       // not used by client
  VEND_40_IX,                       // not used by client
  VEND_41_IX,                       // not used by client
  VEND_42_IX,                       // not used by client
  VEND_43_IX,                       // not used by client
  VEND_44_IX,                       // not used by client
  VEND_45_IX,                       // not used by client
  VEND_46_IX,                       // not used by client
  VEND_47_IX,                       // not used by client
  VEND_48_IX,                       // not used by client
  VEND_49_IX,                       // not used by client
  VEND_50_IX,                       // not used by client
  VEND_51_IX,                       // not used by client
  VEND_52_IX,                       // not used by client
  VEND_53_IX,                       // not used by client
  VEND_54_IX,                       // not used by client
  VEND_55_IX,                       // not used by client
  VEND_56_IX,                       // not used by client
  VEND_57_IX,                       // not used by client
  VEND_58_IX,                       // not used by client
  VEND_59_IX,                       // not used by client
  VEND_60_IX,                       // not used by client
  VEND_61_IX,                       // not used by client
  VEND_62_IX,                       // not used by client
  VEND_63_IX,                       // not used by client
  VEND_64_IX,                       // not used by client
  VEND_65_IX,                       // not used by client
  VEND_66_IX,                       // not used by client
  VEND_67_IX,                       // not used by client
  VEND_68_IX,                       // not used by client
  VEND_69_IX,                       // not used by client
  VEND_70_IX,                       // not used by client
  VEND_PXE_BOOT_ITEM_IX
};

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// array of options in which we are interested
// value 0 -> not of interest, else value is index into OPTION array
// option values from 1 to MAX_OUR_OPT
//
UINT8  OurDhcpOptions[MAX_OUR_OPT] = {
  OP_SUBNET_MASK_IX,                      // OP_SUBNET_MASK   1   // data is the subnet mask
  OP_TIME_OFFSET_IX,                      // OP_TIME_OFFSET   2   // data is the time offset of subnet to UTC in seconds
  OP_ROUTER_LIST_IX,                      // OP_ROUTER_LIST   3   // list of routers on subnet
  OP_TIME_SERVERS_IX,                     // OP_TIME_SERVERS  4   // list of time servers available
  OP_NAME_SERVERS_IX,                     // OP_NAME_SERVERS  5   // list of name servers available
  OP_DNS_SERVERS_IX,                      // OP_DNS_SERVERS   6   // list of DNS servers available
  OP_LOG_SERVERS_IX,                      // OP_LOG_SERVERS   7
  OP_COOKIE_SERVERS_IX,                   // OP_COOKIE_SERVERS    8
  OP_LPR_SREVERS_IX,                      // OP_LPR_SREVERS   9
  OP_IMPRESS_SERVERS_IX,                  // OP_IMPRESS_SERVERS   10
  OP_RES_LOC_SERVERS_IX,                  // OP_RES_LOC_SERVERS   11
  OP_HOST_NAME_IX,                        // OP_HOST_NAME 12  // client name
  OP_BOOT_FILE_SZ_IX,                     // OP_BOOT_FILE_SZ  13  // number of 512 blocks of boot file
  OP_DUMP_FILE_IX,                        // OP_DUMP_FILE 14  // path name of dump file if client crashes
  OP_DOMAIN_NAME_IX,                      // OP_DOMAIN_NAME   15  // domain name to use
  OP_SWAP_SERVER_IX,                      // OP_SWAP_SERVER   16
  OP_ROOT_PATH_IX,                        // OP_ROOT_PATH 17  // path name containing root disk
  OP_EXTENSION_PATH_IX,                   // OP_EXTENSION_PATH    18  // name of TFTP downloadable file of form of OP
  OP_IP_FORWARDING_IX,                    // OP_IP_FORWARDING 19  // enable/disable IP packet forwarding
  OP_NON_LOCAL_SRC_RTE_IX,                // OP_NON_LOCAL_SRC_RTE 20  // enable/disable non local source routing
  OP_POLICY_FILTER_IX,                    // OP_POLICY_FILTER 21  // policy filters for non local source routing
  OP_MAX_DATAGRAM_SZ_IX,                  // OP_MAX_DATAGRAM_SZ   22  // maximum datagram reassembly size
  OP_DEFAULT_TTL_IX,                      // OP_DEFAULT_TTL   23  // default IP time to live
  OP_MTU_AGING_TIMEOUT_IX,                // OP_MTU_AGING_TIMEOUT 24
  OP_MTU_SIZES_IX,                        // OP_MTU_SIZES 25
  OP_MTU_TO_USE_IX,                       // OP_MTU_TO_USE    26
  OP_ALL_SUBNETS_LOCAL_IX,                // OP_ALL_SUBNETS_LOCAL 27
  OP_BROADCAST_ADD_IX,                    // OP_BROADCAST_ADD 28  // broadcast address used on subnet
  OP_PERFORM_MASK_DISCOVERY_IX,           // OP_PERFORM_MASK_DISCOVERY    29  // perform mask discovery using ICMP
  OP_RESPOND_TO_MASK_REQ_IX,              // OP_RESPOND_TO_MASK_REQ   30  // respond to subnet mask requests using ICMP
  OP_PERFORM_ROUTER_DISCOVERY_IX,         // OP_PERFORM_ROUTER_DISCOVERY  31
  OP_ROUTER_SOLICIT_ADDRESS_IX,           // OP_ROUTER_SOLICIT_ADDRESS    32
  OP_STATIC_ROUTER_LIST_IX,               // OP_STATIC_ROUTER_LIST    33  // list of dest/route pairs
  OP_USE_ARP_TRAILERS_IX,                 // OP_USE_ARP_TRAILERS      34
  OP_ARP_CACHE_TIMEOUT_IX,                // OP_ARP_CACHE_TIMEOUT 35
  OP_ETHERNET_ENCAPSULATION_IX,           // OP_ETHERNET_ENCAPSULATION    36  // 0 -> RFC 894, 1 -> IEEE 802.3 (RFC 1042)
  OP_TCP_DEFAULT_TTL_IX,                  // OP_TCP_DEFAULT_TTL   37  // default time to live when sending TCP segments
  OP_TCP_KEEP_ALIVE_INT_IX,               // OP_TCP_KEEP_ALIVE_INT    38  // keep alive interval in seconds
  OP_KEEP_ALIVE_GARBAGE_IX,               // OP_KEEP_ALIVE_GARBAGE    39
  OP_NIS_DOMAIN_NAME_IX,                  // OP_NIS_DOMAIN_NAME   40
  OP_NIS_SERVERS_IX,                      // OP_NIS_SERVERS   41
  OP_NTP_SERVERS_IX,                      // OP_NTP_SERVERS   42
  OP_VENDOR_SPECIFIC_IX,                  // OP_VENDOR_SPECIFIC   43
  OP_NBNS_SERVERS_IX,                     // OP_NBNS_SERVERS  44
  OP_NBDD_SERVERS_IX,                     // OP_NBDD_SERVERS  45
  OP_NETBIOS_NODE_TYPE_IX,                // OP_NETBIOS_NODE_TYPE 46
  OP_NETBIOS_SCOPE_IX,                    // OP_NETBIOS_SCOPE 47
  OP_XWINDOW_SYSTEM_FONT_SERVERS_IX,      // OP_XWINDOW_SYSTEM_FONT_SERVERS   48
  OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS_IX,  // OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS   49
  OP_DHCP_REQ_IP_ADD_IX,                  // OP_DHCP_REQ_IP_ADD   50  // requested IP address - in DHCPDISCOVER
  OP_DHCP_LEASE_TIME_IX,                  // OP_DHCP_LEASE_TIME   51  // lease time requested/granted
  OP_DHCP_OPTION_OVERLOAD_IX,             // OP_DHCP_OPTION_OVERLOAD  52  // file/server name/both used to hold options
  OP_DHCP_MESSAGE_TYPE_IX,                // OP_DHCP_MESSAGE_TYPE 53  // message type
  OP_DHCP_SERVER_IP_IX,                   // OP_DHCP_SERVER_IP    54      // IP of server
  OP_DHCP_PARM_REQ_LIST_IX,               // OP_DHCP_PARM_REQ_LIST    55  // list of requested parameters
  OP_DHCP_ERROR_MESSAGE_IX,               // OP_DHCP_ERROR_MESSAGE    56  // in DHCPNAK or DECLINE messages
  OP_DHCP_MAX_MESSAGE_SZ_IX,              // OP_DHCP_MAX_MESSAGE_SZ   57  // maximum DHCP message size client will accept
  OP_DHCP_RENEWAL_TIME_IX,                // OP_DHCP_RENEWAL_TIME 58  // time in seconds before transitioning to RENEWING state
  OP_DHCP_REBINDING_TIME_IX,              // OP_DHCP_REBINDING_TIME   59  // time in seconds before transitioning to REBINDING state
  OP_DHCP_CLASS_IDENTIFIER_IX,            // OP_DHCP_CLASS_IDENTIFIER 60
  OP_DHCP_CLIENT_IDENTIFIER_IX,           // OP_DHCP_CLIENT_IDENTIFIER    61
  OP_RESERVED62_IX,                       // OP_RESERVED62
  OP_RESERVED63_IX,                       // OP_RESERVED63
  OP_NISPLUS_DOMAIN_NAME_IX,              // OP_NISPLUS_DOMAIN_NAME   64
  OP_NISPLUS_SERVERS_IX,                  // OP_NISPLUS_SERVERS   65
  OP_DHCP_TFTP_SERVER_NAME_IX,            // OP_DHCP_TFTP_SERVER_NAME 66
  OP_DHCP_BOOTFILE_IX                     // OP_DHCP_BOOTFILE 67
};

#define RxBuf ((DHCP_RECEIVE_BUFFER *) (Private->ReceiveBuffers))

#pragma pack()

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/**

  @param  Smbios              Pointer to SMBIOS structure
  @param  StringNumber        String number to return. 0 is used to skip all
                              strings and  point to the next SMBIOS structure.

  @return Pointer to string, or pointer to next SMBIOS strcuture if StringNumber == 0

**/
CHAR8 *
PxeBcLibGetSmbiosString (
  IN  SMBIOS_STRUCTURE_POINTER  *Smbios,
  IN  UINT16                    StringNumber
  )
{
  UINT16  Index;
  CHAR8   *String;

  //
  // Skip over formatted section
  //
  String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);

  //
  // Look through unformated section
  //
  for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {
    if (StringNumber == Index) {
      return String;
    }
    //
    // Skip string
    //
    for (; *String != 0; String++)
      ;
    String++;

    if (*String == 0) {
      //
      // If double NULL then we are done.
      //  Return pointer to next structure in Smbios.
      //  if you pass in a 0 you will always get here
      //
      Smbios->Raw = (UINT8 *)++String;
      return NULL;
    }
  }

  return NULL;
}


/**
  This function gets system guid and serial number from the smbios table

  @param  SystemGuid          The pointer of returned system guid
  @param  SystemSerialNumber  The pointer of returned system serial number

  @retval EFI_SUCCESS         Successfully get the system guid and system serial
                              number
  @retval EFI_NOT_FOUND       Not find the SMBIOS table

**/
EFI_STATUS
PxeBcLibGetSmbiosSystemGuidAndSerialNumber (
  IN  EFI_GUID  *SystemGuid,
  OUT CHAR8     **SystemSerialNumber
  )
{
  EFI_STATUS                Status;
  SMBIOS_TABLE_ENTRY_POINT  *SmbiosTable;
  SMBIOS_STRUCTURE_POINTER  Smbios;
  SMBIOS_STRUCTURE_POINTER  SmbiosEnd;
  UINT16                    Index;

  Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);

  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  Smbios.Hdr    = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;
  SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);

  for (Index = 0; Index < SmbiosTable->TableLength; Index++) {
    if (Smbios.Hdr->Type == 1) {
      if (Smbios.Hdr->Length < 0x19) {
        //
        // Older version did not support Guid and Serial number
        //
        continue;
      }
      //
      // SMBIOS tables are byte packed so we need to do a byte copy to
      // prevend alignment faults on Itanium-based platform.
      //
      CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));
      *SystemSerialNumber = PxeBcLibGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);

      return EFI_SUCCESS;
    }
    //
    // Make Smbios point to the next record
    //
    PxeBcLibGetSmbiosString (&Smbios, 0);

    if (Smbios.Raw >= SmbiosEnd.Raw) {
      //
      // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.
      // given this we must double check against the lenght of
      // the structure.
      //
      return EFI_SUCCESS;
    }
  }

  return EFI_SUCCESS;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// add router list to list
//
VOID
Ip4AddRouterList (
  PXE_BASECODE_DEVICE *Private,
  DHCPV4_OP_IP_LIST   *IpListPtr
  )
{
  EFI_IP_ADDRESS  TmpIp;
  INTN            Index;
  INTN            num;

  if (IpListPtr == NULL) {
    return ;
  }

  for (Index = 0, num = IpListPtr->Header.Length >> 2; Index < num; ++Index) {
    CopyMem (&TmpIp, &IpListPtr->IpList[Index], 4);
    Ip4AddRouter (Private, &TmpIp);
  }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// send ARP for our IP - fail if someone has it
//
BOOLEAN
SetStationIP (
  PXE_BASECODE_DEVICE *Private
  )
{
  EFI_MAC_ADDRESS DestMac;
  EFI_STATUS      EfiStatus;

  ZeroMem (&DestMac, sizeof DestMac);

  if (GetHwAddr(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac)
    || DoArp(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac) == EFI_SUCCESS) {
    return FALSE;   // somebody else has this IP
  }

  CopyMem (
    (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->StationIp,
    &DHCP_REQ_OPTIONS.OpReqIP.Ip,
    sizeof (EFI_IPv4_ADDRESS)
    );

  Private->GoodStationIp = TRUE;

  if (!Private->UseIgmpv1Reporting) {
    return TRUE;
  }

  if (Private->Igmpv1TimeoutEvent != NULL) {
    return TRUE;
  }

  EfiStatus = gBS->CreateEvent (
                    EVT_TIMER,
                    TPL_CALLBACK,
                    NULL,
                    NULL,
                    &Private->Igmpv1TimeoutEvent
                    );

  if (EFI_ERROR (EfiStatus)) {
    Private->Igmpv1TimeoutEvent = NULL;
    return TRUE;
  }

  EfiStatus = gBS->SetTimer (
                    Private->Igmpv1TimeoutEvent,
                    TimerRelative,
                    (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000
                    );  /* 400 seconds */

  if (EFI_ERROR (EfiStatus)) {
    gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
    Private->Igmpv1TimeoutEvent = NULL;
  }

  return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
AddRouters (
  PXE_BASECODE_DEVICE *Private,
  DHCP_RECEIVE_BUFFER *RxBufPtr
  )
{
  Ip4AddRouterList (
    Private,
    (DHCPV4_OP_IP_LIST *) RxBufPtr->OpAdds.PktOptAdds[OP_ROUTER_LIST_IX - 1]
    );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
DoUdpWrite (
  PXE_BASECODE_DEVICE         *Private,
  EFI_IP_ADDRESS              *ServerIpPtr,
  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,
  EFI_IP_ADDRESS              *ClientIpPtr,
  EFI_PXE_BASE_CODE_UDP_PORT  *ClientPortPtr
  )
{
  UINTN Len;

  Len = sizeof DHCPV4_TRANSMIT_BUFFER;

  return UdpWrite (
          Private,
          EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
          ServerIpPtr,
          ServerPortPtr,
          0,
          ClientIpPtr,
          ClientPortPtr,
          0,
          0,
          &Len,
          Private->TransmitBuffer
          );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// initialize the DHCP structure
//
typedef struct {
  UINT8 x[4];
} C4Str;

VOID
InitDhcpv4TxBuf (
  PXE_BASECODE_DEVICE *Private
  )
{
  UINTN                   HwAddrLen;
  UINT8                   *String;
  CHAR8                   *SystemSerialNumber;
  EFI_PXE_BASE_CODE_MODE  *PxebcMode;

  PxebcMode = Private->EfiBc.Mode;

  ZeroMem (&DHCPV4_TRANSMIT_BUFFER, sizeof (DHCPV4_STRUCT));
  DHCPV4_TRANSMIT_BUFFER.op     = BOOTP_REQUEST;
  DHCPV4_TRANSMIT_BUFFER.htype  = Private->SimpleNetwork->Mode->IfType;
  DHCPV4_TRANSMIT_BUFFER.flags  = HTONS (DHCP_BROADCAST_FLAG);
  CopyMem (&DHCPV4_OPTIONS_BUFFER, (VOID *) &DHCPOpStart, sizeof (DHCPOpStart));

  //
  // default to hardware address
  //
  HwAddrLen = Private->SimpleNetwork->Mode->HwAddressSize;

  if (HwAddrLen > sizeof DHCPV4_TRANSMIT_BUFFER.chaddr) {
    HwAddrLen = sizeof DHCPV4_TRANSMIT_BUFFER.chaddr;
  }

  String = (UINT8 *) &Private->SimpleNetwork->Mode->CurrentAddress;

  if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber (
        (EFI_GUID *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid,
        &SystemSerialNumber
        ) == EFI_SUCCESS) {
    if (PxebcMode->SendGUID) {
      HwAddrLen = sizeof (EFI_GUID);
      String    = (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid;
    }
  } else {
    //
    // GUID not yet set - send all 0xff's to show programable (via SetVariable)
    // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
    // GUID not yet set - send all 0's to show not programable
    //
    ZeroMem (DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof (EFI_GUID));
  }

  DHCPV4_TRANSMIT_BUFFER.hlen = (UINT8) HwAddrLen;
  CopyMem (DHCPV4_TRANSMIT_BUFFER.chaddr, String, HwAddrLen);

  CvtNum (
    SYS_ARCH,
    (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType,
    sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType
    );

  DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.Type                         = Private->NiiPtr->Type;
  DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion                 = Private->NiiPtr->MajorVer;
  DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion                 = Private->NiiPtr->MinorVer;

  *(C4Str *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.InterfaceName = *(C4Str *) Private->NiiPtr->StringId;

  CvtNum (
    DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion,
    (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor,
    sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor
    );

  CvtNum (
    DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion,
    (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor,
    sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor
    );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
UINT32
DecodePxeOptions (
  DHCP_RECEIVE_BUFFER *RxBufPtr,
  UINT8               *ptr,
  INTN                Len
  )
{
  UINT8     Op;
  UINT8     *EndPtr;
  INTN      Index;
  UNION_PTR LocalPtr;
  UINT32    status;

  status = 0;

  for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) {
    Op  = ptr[0];
    Len = ptr[1];

    switch (Op) {
    case OP_PAD:
      Len = -1;
      break;

    case OP_END:
      return status;

    default:
      LocalPtr.BytePtr = ptr;
      if (Op <= MAX_OUR_PXE_OPT) {
        Index = ourPXEopts[Op - 1];
        if (Index) {
          RxBufPtr->OpAdds.PxeOptAdds[Index - 1] = LocalPtr.OpPtr;
          status |= 1 << Index;
          if (Index == VEND_PXE_BOOT_ITEM && LocalPtr.BootItem->Header.Length == 3) {
            RxBufPtr->OpAdds.Status |= USE_THREE_BYTE;
          }
        }
      }
      break;
    }
  }

  return status;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
DecodeOptions (
  DHCP_RECEIVE_BUFFER *RxBufPtr,
  UINT8               *ptr,
  INTN                Len
  )
{
  UINT8     Op;
  UINT8     *EndPtr;
  INTN      Index;
  UNION_PTR LocalPtr;

  for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) {
    Op  = ptr[0];
    Len = ptr[1];

    switch (Op) {
    case OP_PAD:
      Len = -1;
      break;

    case OP_END:
      return ;

    default:
      LocalPtr.BytePtr = ptr;
      if (Op <= MAX_OUR_OPT) {
        Index = OurDhcpOptions[Op - 1];
        if (Index) {
          RxBufPtr->OpAdds.PktOptAdds[Index - 1] = LocalPtr.OpPtr;
          if (Index == OP_VENDOR_SPECIFIC_IX) {
            UINT32  status;
            status = DecodePxeOptions (
                      RxBufPtr,
                      (UINT8 *) LocalPtr.VendorOptions->VendorOptions,
                      LocalPtr.VendorOptions->Header.Length
                      );
            if (status) {
              RxBufPtr->OpAdds.Status |= PXE_TYPE;
              //
              // check for all the MTFTP info options present - any missing is a nogo
              //
              if ((status & WfM11a_OPTS) == WfM11a_OPTS) {
                RxBufPtr->OpAdds.Status |= WfM11a_TYPE;
              }

              if (status & DISCOVER_OPTS) {
                RxBufPtr->OpAdds.Status |= DISCOVER_TYPE;
              }

              if (status & CREDENTIALS_OPT) {
                RxBufPtr->OpAdds.Status |= CREDENTIALS_TYPE;
              }
            }
          }
        }
      }
      break;
    }
  }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
Parse (
  DHCP_RECEIVE_BUFFER *RxBufPtr,
  INTN                Len
  )
{
  UNION_PTR LocalPtr;

  //
  // initialize
  //
  SetMem (&RxBufPtr->OpAdds, sizeof RxBufPtr->OpAdds, 0);

  DecodeOptions (
    RxBufPtr,
    RxBufPtr->u.Dhcpv4.options + 4,
    Len - (sizeof RxBufPtr->u.Dhcpv4 - sizeof RxBufPtr->u.Dhcpv4.options + 4)
    );

  LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_OPTION_OVERLOAD_IX - 1];

  if ((LocalPtr.OpPtr) && (LocalPtr.Overload->Overload & OVLD_SRVR_NAME)) {
    DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.sname, sizeof RxBufPtr->u.Dhcpv4.sname);
  }

  if (LocalPtr.OpPtr && (LocalPtr.Overload->Overload & OVLD_FILE)) {
    DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.file, sizeof RxBufPtr->u.Dhcpv4.file);
  } else if (!RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && RxBufPtr->u.Dhcpv4.file[0]) {
    RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] = (DHCPV4_OP_STRUCT *) (RxBufPtr->u.Dhcpv4.file - sizeof (DHCPV4_OP_HEADER));

    RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length = (UINT8) AsciiStrLen ((CHAR8 *) RxBufPtr->u.Dhcpv4.file);
  }

  LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_CLASS_IDENTIFIER_IX - 1];

  if ((LocalPtr.OpPtr) &&
      LocalPtr.PxeClassStr->Header.Length >= 9 &&
      !CompareMem (LocalPtr.PxeClassStr->Class, "PXEClient", 9)
        ) {
    RxBufPtr->OpAdds.Status |= PXE_TYPE;
  }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
CopyParseRxBuf (
  PXE_BASECODE_DEVICE *Private,
  INTN                RxBufIndex,
  INTN                PacketIndex
  )
{
  DHCP_RECEIVE_BUFFER *RxBufPtr;

  RxBufPtr = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[PacketIndex];

  CopyMem (
    &RxBufPtr->u.Dhcpv4,
    &RxBuf[RxBufIndex].u.Dhcpv4,
    sizeof (RxBuf[RxBufIndex].u.Dhcpv4)
    );

  Parse (RxBufPtr, sizeof RxBufPtr->u.ReceiveBuffer);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
CopyProxyRxBuf (
  PXE_BASECODE_DEVICE *Private,
  INTN                RxBufIndex
  )
{
  Private->EfiBc.Mode->ProxyOfferReceived = TRUE;
  CopyParseRxBuf (Private, RxBufIndex, PXE_OFFER_INDEX);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
CopyParse (
  PXE_BASECODE_DEVICE       *Private,
  EFI_PXE_BASE_CODE_PACKET  *PacketPtr,
  EFI_PXE_BASE_CODE_PACKET  *NewPacketPtr,
  INTN                      Index
  )
{
  DHCP_RECEIVE_BUFFER *DhcpRxBuf;

  DhcpRxBuf = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[Index];

  CopyMem (
    (EFI_PXE_BASE_CODE_PACKET *) &DhcpRxBuf->u.Dhcpv4,
    NewPacketPtr,
    sizeof (*NewPacketPtr)
    );

  CopyMem (&*PacketPtr, &*NewPacketPtr, sizeof (*NewPacketPtr));

  Parse (DhcpRxBuf, sizeof DhcpRxBuf->u.ReceiveBuffer);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
AckEdit (
  DHCP_RECEIVE_BUFFER *DhcpRxBuf
  )
{
  UNION_PTR LocalPtr;

  LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1];

  //
  // check that an ACK
  // if a DHCP type, must be DHCPOFFER and must have server id
  //
  return (BOOLEAN)
    (
      (LocalPtr.OpPtr) &&
      (LocalPtr.MessageType->Type == DHCPACK) &&
      DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]
    );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// if a discover type packet, make sure all required fields are present
//
BOOLEAN
DHCPOfferAckEdit (
  DHCP_RECEIVE_BUFFER *DhcpRxBuf
  )
{
  PXE_OP_SERVER_LIST  *BootServerOpPtr;
  UNION_PTR           LocalPtr;

  if ((DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) == 0) {
    return TRUE;
  }

  LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];

  if (LocalPtr.OpPtr == NULL) {
    LocalPtr.OpPtr  = (DHCPV4_OP_STRUCT *) &DefaultDisCtl;
    DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultDisCtl;
  }
  //
  // make sure all required fields are here
  // if mucticast enabled, need multicast address
  //
  if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST) &&
      (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1] || !IS_MULTICAST (((DHCPV4_OP_STRUCT *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Data))
      ) {
    return FALSE;
    //
    // missing required field
    //
  }
  //
  // if a list, it better be good
  //
  BootServerOpPtr = (PXE_OP_SERVER_LIST *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1];

  if (BootServerOpPtr != NULL) {
    PXE_SERVER_LIST *BootServerListPtr;
    INTN            ServerListLen;
    INTN            ServerEntryLen;

    BootServerListPtr = BootServerOpPtr->ServerList;
    ServerListLen     = BootServerOpPtr->Header.Length;

    do {
      EFI_IPv4_ADDRESS  *IpListPtr;
      INTN              IpCnt;

      IpCnt           = BootServerListPtr->u.Ipv4List.IpCount;

      ServerEntryLen  = sizeof (PXEV4_SERVER_LIST) + 2 + (IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS);

      if (ServerListLen < ServerEntryLen) {
        //
        // missing required field
        //
        return FALSE;
      }

      IpListPtr = BootServerListPtr->u.Ipv4List.IpList;

      while (IpCnt--) {
        if (IS_MULTICAST (IpListPtr)) {
          //
          // missing required field
          //
          return FALSE;
        } else {
          ++IpListPtr;
        }
      }

      BootServerListPtr = (PXE_SERVER_LIST *) IpListPtr;
    } while (ServerListLen -= ServerEntryLen);
  }
  //
  // else there must be a list if use list enabled or multicast and
  // broadcast disabled
  //
  else if ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) ||
           ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST))
          ) {
    //
    // missing required field
    //
    return FALSE;
  }
  //
  // if not USE_BOOTFILE or no bootfile given, must have menu stuff
  //
  if (!(LocalPtr.DiscoveryControl->ControlBits & USE_BOOTFILE) ||
      !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]
      ) {
    INTN  MenuLth;

    LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];

    if (LocalPtr.OpPtr == NULL || !DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]) {
      //
      // missing required field
      //
      return FALSE;
    }
    //
    // make sure menu valid
    //
    MenuLth               = LocalPtr.BootMenu->Header.Length;
    LocalPtr.BootMenuItem = LocalPtr.BootMenu->MenuItem;

    do {
      INTN  MenuItemLen;

      MenuItemLen = LocalPtr.BootMenuItem->DataLen;

      if (MenuItemLen == 0) {
        //
        // missing required field
        //
        return FALSE;
      }

      MenuItemLen += sizeof (*LocalPtr.BootMenuItem) - sizeof (LocalPtr.BootMenuItem->Data);

      MenuLth -= MenuItemLen;
      LocalPtr.BytePtr += MenuItemLen;
    } while (MenuLth > 0);

    if (MenuLth != 0) {
      //
      // missing required field
      //
      return FALSE;
    }
  }

  if (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) {
    DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultBootItem;
  }

  return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
DHCPAckEdit (
  DHCP_RECEIVE_BUFFER *RxBufPtr
  )
{
  return (BOOLEAN) (DHCPOfferAckEdit (RxBufPtr) ? AckEdit (RxBufPtr) : FALSE);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// get an offer/ack
//
EFI_STATUS
GetOfferAck (
  PXE_BASECODE_DEVICE          *Private,
  BOOLEAN                     (*ExtraEdit)(DHCP_RECEIVE_BUFFER *DhcpRxBuf),
  UINT16 OpFlags, // for Udp read
  EFI_IP_ADDRESS *ServerIpPtr,
  EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
  EFI_IP_ADDRESS *ClientIpPtr,
  EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr,
  DHCP_RECEIVE_BUFFER *DhcpRxBuf,
  EFI_EVENT TimeoutEvent
  )
/*++
Routine description:
  Wait for an OFFER/ACK packet.

Parameters:
  Private := Pointer to PxeBc interface
  ExtraEdit := Pointer to extra option checking function
  OpFlags := UdpRead() option flags
  ServerIpPtr :=
  ServerPortPtr :=
  ClientIpPtr :=
  ClientPortPtr :=
  DhcpRxBuf :=
  TimeoutEvent :=

Returns:
--*/
{
  EFI_IP_ADDRESS  ServerIp;
  EFI_STATUS      StatCode;
  INTN            RxBufLen;

  for (;;) {
    //
    // Wait until we get a UDP packet.
    //
    ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));
    RxBufLen = sizeof RxBuf[0].u.ReceiveBuffer;

    if ((StatCode = UdpRead (
                      Private,
                      OpFlags,
                      ClientIpPtr,
                      ClientPortPtr,
                      ServerIpPtr,
                      ServerPortPtr,
                      0,
                      0,
                      (UINTN *) &RxBufLen,
                      &DhcpRxBuf->u.Dhcpv4,
                      TimeoutEvent
                      )) != EFI_SUCCESS) {
      if (StatCode == EFI_TIMEOUT) {
        StatCode = EFI_NO_RESPONSE;
      }

      break;
    }
    //
    // got a packet - see if a good offer
    //
    if (DhcpRxBuf->u.Dhcpv4.op != BOOTP_REPLY) {
      continue;
    }

    if (DhcpRxBuf->u.Dhcpv4.xid != DHCPV4_TRANSMIT_BUFFER.xid) {
      continue;
    }

    if (*(UINT32 *) DHCPV4_TRANSMIT_BUFFER.options != * (UINT32 *) DhcpRxBuf->u.Dhcpv4.options) {
      continue;
    }

    if (*(UINT8 *) &DhcpRxBuf->u.Dhcpv4.yiaddr > 223) {
      continue;
    }

    if (CompareMem (
          DhcpRxBuf->u.Dhcpv4.chaddr,
          DHCPV4_TRANSMIT_BUFFER.chaddr,
          sizeof DhcpRxBuf->u.Dhcpv4.chaddr
          )) {
      //
      // no good
      //
      continue;
    }

    Parse (DhcpRxBuf, RxBufLen);

    if (!(*ExtraEdit) (DhcpRxBuf)) {
      continue;
    }
    //
    // Good DHCP packet.
    //
    StatCode = EFI_SUCCESS;
    break;
  }

  return StatCode;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// get DHCPOFFER's
//
EFI_STATUS
GetOffers (
  PXE_BASECODE_DEVICE *Private
  )
{
  EFI_IP_ADDRESS  ClientIp;
  EFI_IP_ADDRESS  ServerIp;
  EFI_STATUS      StatCode;
  EFI_EVENT       TimeoutEvent;
  INTN            NumOffers;
  INTN            Index;

  //
  //
  //
  ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));
  NumOffers = 0;

  for (Index = 0; Index < (sizeof Private->ServerCount) / sizeof Private->ServerCount[0]; ++Index) {
    Private->ServerCount[Index] = 0;
    Private->GotProxy[Index]    = 0;
  }

  Private->GotBootp               = 0;
  //
  // these we throw away
  //
  Private->GotProxy[DHCP_ONLY_IX] = 1;
  StatCode = gBS->CreateEvent (
                    EVT_TIMER,
                    TPL_CALLBACK,
                    NULL,
                    NULL,
                    &TimeoutEvent
                    );

  if (EFI_ERROR (StatCode)) {
    return StatCode;
  }

  StatCode = gBS->SetTimer (
                    TimeoutEvent,
                    TimerRelative,
                    Private->Timeout * 10000000 + 1000000
                    );

  if (EFI_ERROR (StatCode)) {
    gBS->CloseEvent (TimeoutEvent);
    return StatCode;
  }
  //
  // get offers
  //
  for (;;) {
    DHCP_RECEIVE_BUFFER *DhcpRxBuf;
    UNION_PTR           LocalPtr;

    DhcpRxBuf = &RxBuf[NumOffers];

    if ((
          StatCode = GetOfferAck (
                  Private,
        DHCPOfferAckEdit,
        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP |
        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
        &ServerIp,
        &DhcpServerPort,
        &ClientIp,
        &DHCPClientPort,
        DhcpRxBuf,
        TimeoutEvent
        )
) != EFI_SUCCESS
        ) {
      break;
    }

    LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1];

    //
    // check type of offer
    //
    if (LocalPtr.OpPtr == NULL) {
      //
      // bootp - we only need one and make sure has bootfile
      //
      if (Private->GotBootp || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
        continue;
      }

      Private->GotBootp = (UINT8) (NumOffers + 1);
    }
    //
    // if a DHCP type, must be DHCPOFFER and must have server id
    //
    else if (LocalPtr.MessageType->Type != DHCPOFFER || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]) {
      continue;
    } else {
      INTN  TypeIx;

      //
      // get type - PXE10, WfM11a, or BINL
      //
      if (DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) {
        TypeIx = PXE10_IX;
      } else if (DhcpRxBuf->OpAdds.Status & WfM11a_TYPE) {
        //
        // WfM - make sure it has a bootfile
        //
        if (!DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
          continue;
        }

        TypeIx = WfM11a_IX;
      } else {
        TypeIx = (DhcpRxBuf->OpAdds.Status & PXE_TYPE) ? BINL_IX : DHCP_ONLY_IX;
      }
      //
      // check DHCP or proxy
      //
      if (DhcpRxBuf->u.Dhcpv4.yiaddr == 0) {
        //
        // proxy - only need one of each type if not BINL
        // and must have at least PXE_TYPE
        //
        if (TypeIx == BINL_IX) {
          Private->BinlProxies[Private->GotProxy[BINL_IX]++] = (UINT8) NumOffers;
        } else if (Private->GotProxy[TypeIx]) {
          continue;
        } else {
          Private->GotProxy[TypeIx] = (UINT8) (NumOffers + 1);
        }
      } else {
        Private->OfferCount[TypeIx][Private->ServerCount[TypeIx]++] = (UINT8) NumOffers;
      }
    }

    if (++NumOffers == MAX_OFFERS) {
      break;
    }
  }

  gBS->CloseEvent (TimeoutEvent);
  Private->NumOffersReceived = NumOffers;

  return (Private->NumOffersReceived) ? EFI_SUCCESS : EFI_NO_RESPONSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// send DHCPDECLINE
//
VOID
DeclineOffer (
  PXE_BASECODE_DEVICE *Private
  )
{
  EFI_PXE_BASE_CODE_MODE  *PxebcMode;
  UINT16                  SaveSecs;

  PxebcMode                     = Private->EfiBc.Mode;
  SaveSecs                      = DHCPV4_TRANSMIT_BUFFER.secs;

  DHCPV4_TRANSMIT_BUFFER.secs   = 0;
  DHCPV4_TRANSMIT_BUFFER.flags  = 0;
  SetMem (
    DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opdeclinestr),
    sizeof (DHCPOpStart) - sizeof (struct opdeclinestr),
    OP_PAD
    );
  DHCPDECLINEoptions.DhcpMessageType.Type = DHCPDECLINE;
  CopyMem (&DHCPDECLINEoptions.OpDeclineEnd, &DHCP_REQ_OPTIONS, sizeof (DHCPDECLINEoptions.OpDeclineEnd));

  {
    EFI_IP_ADDRESS  TmpIp;

    CopyMem (&TmpIp, &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, sizeof TmpIp);

    DoUdpWrite (
      Private,
      &TmpIp,
      &DhcpServerPort,
      &PxebcMode->StationIp,
      &DHCPClientPort
      );
  }

  InitDhcpv4TxBuf (Private);
  DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;
  Private->GoodStationIp      = FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// send DHCPRELEASE
//
BOOLEAN
Release (
  PXE_BASECODE_DEVICE *Private
  )
{
  EFI_PXE_BASE_CODE_MODE  *PxebcMode;
  UINT16                  SaveSecs;

  PxebcMode                   = Private->EfiBc.Mode;
  SaveSecs                    = DHCPV4_TRANSMIT_BUFFER.secs;
  DHCPV4_TRANSMIT_BUFFER.secs = 0;

  SetMem (
    DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opreleasestr),
    sizeof (DHCPOpStart) - sizeof (struct opreleasestr),
    OP_PAD
    );

  DHCPRELEASEoptions.DhcpMessageType.Type = DHCPRELEASE;

  CopyMem (
    &DHCPRELEASEoptions.DhcServerIpPtr,
    (DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1],
    sizeof DHCPRELEASEoptions.DhcServerIpPtr
    );

  DHCPRELEASEoptions.End[0] = OP_END;

  {
    EFI_IP_ADDRESS  TmpIp;

    CopyMem (&TmpIp, &DHCPRELEASEoptions.DhcServerIpPtr.Ip, sizeof TmpIp);

    DoUdpWrite (
      Private,
      &TmpIp,
      &DhcpServerPort,
      &PxebcMode->StationIp,
      &DHCPClientPort
      );
  }

  InitDhcpv4TxBuf (Private);

  DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;
  Private->GoodStationIp      = FALSE;
  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
GetBINLAck (
  PXE_BASECODE_DEVICE *Private,
  EFI_IP_ADDRESS      *ServerIpPtr
  )
{
  DHCP_RECEIVE_BUFFER *DhcpRxBuf;
  EFI_STATUS          StatCode;
  EFI_EVENT           TimeoutEvent;

  //
  //
  //
  StatCode = gBS->CreateEvent (
                    EVT_TIMER,
                    TPL_CALLBACK,
                    NULL,
                    NULL,
                    &TimeoutEvent
                    );

  if (EFI_ERROR (StatCode)) {
    return FALSE;
  }

  StatCode = gBS->SetTimer (
                    TimeoutEvent,
                    TimerRelative,
                    Private->Timeout * 10000000 + 1000000
                    );

  if (EFI_ERROR (StatCode)) {
    gBS->CloseEvent (TimeoutEvent);
    return FALSE;
  }
  //
  //
  //
  DhcpRxBuf = &PXE_BINL_BUFFER;

  for (;;) {
    EFI_PXE_BASE_CODE_UDP_PORT  BINLSrvPort;

    BINLSrvPort = 0;

    if (GetOfferAck (
          Private,
          AckEdit,
          EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
          ServerIpPtr,
          &BINLSrvPort,
          &Private->EfiBc.Mode->StationIp,
          &PSEUDO_DHCP_CLIENT_PORT,
          DhcpRxBuf,
          TimeoutEvent
          ) != EFI_SUCCESS) {
      break;
    }
    //
    // make sure from whom we wanted
    //
    if (!DhcpRxBuf->u.Dhcpv4.yiaddr && !CompareMem (
                                          &ServerIpPtr->v4,
                                          &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
                                          sizeof (ServerIpPtr->v4)
                                          )) {
      gBS->CloseEvent (TimeoutEvent);
      //
      // got an ACK from server
      //
      return TRUE;
    }
  }

  gBS->CloseEvent (TimeoutEvent);
  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// make sure we can get BINL
// send DHCPREQUEST to PXE server
//
BOOLEAN
TryBINL (
  PXE_BASECODE_DEVICE *Private,
  INTN                OfferIx
  )
{
  DHCP_RECEIVE_BUFFER *DhcpRxBuf;
  EFI_IP_ADDRESS      ServerIp;
  UINT16              SaveSecs;
  INTN                Index;

  DhcpRxBuf = &RxBuf[OfferIx];

  //
  // use next server address first.
  //
  ServerIp.Addr[0] = DhcpRxBuf->u.Dhcpv4.siaddr;
  if (ServerIp.Addr[0] == 0) {
    //
    // next server address is NULL, use option 54.
    //
    CopyMem (
      ((EFI_IPv4_ADDRESS *) &ServerIp),
      &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
      sizeof (EFI_IPv4_ADDRESS)
      );
  }

  //
  // client IP address - filled in by client if it knows it
  //
  CopyMem (
    ((EFI_IPv4_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr),
    &DHCP_REQ_OPTIONS.OpReqIP.Ip,
    sizeof (EFI_IPv4_ADDRESS)
    );

  SetMem (&DHCP_REQ_OPTIONS, sizeof DHCP_REQ_OPTIONS, OP_PAD);
  DHCPV4_TRANSMIT_BUFFER.flags  = 0;
  DHCPV4_OPTIONS_BUFFER.End[0]  = OP_END;
  AddRouters (Private, DhcpRxBuf);
  SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs;

  for (Index = 0; Index < 3; Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Index) {
    DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);

    //
    // unicast DHCPREQUEST to PXE server
    //
    if (DoUdpWrite (
          Private,
          &ServerIp,
          &PseudoDhcpServerPort,
          (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
          &PSEUDO_DHCP_CLIENT_PORT
          ) != EFI_SUCCESS) {
      break;
    }

    if (!GetBINLAck (Private, &ServerIp)) {
      continue;
    }
    //
    // early exit failures
    // make sure a good ACK
    //
    if (!DHCPOfferAckEdit (&PXE_BINL_BUFFER) || (
          !(PXE_BINL_BUFFER.OpAdds.Status & DISCOVER_TYPE) && !PXE_BINL_BUFFER.OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]
      )
        ) {
      break;
    }

    Private->EfiBc.Mode->ProxyOfferReceived = TRUE;
    return TRUE;
  }
  //
  // failed - reset seconds field, etc.
  //
  Private->EfiBc.Mode->RouteTableEntries = 0;
  //
  // reset
  //
  DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;
  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
TryFinishBINL (
  PXE_BASECODE_DEVICE *Private,
  INTN                OfferIx
  )
{
  if (TryBINL (Private, OfferIx)) {
    return TRUE;
  }

  return Release (Private);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
TryFinishProxyBINL (
  PXE_BASECODE_DEVICE *Private
  )
{
  INTN  Index;

  for (Index = 0; Index < Private->GotProxy[BINL_IX]; ++Index) {
    if (TryBINL (Private, Private->BinlProxies[Index])) {
      return TRUE;
    }
  }

  return Release (Private);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// try to finish DORA - send DHCP request, wait for ACK, check with ARP
//
BOOLEAN
TryFinishDORA (
  PXE_BASECODE_DEVICE *Private,
  INTN                OfferIx
  )
{
  DHCP_RECEIVE_BUFFER *DhcpRxBuf;
  EFI_IP_ADDRESS      ClientIp;
  EFI_IP_ADDRESS      ServerIp;
  EFI_STATUS          StatCode;
  UNION_PTR           LocalPtr;
  EFI_EVENT           TimeoutEvent;

  //
  // send DHCP request
  // if fail return false
  //
  DhcpRxBuf = &DHCPV4_ACK_BUFFER;
  DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type  = DHCPREQUEST;
  CopyMem (&DHCP_REQ_OPTIONS, &RequestOpEndStr, sizeof (DHCP_REQ_OPTIONS));
  DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[OfferIx].u.Dhcpv4.yiaddr;

  CopyMem (
    &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip,
    &((DHCPV4_OP_SERVER_IP *) RxBuf[OfferIx].OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
    sizeof DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip
    );

  CopyMem (
    Private->EfiBc.Mode->SubnetMask.Addr,
    &DefaultSubnetMask,
    4
    );

  //
  // broadcast DHCPREQUEST
  //
  if (DoUdpWrite (
        Private,
        &BroadcastIP,
        &DhcpServerPort,
        (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
        &DHCPClientPort
        ) != EFI_SUCCESS) {
    return FALSE;
  }
  //
  //
  //
  StatCode = gBS->CreateEvent (
                    EVT_TIMER,
                    TPL_CALLBACK,
                    NULL,
                    NULL,
                    &TimeoutEvent
                    );

  if (EFI_ERROR (StatCode)) {
    return FALSE;
  }

  StatCode = gBS->SetTimer (
                    TimeoutEvent,
                    TimerPeriodic,
                    Private->Timeout * 10000000 + 1000000
                    );

  if (EFI_ERROR (StatCode)) {
    gBS->CloseEvent (TimeoutEvent);
    return FALSE;
  }
  //
  // wait for ACK
  //
  for (;;) {
    if (GetOfferAck (
          Private,
          DHCPAckEdit,
          EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP,
          &ServerIp,
          &DhcpServerPort,
          &ClientIp,
          &DHCPClientPort,
          DhcpRxBuf,
          TimeoutEvent
          ) != EFI_SUCCESS) {
      break;
    }
    //
    // check type of response - need DHCPACK
    //
    if (CompareMem (
          &DHCP_REQ_OPTIONS.OpReqIP.Ip,
          &DhcpRxBuf->u.Dhcpv4.yiaddr,
          sizeof (EFI_IPv4_ADDRESS)
          ) || CompareMem (
          &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip,
          &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
          sizeof (EFI_IPv4_ADDRESS)
          )) {
      continue;
    }
    //
    // got ACK
    // check with ARP that IP unused - good return true
    //
    if (!SetStationIP (Private)) {
      //
      // fail - send DHCPDECLINE and return false
      //
      DeclineOffer (Private);
      break;
    }

    LocalPtr.OpPtr = DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1];

    if (LocalPtr.OpPtr != NULL) {
      CopyMem (
        (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask,
        &LocalPtr.SubnetMaskStr->Ip,
        sizeof (EFI_IPv4_ADDRESS)
        );
    }

    AddRouters (Private, DhcpRxBuf);
    gBS->CloseEvent (TimeoutEvent);
    return TRUE;
  }

  gBS->CloseEvent (TimeoutEvent);
  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// try a DHCP server of appropriate type
//
BOOLEAN
TryDHCPFinishDORA (
  PXE_BASECODE_DEVICE *Private,
  INTN                TypeIx
  )
{
  INTN  Index;

  //
  // go through the DHCP servers of the requested type
  //
  for (Index = 0; Index < Private->ServerCount[TypeIx]; ++Index) {
    if (TryFinishDORA (Private, Index = Private->OfferCount[TypeIx][Index])) {
      if (TypeIx == BINL_IX && !TryFinishBINL (Private, Index)) {
        continue;
      }

      return TRUE;
    }
  }

  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// try a DHCP only server and a proxy of appropriate type
//
BOOLEAN
TryProxyFinishDORA (
  PXE_BASECODE_DEVICE *Private,
  INTN                TypeIx
  )
{
  INTN  Index;

  if (!Private->GotProxy[TypeIx]) {
    //
    // no proxies of the type wanted
    //
    return FALSE;
  }
  //
  // go through the DHCP only servers
  //
  for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) {
    if (TryFinishDORA (Private, Private->OfferCount[DHCP_ONLY_IX][Index])) {
      if (TypeIx != BINL_IX) {
        CopyProxyRxBuf (Private, Private->GotProxy[TypeIx] - 1);
      } else if (!TryFinishProxyBINL (Private)) {
        //
        // if didn't work with this DHCP, won't work with any
        //
        return FALSE;
      }

      return TRUE;
    }
  }

  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// getting to the bottom of the barrel
//
BOOLEAN
TryAnyWithBootfileFinishDORA (
  PXE_BASECODE_DEVICE *Private
  )
{
  //
  // try a DHCP only server who has a bootfile
  //
  UNION_PTR LocalPtr;
  INTN      Index;

  for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) {
    INTN  offer;

    offer = Private->OfferCount[DHCP_ONLY_IX][Index];

    if (RxBuf[offer].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && TryFinishDORA (Private, offer)) {
      return TRUE;
    }
  }
  //
  // really at bottom - see if be have any bootps
  //
  if (!Private->GotBootp) {
    return FALSE;
  }

  DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[Private->GotBootp - 1].u.Dhcpv4.yiaddr;

  if (!SetStationIP (Private)) {
    return FALSE;
  }
  //
  // treat BOOTP response as DHCP ACK packet
  //
  CopyParseRxBuf (Private, Private->GotBootp - 1, DHCPV4_ACK_INDEX);

  LocalPtr.OpPtr = RxBuf[Private->GotBootp - 1].OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1];

  if (LocalPtr.OpPtr != NULL) {
    *(EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask = LocalPtr.SubnetMaskStr->Ip;
  }

  return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/* DoDhcpDora()
 */
EFI_STATUS
DoDhcpDora (
  PXE_BASECODE_DEVICE *Private,
  BOOLEAN             SortOffers
  )
{
  EFI_PXE_BASE_CODE_IP_FILTER Filter;
  EFI_STATUS                  StatCode;
  INTN                        NumOffers;

  Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;

  Filter.IpCnt    = 0;
  Filter.reserved = 0;

  //
  // set filter unicast or broadcast
  //
  if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
    return StatCode;
  }
  //
  // seed random number with hardware address
  //
  SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress);

  for (Private->Timeout = 1;
       Private->Timeout < 17;
       Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), Private->Timeout <<= 1
      ) {
    INTN  Index;

    InitDhcpv4TxBuf (Private);
    DHCPV4_TRANSMIT_BUFFER.xid  = Random (Private);
    DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);

    //
    // broadcast DHCPDISCOVER
    //
    StatCode = DoUdpWrite (
                Private,
                &BroadcastIP,
                &DhcpServerPort,
                (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
                &DHCPClientPort
                );

    if (StatCode != EFI_SUCCESS) {
      return StatCode;
    }

    CopyMem (
      &Private->EfiBc.Mode->DhcpDiscover,
      (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER,
      sizeof (EFI_PXE_BASE_CODE_PACKET)
      );

    //
    // get DHCPOFFER's
    //
    if ((StatCode = GetOffers (Private)) != EFI_SUCCESS) {
      if (StatCode != EFI_NO_RESPONSE) {
        return StatCode;
      }

      continue;
    }
    //
    // select offer and reply DHCPREQUEST
    //
    if (SortOffers) {
      if (TryDHCPFinishDORA(Private, PXE10_IX) || // try DHCP with PXE10
        TryDHCPFinishDORA(Private, WfM11a_IX)  || // no - try with WfM
        TryProxyFinishDORA(Private, PXE10_IX)  || // no - try DHCP only and proxy with PXE10
        TryProxyFinishDORA(Private, WfM11a_IX) || // no - try DHCP only and proxy with WfM
        TryDHCPFinishDORA(Private, BINL_IX)    || // no - try with WfM
        TryProxyFinishDORA(Private, BINL_IX)   || // no - try DHCP only and proxy with PXE10
        TryAnyWithBootfileFinishDORA(Private))
      {
        return EFI_SUCCESS;
      }

      continue;
    }
    //
    // FIFO order
    //
    NumOffers = Private->NumOffersReceived;

    for (Index = 0; Index < NumOffers; ++Index) {
      //
      // ignore proxies
      //
      if (!RxBuf[Index].u.Dhcpv4.yiaddr) {
        continue;
      }
      //
      // check if a bootp server
      //
      if (!RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]) {
        //
        // it is - just check ARP
        //
        if (!SetStationIP (Private)) {
          continue;
        }
      }
      //
      // else check if a DHCP only server
      //
      else if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE))) {
        //
        // it is a normal DHCP offer (without any PXE options), just finish the D.O.R.A by sending DHCP request.
        //
        if (!TryFinishDORA (Private, Index)) {
          continue;
        }
      } else if (TryFinishDORA (Private, Index)) {
        if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) && !TryFinishBINL (Private, Index)) {
          continue;
        }
      }

      DEBUG ((DEBUG_WARN, "\nDoDhcpDora()  Got packets.  "));
      return EFI_SUCCESS;
    }
    //
    // now look for DHCP onlys and a Proxy
    //
    for (Index = 0; Index < NumOffers; ++Index) {
      INTN  Index2;

      //
      // ignore proxies, bootps, non DHCP onlys, and bootable DHCPS
      //
      if (!RxBuf[Index].u.Dhcpv4.yiaddr ||
          !RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1] ||
          RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE) ||
          RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]
          ) {
        continue;
      }
      //
      // found non bootable DHCP only - try to find a proxy
      //
      for (Index2 = 0; Index2 < NumOffers; ++Index2) {
        if (!RxBuf[Index2].u.Dhcpv4.yiaddr) {
          if (!TryFinishDORA (Private, Index)) {
            //
            // DHCP no ACK
            //
            break;
          }

          if (RxBuf[Index2].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) {
            CopyProxyRxBuf (Private, Index2);
          } else if (!TryFinishBINL (Private, Index2)) {
            continue;
          }

          DEBUG ((DEBUG_WARN, "\nDoDhcpDora()  Got packets.  "));
          return EFI_SUCCESS;
        }
      }
    }
  }

  return EFI_NO_RESPONSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// determine if the server ip is in the ip list
//
BOOLEAN
InServerList (
  EFI_IP_ADDRESS    *ServerIpPtr,
  PXE_SERVER_LISTS  *ServerListPtr
  )
{
  UINTN Index;

  if (!ServerListPtr || !ServerListPtr->Ipv4List.IpCount) {
    return TRUE;
  }

  for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) {
    if (!CompareMem (
          ServerIpPtr,
          &ServerListPtr->Ipv4List.IpList[Index],
          sizeof (EFI_IPv4_ADDRESS)
          )) {
      return TRUE;
    }
  }

  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
ExtractBootServerList (
  UINT16            Type,
  DHCPV4_OP_STRUCT  *ptr,
  PXE_SERVER_LISTS  **ServerListPtr
  )
{
  UNION_PTR LocalPtr;
  INTN      ServerListLen;

  LocalPtr.OpPtr  = ptr;
  ServerListLen   = LocalPtr.BootServersStr->Header.Length;

  //
  // find type
  //
  LocalPtr.BootServerList = LocalPtr.BootServersStr->ServerList;

  while (ServerListLen) {
    INTN  ServerEntryLen;

    ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (LocalPtr.BootServerList->u.Ipv4List.IpCount - 1) *
    sizeof (EFI_IPv4_ADDRESS);

    if (NTOHS (LocalPtr.BootServerList->Type) == Type) {
      *ServerListPtr = &LocalPtr.BootServerList->u;
      return TRUE;
    }

    (LocalPtr.BytePtr) += ServerEntryLen;
    ServerListLen -= ServerEntryLen;
  }

  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
FreeMem (
  PXE_BASECODE_DEVICE *Private
  )
{
  if (Private->TransmitBuffer != NULL) {
    gBS->FreePool (Private->TransmitBuffer);
    Private->TransmitBuffer = NULL;
  }

  if (Private->ReceiveBuffers != NULL) {
    gBS->FreePool (Private->ReceiveBuffers);
    Private->ReceiveBuffers = NULL;
  }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
GetMem (
  PXE_BASECODE_DEVICE *Private
  )
{
  EFI_STATUS  Status;

  if (Private->DhcpPacketBuffer == NULL) {
    Status = gBS->AllocatePool (
                    EfiBootServicesData,
                    sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1),
                    &Private->DhcpPacketBuffer
                    );

    if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) {
      Private->DhcpPacketBuffer = NULL;
      FreeMem (Private);
      return FALSE;
    }
  }

  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  sizeof (EFI_PXE_BASE_CODE_PACKET),
                  &Private->TransmitBuffer
                  );

  if (EFI_ERROR (Status) || Private->TransmitBuffer == NULL) {
    gBS->FreePool (Private->DhcpPacketBuffer);
    Private->DhcpPacketBuffer = NULL;
    Private->TransmitBuffer   = NULL;
    FreeMem (Private);
    return FALSE;
  }

  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  sizeof (DHCP_RECEIVE_BUFFER) * (MAX_OFFERS),
                  &Private->ReceiveBuffers
                  );

  if (EFI_ERROR (Status) || Private->ReceiveBuffers == NULL) {
    gBS->FreePool (Private->TransmitBuffer);
    gBS->FreePool (Private->DhcpPacketBuffer);
    Private->DhcpPacketBuffer = NULL;
    Private->TransmitBuffer   = NULL;
    Private->ReceiveBuffers   = NULL;
    FreeMem (Private);
    return FALSE;
  }

  return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/**


**/
EFI_STATUS
EFIAPI
BcDhcp (
  IN EFI_PXE_BASE_CODE_PROTOCOL *This,
  IN BOOLEAN                    SortOffers
  )
{
  EFI_PXE_BASE_CODE_IP_FILTER Filter;
  EFI_PXE_BASE_CODE_MODE      *PxebcMode;
  PXE_BASECODE_DEVICE         *Private;
  EFI_STATUS                  StatCode;

  //
  // Lock the instance data and make sure started
  //
  StatCode = EFI_SUCCESS;

  if (This == NULL) {
    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
    return EFI_INVALID_PARAMETER;
  }

  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);

  if (Private == NULL) {
    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL"));
    return EFI_INVALID_PARAMETER;
  }

  EfiAcquireLock (&Private->Lock);

  if (This->Mode == NULL || !This->Mode->Started) {
    DEBUG ((DEBUG_ERROR, "BC was not started."));
    EfiReleaseLock (&Private->Lock);
    return EFI_NOT_STARTED;
  }

  Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
  Filter.IpCnt    = 0;
  Filter.reserved = 0;

  DEBUG ((DEBUG_INFO, "\nBcDhcp()  Enter.  "));

  PxebcMode = Private->EfiBc.Mode;

  if (!GetMem (Private)) {
    DEBUG ((DEBUG_ERROR, "\nBcDhcp()  GetMem() failed.\n"));
    EfiReleaseLock (&Private->Lock);
    return EFI_OUT_OF_RESOURCES;
  }

  PxebcMode->DhcpDiscoverValid = FALSE;
  PxebcMode->DhcpAckReceived = FALSE;
  PxebcMode->ProxyOfferReceived = FALSE;

  Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP;

  //
  // Issue BC command
  //
  if (Private->TotalSeconds == 0) {
    //
    // put in seconds field of DHCP send packets
    //
    Private->TotalSeconds = 4;
  }

  if ((StatCode = DoDhcpDora (Private, SortOffers)) == EFI_SUCCESS) {
    //
    // success - copy packets
    //
    PxebcMode->DhcpDiscoverValid = PxebcMode->DhcpAckReceived = TRUE;

    CopyMem (
      &PxebcMode->DhcpAck,
      (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_ACK_PACKET,
      sizeof (EFI_PXE_BASE_CODE_PACKET)
      );

    if (PxebcMode->ProxyOfferReceived) {
      CopyMem (
        &PxebcMode->ProxyOffer,
        (EFI_PXE_BASE_CODE_PACKET *) &PXE_OFFER_PACKET,
        sizeof (EFI_PXE_BASE_CODE_PACKET)
        );
    }
  }
  //
  // set filter back to unicast
  //
  IpFilter (Private, &Filter);

  FreeMem (Private);

  //
  // Unlock the instance data
  //
  DEBUG ((DEBUG_WARN, "\nBcDhcp()  Exit = %xh  ", StatCode));

  EfiReleaseLock (&Private->Lock);
  return StatCode;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOLEAN
VerifyCredentialOption (
  UINT8 *tx,
  UINT8 *rx
  )
{
  UINTN n;

  //
  // Fail verification if either pointer is NULL.
  //
  if (tx == NULL || rx == NULL) {
    return FALSE;
  }
  //
  // Fail verification if tx[0] is not a credential type option
  // or if the length is zero or not a multiple of four.
  //
  if (tx[0] != VEND_PXE_CREDENTIAL_TYPES || tx[1] == 0 || tx[1] % 4 != 0) {
    return FALSE;
  }
  //
  // Fail verification if rx[0] is not a credential type option
  // or if the length is not equal to four.
  //
  if (rx[0] != VEND_PXE_CREDENTIAL_TYPES || rx[1] != 4) {
    return FALSE;
  }
  //
  // Look through transmitted credential types for a copy
  // of the received credential type.
  //
  for (n = 0; n < tx[1]; n += 4) {
    if (!CompareMem (&tx[n + 2], &rx[2], 4)) {
      return TRUE;
    }
  }

  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/**


**/
EFI_STATUS
DoDiscover (
  PXE_BASECODE_DEVICE *Private,
  UINT16              OpFlags,
  IN UINT16           Type,
  IN UINT16           *LayerPtr,
  IN BOOLEAN          UseBis,
  EFI_IP_ADDRESS      *DestPtr,
  PXE_SERVER_LISTS    *ServerListPtr
  )
{
  EFI_PXE_BASE_CODE_UDP_PORT  ClientPort;
  EFI_PXE_BASE_CODE_UDP_PORT  ServerPort;
  EFI_PXE_BASE_CODE_MODE      *PxebcMode;
  EFI_STATUS                  StatCode;
  EFI_EVENT                   TimeoutEvent;
  UINT8                       OpLen;

  PxebcMode = Private->EfiBc.Mode;

  if (DestPtr->Addr[0] == 0) {
    DEBUG ((DEBUG_WARN, "\nDoDiscover()  !DestPtr->Addr[0]"));
    return EFI_INVALID_PARAMETER;
  }
  //
  // seed random number with hardware address
  //
  SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress);

  if (DestPtr->Addr[0] == BroadcastIP.Addr[0]) {
    ClientPort  = DHCPClientPort;
    ServerPort  = DhcpServerPort;
  } else {
    ClientPort  = PSEUDO_DHCP_CLIENT_PORT;
    ServerPort  = PseudoDhcpServerPort;
  }

  if (UseBis) {
    *LayerPtr |= PXE_BOOT_LAYER_CREDENTIAL_FLAG;
  } else {
    *LayerPtr &= PXE_BOOT_LAYER_MASK;
  }

  for (Private->Timeout = 1;
       Private->Timeout < 5;
       Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Private->Timeout
      ) {
    InitDhcpv4TxBuf (Private);
    //
    // initialize DHCP message structure
    //
    DHCPV4_TRANSMIT_BUFFER.xid  = Random (Private);
    DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);
    CopyMem (
      &DHCPV4_TRANSMIT_BUFFER.ciaddr,
      &PxebcMode->StationIp,
      sizeof DHCPV4_TRANSMIT_BUFFER.ciaddr
      );

    DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type  = DHCPREQUEST;
    DISCOVERoptions.Header.OpCode               = OP_VENDOR_SPECIFIC;
    DISCOVERoptions.BootItem.Header.OpCode      = VEND_PXE_BOOT_ITEM;
    DISCOVERoptions.BootItem.Header.Length      = DHCPV4_OPTION_LENGTH (PXE_OP_BOOT_ITEM);
    DISCOVERoptions.BootItem.Type               = HTONS (Type);
    DISCOVERoptions.BootItem.Layer              = HTONS (*LayerPtr);

    if (UseBis) {
      EFI_BIS_PROTOCOL        *BisPtr;
      BIS_APPLICATION_HANDLE  BisAppHandle;
      EFI_BIS_DATA            *BisDataSigInfo;
      EFI_BIS_SIGNATURE_INFO  *BisSigInfo;
      UINTN                   Index;
      UINTN                   Index2;

      BisPtr = PxebcBisStart (
                Private,
                &BisAppHandle,
                &BisDataSigInfo
                );

      if (BisPtr == NULL) {
        //
        // %%TBD - In order to get here, BIS must have
        // been present when PXEBC.Start() was called.
        // BIS had to be shutdown/removed/damaged
        // before PXEBC.Discover() was called.
        // Do we need to document a specific error
        // for this case?
        //
        return EFI_OUT_OF_RESOURCES;
      }
      //
      // Compute number of credential types.
      //
      Index2                        = BisDataSigInfo->Length / sizeof (EFI_BIS_SIGNATURE_INFO);

      DISCREDoptions.Header.OpCode  = VEND_PXE_CREDENTIAL_TYPES;

      DISCREDoptions.Header.Length  = (UINT8) (Index2 * sizeof (PXE_CREDENTIAL));

      OpLen = (UINT8) (DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS) + sizeof (DHCPV4_OP_HEADER) + DISCREDoptions.Header.Length);

      BisSigInfo = (EFI_BIS_SIGNATURE_INFO *) BisDataSigInfo->Data;

      for (Index = 0; Index < Index2; ++Index) {
        UINT32  x;

        CopyMem (&x, &BisSigInfo[Index], sizeof x);
        x = HTONL (x);
        CopyMem (&DISCREDoptions.Credentials[Index], &x, sizeof x);
      }

      PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo);
    } else {
      OpLen = DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS);
    }

    DISCOVERoptions.Header.Length = OpLen;

    ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen - 1] = OP_END;
    ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen]     = OP_END;

    StatCode = DoUdpWrite (
                Private,
                DestPtr,
                &ServerPort,
                (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
                &ClientPort
                );

    if (StatCode != EFI_SUCCESS) {
      return StatCode;
    }
    //
    //
    //
    StatCode = gBS->CreateEvent (
                      EVT_TIMER,
                      TPL_CALLBACK,
                      NULL,
                      NULL,
                      &TimeoutEvent
                      );

    if (EFI_ERROR (StatCode)) {
      return StatCode;
    }

    StatCode = gBS->SetTimer (
                      TimeoutEvent,
                      TimerRelative,
                      Private->Timeout * 10000000 + 1000000
                      );

    if (EFI_ERROR (StatCode)) {
      gBS->CloseEvent (TimeoutEvent);
      return StatCode;
    }
    //
    // wait for ACK
    //
    for (;;) {
      DHCP_RECEIVE_BUFFER *RxBufPtr;
      UINT16              TmpType;
      UINT16              TmpLayer;

      RxBufPtr = UseBis ? &PXE_BIS_BUFFER : &PXE_ACK_BUFFER;
      ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));

      if (GetOfferAck (
            Private,
            AckEdit,
            OpFlags,
            (EFI_IP_ADDRESS *) &Private->ServerIp,
            0,
            (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
            &ClientPort,
            RxBufPtr,
            TimeoutEvent
            ) != EFI_SUCCESS) {
        break;
      }
      //
      // check type of response - need PXEClient DHCPACK of proper type with bootfile
      //
      if (!(RxBufPtr->OpAdds.Status & PXE_TYPE) ||
          (UseBis && (RxBufPtr->OpAdds.Status & USE_THREE_BYTE)) ||
          !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] ||
          !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1] ||
          !InServerList((EFI_IP_ADDRESS *)&((DHCPV4_OP_SERVER_IP *)RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX-1])->Ip, ServerListPtr)) {

        continue;
      }

      TmpType = TmpLayer = 0;

      if (RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) {
        TmpType = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Type);

        if (RxBufPtr->OpAdds.Status & USE_THREE_BYTE) {
          TmpLayer = (UINT16) (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer >> 8);
        } else {
          TmpLayer = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer);
        }
      }

      if (TmpType != Type) {
        continue;
      }

      if (UseBis) {
        if (!RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]) {
          continue;
        }

        if (!VerifyCredentialOption (
              (UINT8 *) &DISCREDoptions.Header,
              (UINT8 *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]
              )) {
          continue;
        }
      }

      *LayerPtr = TmpLayer;

      if (UseBis) {
        CopyMem (
          &PxebcMode->PxeBisReply,
          &RxBufPtr->u.Dhcpv4,
          sizeof (EFI_PXE_BASE_CODE_PACKET)
          );

        PxebcMode->PxeBisReplyReceived = TRUE;

        StatCode = DoDiscover (
                    Private,
                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
                    Type,
                    LayerPtr,
                    FALSE,
                    &Private->ServerIp,
                    0
                    );

        gBS->CloseEvent (TimeoutEvent);
        return StatCode;
      }

      PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = TRUE;

      CopyMem (
        &PxebcMode->PxeDiscover,
        &*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER,
        sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER)
        );

      CopyMem (
        &PxebcMode->PxeReply,
        &*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4,
        sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4)
        );

      AddRouters (Private, RxBufPtr);

      gBS->CloseEvent (TimeoutEvent);
      return EFI_SUCCESS;
    }

    gBS->CloseEvent (TimeoutEvent);
  }
  //
  // end for loop
  //
  return EFI_TIMEOUT;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/**
  Parameters:
  Private := Pointer to PxeBc interface
  Type :=
  LayerPtr :=
  UseBis :=
  DiscoverInfoPtr :=
  McastServerListPtr :=
  ServerListPtr :=


**/
EFI_STATUS
Discover (
  PXE_BASECODE_DEVICE                 *Private,
  IN UINT16                           Type,
  IN UINT16                           *LayerPtr,
  IN BOOLEAN                          UseBis,
  IN EFI_PXE_BASE_CODE_DISCOVER_INFO  *DiscoverInfoPtr,
  PXE_SERVER_LISTS                    *McastServerListPtr,
  PXE_SERVER_LISTS                    *ServerListPtr
  )
{
  EFI_IP_ADDRESS  DestIp;
  EFI_STATUS      StatCode;

  DEBUG ((DEBUG_INFO, "\nDiscover()  Type=%d  Layer=%d  ", Type, *LayerPtr));

  if (UseBis) {
    DEBUG ((DEBUG_INFO, "BIS  "));
  }
  //
  // get dest IP addr - mcast, bcast, or unicast
  //
  if (DiscoverInfoPtr->UseMCast) {
    DestIp.v4 = DiscoverInfoPtr->ServerMCastIp.v4;

    DEBUG (
      (DEBUG_INFO,
      "\nDiscover()  MCast %d.%d.%d.%d  ",
      DestIp.v4.Addr[0],
      DestIp.v4.Addr[1],
      DestIp.v4.Addr[2],
      DestIp.v4.Addr[3])
      );

    if ((StatCode = DoDiscover (
                      Private,
                      EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
                      Type,
                      LayerPtr,
                      UseBis,
                      &DestIp,
                      McastServerListPtr
                      )) != EFI_TIMEOUT) {
      DEBUG (
        (DEBUG_WARN,
        "\nDiscover()  status == %r (%Xh)",
        StatCode,
        StatCode)
        );

      return StatCode;
    }
  }

  if (DiscoverInfoPtr->UseBCast) {
    DEBUG ((DEBUG_INFO, "\nDiscver()  BCast  "));

    if ((StatCode = DoDiscover (
                      Private,
                      EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
                      Type,
                      LayerPtr,
                      UseBis,
                      &BroadcastIP,
                      McastServerListPtr
                      )) != EFI_TIMEOUT) {

      DEBUG ((DEBUG_WARN, "\nDiscover()  status == %r (%Xh)", StatCode, StatCode));

      return StatCode;
    }
  }

  if (DiscoverInfoPtr->UseUCast) {
    UINTN Index;

    DEBUG (
      (DEBUG_INFO,
      "\nDiscover()  UCast  IP#=%d  ",
      ServerListPtr->Ipv4List.IpCount)
      );

    for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) {
      CopyMem (&DestIp, &ServerListPtr->Ipv4List.IpList[Index], 4);

      DEBUG (
        (DEBUG_INFO,
        "\nDiscover()  UCast %d.%d.%d.%d  ",
        DestIp.v4.Addr[0],
        DestIp.v4.Addr[1],
        DestIp.v4.Addr[2],
        DestIp.v4.Addr[3])
        );

      if ((StatCode = DoDiscover (
                        Private,
                        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
                        Type,
                        LayerPtr,
                        UseBis,
                        &DestIp,
                        0
                        )) != EFI_TIMEOUT) {
        DEBUG (
          (DEBUG_WARN,
          "\nDiscover()  status == %r (%Xh)",
          StatCode,
          StatCode)
          );

        return StatCode;
      }
    }
  }

  DEBUG ((DEBUG_WARN, "\nDiscover()  TIMEOUT"));

  return EFI_TIMEOUT;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/* BcDiscover()
 */

/**


**/
EFI_STATUS
EFIAPI
BcDiscover (
  IN EFI_PXE_BASE_CODE_PROTOCOL       * This,
  IN UINT16                           Type,
  IN UINT16                           *LayerPtr,
  IN BOOLEAN                          UseBis,
  IN EFI_PXE_BASE_CODE_DISCOVER_INFO  * DiscoverInfoPtr OPTIONAL
  )
{
  EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;
  EFI_PXE_BASE_CODE_MODE          *PxebcMode;
  DHCP_RECEIVE_BUFFER             *DhcpRxBuf;
  PXE_SERVER_LISTS                DefaultSrvList;
  PXE_SERVER_LISTS                *ServerListPtr;
  PXE_SERVER_LISTS                *McastServerListPtr;
  UNION_PTR                       LocalPtr;
  UINTN                           Index;
  UINTN                           Index2;
  BOOLEAN                         AcquiredSrvList;
  EFI_STATUS                      StatCode;
  PXE_BASECODE_DEVICE             *Private;

  //
  // Lock the instance data and make sure started
  //
  StatCode = EFI_SUCCESS;

  if (This == NULL) {
    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
    return EFI_INVALID_PARAMETER;
  }

  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);

  if (Private == NULL) {
    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
    return EFI_INVALID_PARAMETER;
  }

  EfiAcquireLock (&Private->Lock);

  if (This->Mode == NULL || !This->Mode->Started) {
    DEBUG ((DEBUG_ERROR, "BC was not started."));
    EfiReleaseLock (&Private->Lock);
    return EFI_NOT_STARTED;
  }

  ServerListPtr       = NULL;
  McastServerListPtr  = NULL;
  AcquiredSrvList     = FALSE;

  PxebcMode           = Private->EfiBc.Mode;

  if (!GetMem (Private)) {
    EfiReleaseLock (&Private->Lock);
    return EFI_OUT_OF_RESOURCES;
  }

  if (UseBis) {
    if (!PxebcMode->BisSupported) {
      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }
  }

  Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;

  if (Private->TotalSeconds == 0) {
    //
    // put in seconds field of DHCP send packets
    //
    Private->TotalSeconds = 4;
  }

  ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO));

  //
  // if layer number not zero, use previous discover
  //
  if (*LayerPtr != 0) {
    DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0"));

    if (DiscoverInfoPtr != NULL) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && DiscoverInfoPtr != NULL\n"));

      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }

    if (!PxebcMode->PxeDiscoverValid) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && PxeDiscoverValid == 0\n"));

      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }

    if (!PxebcMode->PxeReplyReceived) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && PxeReplyReceived == 0\n"));

      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }

    if (UseBis && !PxebcMode->PxeBisReplyReceived) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && PxeBisReplyReceived == 0\n"));

      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }

    DefaultInfo.UseUCast            = TRUE;
    DiscoverInfoPtr                 = &DefaultInfo;

    DefaultSrvList.Ipv4List.IpCount = 1;
    CopyMem (&DefaultSrvList.Ipv4List.IpList[0], &Private->ServerIp, 4);

    ServerListPtr = &DefaultSrvList;
  }
  //
  // layer is zero - see if info is supplied or if we need to use info from a cached offer
  //
  else if (!DiscoverInfoPtr) {
    //
    // not supplied - generate it
    // make sure that there is cached, appropriate information
    // if neither DhcpAck packet nor ProxyOffer packet has pxe info, fail
    //
    DhcpRxBuf = (PxebcMode->ProxyOfferReceived) ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;

    if (!PxebcMode->DhcpAckReceived || !(DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE)) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  !ack && !proxy"));
      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }

    DiscoverInfoPtr = &DefaultInfo;

    LocalPtr.OpPtr  = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];

    //
    // if multicast enabled, need multicast address
    //
    if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST)) {
      DefaultInfo.UseMCast = TRUE;

      CopyMem (
        ((EFI_IPv4_ADDRESS *) &DefaultInfo.ServerMCastIp),
        &((DHCPV4_OP_IP_ADDRESS *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Ip,
        sizeof (EFI_IPv4_ADDRESS)
        );
    }

    DefaultInfo.UseBCast    = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & DISABLE_BCAST) == 0);

    DefaultInfo.MustUseList = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) != 0);

    DefaultInfo.UseUCast = (BOOLEAN)
      (
        (DefaultInfo.MustUseList) ||
        ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST))
      );

    if ((DefaultInfo.UseUCast | DefaultInfo.MustUseList) && !ExtractBootServerList (
                                                              Type,
                                                              DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1],
                                                              &ServerListPtr
                                                              )) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  type not in list"));
      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }
  }
  //
  // Info supplied - make SrvList if required
  // if we use ucast discovery or must use list, there better be one
  //
  else if (DiscoverInfoPtr->UseUCast || DiscoverInfoPtr->MustUseList) {
    //
    // there better be a list
    //
    if (DiscoverInfoPtr->IpCnt == 0) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  no bootserver list"));
      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }
    //
    // get its size
    //
    for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) {
      if (DiscoverInfoPtr->SrvList[Index].Type == Type) {
        if (DiscoverInfoPtr->SrvList[Index].AcceptAnyResponse) {
          if (Index2 != 0) {
            DEBUG ((DEBUG_WARN, "\nBcDiscover()  accept any?"));
            EfiReleaseLock (&Private->Lock);
            return EFI_INVALID_PARAMETER;
          } else {
            Index2                          = 1;
            DefaultSrvList.Ipv4List.IpCount = 0;
            ServerListPtr                   = &DefaultSrvList;
            break;
          }
        } else {
          ++Index2;
        }
      }
    }

    if (Index2 == 0) {
      DEBUG ((DEBUG_WARN, "\nBcDiscover()  !Index2?"));
      EfiReleaseLock (&Private->Lock);
      return EFI_INVALID_PARAMETER;
    }

    if (ServerListPtr == NULL) {
      ServerListPtr = AllocatePool (
                        sizeof (PXEV4_SERVER_LIST) + (Index2 - 1) * sizeof (EFI_IPv4_ADDRESS)
                      );

      if (ServerListPtr == NULL) {
        EfiReleaseLock (&Private->Lock);
        return EFI_OUT_OF_RESOURCES;
      }
      //
      // build an array of IP addresses from the server list
      //
      AcquiredSrvList                 = TRUE;
      ServerListPtr->Ipv4List.IpCount = (UINT8) Index2;

      for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) {
        if (DiscoverInfoPtr->SrvList[Index].Type == Type) {
          CopyMem (
            &ServerListPtr->Ipv4List.IpList[Index2++],
            &DiscoverInfoPtr->SrvList[Index].IpAddr.v4,
            sizeof ServerListPtr->Ipv4List.IpList[0]
            );
        }
      }
    }
  }

  if (DiscoverInfoPtr->MustUseList) {
    McastServerListPtr = ServerListPtr;
  }

  if (!(DiscoverInfoPtr->UseMCast || DiscoverInfoPtr->UseBCast || DiscoverInfoPtr->UseUCast)) {
    DEBUG ((DEBUG_WARN, "\nBcDiscover()  Nothing to use!\n"));

    EfiReleaseLock (&Private->Lock);
    return EFI_INVALID_PARAMETER;
  }

  PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = PxebcMode->PxeBisReplyReceived = FALSE;

  StatCode = Discover (
              Private,
              Type,
              LayerPtr,
              UseBis,
              DiscoverInfoPtr,
              McastServerListPtr,
              ServerListPtr
              );

  if (AcquiredSrvList) {
    gBS->FreePool (ServerListPtr);
  }

  FreeMem (Private);

  //
  // Unlock the instance data
  //
  DEBUG (
    (DEBUG_INFO,
    "\nBcDiscover()  status == %r (%Xh)\n",
    StatCode,
    StatCode)
    );

  EfiReleaseLock (&Private->Lock);
  return StatCode;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/**


**/
EFI_STATUS
EFIAPI
BcSetPackets (
  IN EFI_PXE_BASE_CODE_PROTOCOL   * This,
  BOOLEAN                         *NewDhcpDiscoverValid, OPTIONAL
  BOOLEAN                         *NewDhcpAckReceived, OPTIONAL
  BOOLEAN                         *NewProxyOfferReceived, OPTIONAL
  BOOLEAN                         *NewPxeDiscoverValid, OPTIONAL
  BOOLEAN                         *NewPxeReplyReceived, OPTIONAL
  BOOLEAN                         *NewPxeBisReplyReceived, OPTIONAL
  IN EFI_PXE_BASE_CODE_PACKET     * NewDhcpDiscover, OPTIONAL
  IN EFI_PXE_BASE_CODE_PACKET     * NewDhcpAck, OPTIONAL
  IN EFI_PXE_BASE_CODE_PACKET     * NewProxyOffer, OPTIONAL
  IN EFI_PXE_BASE_CODE_PACKET     * NewPxeDiscover, OPTIONAL
  IN EFI_PXE_BASE_CODE_PACKET     * NewPxeReply, OPTIONAL
  IN EFI_PXE_BASE_CODE_PACKET     * NewPxeBisReply OPTIONAL
  )
{
  EFI_PXE_BASE_CODE_MODE  *PxebcMode;
  EFI_STATUS              Status;
  PXE_BASECODE_DEVICE     *Private;

  //
  // Lock the instance data and make sure started
  //

  if (This == NULL) {
    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
    return EFI_INVALID_PARAMETER;
  }

  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);

  if (Private == NULL) {
    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
    return EFI_INVALID_PARAMETER;
  }

  EfiAcquireLock (&Private->Lock);

  if (This->Mode == NULL || !This->Mode->Started) {
    DEBUG ((DEBUG_ERROR, "BC was not started."));
    EfiReleaseLock (&Private->Lock);
    return EFI_NOT_STARTED;
  }

  PxebcMode = Private->EfiBc.Mode;

  if (Private->DhcpPacketBuffer == NULL) {
    Status = gBS->AllocatePool (
                    EfiBootServicesData,
                    sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1),
                    &Private->DhcpPacketBuffer
                    );

    if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) {
      Private->DhcpPacketBuffer = NULL;
      EfiReleaseLock (&Private->Lock);
      return EFI_OUT_OF_RESOURCES;
    }
  }
  //
  // Issue BC command
  //
  //
  // reset
  //
  Private->FileSize = 0;
  if (NewDhcpDiscoverValid != NULL) {
    PxebcMode->DhcpDiscoverValid = *NewDhcpDiscoverValid;
  }

  if (NewDhcpAckReceived != NULL) {
    PxebcMode->DhcpAckReceived = *NewDhcpAckReceived;
  }

  if (NewProxyOfferReceived != NULL) {
    PxebcMode->ProxyOfferReceived = *NewProxyOfferReceived;
  }

  if (NewPxeDiscoverValid != NULL) {
    PxebcMode->PxeDiscoverValid = *NewPxeDiscoverValid;
  }

  if (NewPxeReplyReceived != NULL) {
    PxebcMode->PxeReplyReceived = *NewPxeReplyReceived;
  }

  if (NewPxeBisReplyReceived != NULL) {
    PxebcMode->PxeBisReplyReceived = *NewPxeBisReplyReceived;
  }

  if (NewDhcpDiscover != NULL) {
    CopyMem (
      &PxebcMode->DhcpDiscover,
      NewDhcpDiscover,
      sizeof *NewDhcpDiscover
      );
  }

  if (NewDhcpAck != NULL) {
    CopyParse (Private, &PxebcMode->DhcpAck, NewDhcpAck, DHCPV4_ACK_INDEX);
  }

  if (NewProxyOffer != NULL) {
    CopyParse (Private, &PxebcMode->ProxyOffer, NewProxyOffer, PXE_OFFER_INDEX);
  }

  if (NewPxeDiscover != NULL) {
    CopyMem (
      &PxebcMode->PxeDiscover,
      NewPxeDiscover,
      sizeof *NewPxeDiscover
      );
  }

  if (NewPxeReply != NULL) {
    CopyParse (Private, &PxebcMode->PxeReply, NewPxeReply, PXE_ACK_INDEX);
  }

  if (NewPxeBisReply != NULL) {
    CopyParse (Private, &PxebcMode->PxeBisReply, NewPxeBisReply, PXE_BIS_INDEX);
  }
  //
  // Unlock the instance data
  //
  EfiReleaseLock (&Private->Lock);
  return EFI_SUCCESS;
}

/* eof - pxe_bc_dhcp.c */
