]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c
Import ArpDxe, Dhcp4Dxe, Ip4Dxe, Mtftp4Dxe, PxeBcDxe and PxeDhcp4Dxe.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / PxeDhcp4Dxe / Support.c
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c
new file mode 100644 (file)
index 0000000..2e75052
--- /dev/null
@@ -0,0 +1,1086 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2005, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  support.c\r
+\r
+Abstract:\r
+  Miscellaneous support routines for PxeDhcp4 protocol.\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+#define DebugPrint(x)\r
+//\r
+// #define DebugPrint(x) Aprint x\r
+//\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+UINT16\r
+htons (\r
+  UINTN n\r
+  )\r
+{\r
+  return (UINT16) ((n >> 8) | (n << 8));\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+UINT32\r
+htonl (\r
+  UINTN n\r
+  )\r
+{\r
+  return (UINT32) ((n >> 24) | ((n >> 8) & 0xFF00) | ((n & 0xFF00) << 8) | (n << 24));\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+EFIAPI\r
+timeout_notify (\r
+  IN EFI_EVENT Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  ASSERT (Context);\r
+\r
+  if (Context != NULL) {\r
+    ((PXE_DHCP4_PRIVATE_DATA *) Context)->TimeoutOccurred = TRUE;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+EFIAPI\r
+periodic_notify (\r
+  IN EFI_EVENT Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  ASSERT (Context);\r
+\r
+  if (Context != NULL) {\r
+    ((PXE_DHCP4_PRIVATE_DATA *) Context)->PeriodicOccurred = TRUE;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS := Option was found\r
+  @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL\r
+  @return EFI_INVALID_PARAMETER := OpCode == DHCP4_PAD\r
+  @return EFI_INVALID_PARAMETER := OpCode == DHCP4_END && Skip != 0\r
+  @return EFI_INVALID_PARAMETER := DHCP magik number in Packet is not valid\r
+  @return EFI_NOT_FOUND := op-code was not found in packet\r
+  @return EFI_INVALID_PARAMETER := If present, DHCP_MAX_MESSAGE_SIZE option\r
+  @return does not have a valid value.\r
+\r
+**/\r
+EFI_STATUS\r
+find_opt (\r
+  IN DHCP4_PACKET *Packet,\r
+  IN UINT8        OpCode,\r
+  IN UINTN        Skip,\r
+  OUT DHCP4_OP    **OpPtr\r
+  )\r
+{\r
+  UINTN msg_size;\r
+  UINTN buf_len;\r
+  UINTN n;\r
+  UINT8 *buf;\r
+  UINT8 *end_ptr;\r
+  UINT8 overload;\r
+\r
+  //\r
+  // Verify parameters.\r
+  //\r
+  if (Packet == NULL || OpPtr == NULL || OpCode == DHCP4_PAD || (OpCode == DHCP4_END && Skip != 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet->dhcp4.magik != htonl (DHCP4_MAGIK_NUMBER)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Initialize search variables.\r
+  //\r
+  *OpPtr    = NULL;\r
+\r
+  msg_size  = DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);\r
+\r
+  overload  = 0;\r
+  end_ptr   = NULL;\r
+\r
+  buf       = Packet->dhcp4.options;\r
+  buf_len   = msg_size - (Packet->dhcp4.options - Packet->raw);\r
+\r
+  //\r
+  // Start searching for requested option.\r
+  //\r
+  for (n = 0;;) {\r
+    //\r
+    // If match is found, decrement skip count and return\r
+    // when desired match is found.\r
+    //\r
+    if (buf[n] == OpCode) {\r
+      *OpPtr = (DHCP4_OP *) &buf[n];\r
+\r
+      if (Skip-- == 0) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+    //\r
+    // Skip past current option.  Check for option overload\r
+    // and message size options since these will affect the\r
+    // amount of data to be searched.\r
+    //\r
+    switch (buf[n]) {\r
+    case DHCP4_PAD:\r
+      //\r
+      // Remember the first pad byte of a group.  This\r
+      // could be the end of a badly formed packet.\r
+      //\r
+      if (end_ptr == NULL) {\r
+        end_ptr = &buf[n];\r
+      }\r
+\r
+      ++n;\r
+      break;\r
+\r
+    case DHCP4_END:\r
+      //\r
+      // If we reach the end we are done.\r
+      //\r
+      end_ptr = NULL;\r
+      return EFI_NOT_FOUND;\r
+\r
+    case DHCP4_OPTION_OVERLOAD:\r
+      //\r
+      // Remember the option overload value since it\r
+      // could cause the search to continue into\r
+      // the fname and sname fields.\r
+      //\r
+      end_ptr = NULL;\r
+\r
+      if (buf[n + 1] == 1) {\r
+        overload = buf[n + 2];\r
+      }\r
+\r
+      n += 2 + buf[n + 1];\r
+      break;\r
+\r
+    case DHCP4_MAX_MESSAGE_SIZE:\r
+      //\r
+      // Remember the message size value since it could\r
+      // change the amount of option buffer to search.\r
+      //\r
+      end_ptr = NULL;\r
+\r
+      if (buf[n + 1] == 2 && buf == Packet->dhcp4.options) {\r
+        msg_size = ((buf[n + 2] << 8) | buf[n + 3]) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);\r
+\r
+        if (msg_size < 328) {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+\r
+        buf_len = msg_size - (Packet->dhcp4.options - Packet->raw);\r
+\r
+        if (n + 2 + buf[n + 1] > buf_len) {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+      }\r
+\r
+    /* fall thru */\r
+    default:\r
+      end_ptr = NULL;\r
+\r
+      n += 2 + buf[n + 1];\r
+    }\r
+    //\r
+    // Keep searching until the end of the buffer is reached.\r
+    //\r
+    if (n < buf_len) {\r
+      continue;\r
+    }\r
+    //\r
+    // Reached end of current buffer.  Check if we are supposed\r
+    // to search the fname and sname buffers.\r
+    //\r
+    if (buf == Packet->dhcp4.options &&\r
+        (overload == DHCP4_OVERLOAD_FNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)\r
+        ) {\r
+      buf     = Packet->dhcp4.fname;\r
+      buf_len = 128;\r
+      n       = 0;\r
+      continue;\r
+    }\r
+\r
+    if (buf != Packet->dhcp4.sname && (overload == DHCP4_OVERLOAD_SNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)) {\r
+      buf     = Packet->dhcp4.sname;\r
+      buf_len = 64;\r
+      n       = 0;\r
+      continue;\r
+    }\r
+    //\r
+    // End of last buffer reached.  If this was a search\r
+    // for the end of the options, go back to the start\r
+    // of the current pad block.\r
+    //\r
+    if (OpCode == DHCP4_END && end_ptr != NULL) {\r
+      *OpPtr = (DHCP4_OP *) end_ptr;\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    return EFI_NOT_FOUND;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL\r
+  @return EFI_INVALID_PARAMETER := OpPtr->op == DHCP4_PAD || OpPtr->op == DHCP4_END\r
+  @return EFI_INVALID_PARAMETER := DHCP magik number in DHCP packet is not valid\r
+  @return EFI_INVALID_PARAMETER := If DHCP_MAX_MESSAGE_SIZE option is present and\r
+  @return is not valid\r
+  @return EFI_INVALID_PARAMETER := If DHCP_OPTION_OVERLOAD option is present and\r
+  @return is not valid\r
+  @return EFI_DEVICE_ERROR := Cannot determine end of packet\r
+  @return EFI_BUFFER_TOO_SMALL := Not enough room in packet to add option\r
+  @return EFI_SUCCESS := Option added to DHCP packet\r
+\r
+**/\r
+EFI_STATUS\r
+add_opt (\r
+  IN DHCP4_PACKET *Packet,\r
+  IN DHCP4_OP     *OpPtr\r
+  )\r
+{\r
+  EFI_STATUS  efi_status;\r
+  DHCP4_OP    *msg_size_op;\r
+  DHCP4_OP    *overload_op;\r
+  DHCP4_OP    *op;\r
+  UINTN       msg_size;\r
+  UINTN       buf_len;\r
+  UINT32      magik;\r
+  UINT8       *buf;\r
+\r
+  //\r
+  // Verify parameters.\r
+  //\r
+  ASSERT (Packet);\r
+  ASSERT (OpPtr);\r
+\r
+  if (Packet == NULL || OpPtr == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  switch (OpPtr->op) {\r
+  case DHCP4_PAD:\r
+  case DHCP4_END:\r
+    //\r
+    // No adding PAD or END.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Check the DHCP magik number.\r
+  //\r
+  CopyMem (&magik, &Packet->dhcp4.magik, 4);\r
+\r
+  if (magik != htonl (DHCP4_MAGIK_NUMBER)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Find the DHCP message size option.\r
+  //\r
+  msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;\r
+\r
+  efi_status = find_opt (\r
+                Packet,\r
+                DHCP4_MAX_MESSAGE_SIZE,\r
+                0,\r
+                &msg_size_op\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    if (efi_status != EFI_NOT_FOUND) {\r
+      DebugPrint (\r
+        ("%s:%d:%r\n",\r
+        __FILE__,\r
+        __LINE__,\r
+        efi_status)\r
+        );\r
+      return efi_status;\r
+    }\r
+\r
+    msg_size_op = NULL;\r
+  } else {\r
+    CopyMem (&msg_size, msg_size_op->data, 2);\r
+    msg_size = htons (msg_size);\r
+\r
+    if (msg_size < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+  //\r
+  // Find the DHCP option overload option.\r
+  //\r
+  efi_status = find_opt (\r
+                Packet,\r
+                DHCP4_OPTION_OVERLOAD,\r
+                0,\r
+                &overload_op\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    if (efi_status != EFI_NOT_FOUND) {\r
+      DebugPrint (\r
+        ("%s:%d:%r\n",\r
+        __FILE__,\r
+        __LINE__,\r
+        efi_status)\r
+        );\r
+      return efi_status;\r
+    }\r
+\r
+    overload_op = NULL;\r
+  } else {\r
+    if (overload_op->len != 1) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    switch (overload_op->data[0]) {\r
+    case 1:\r
+    case 2:\r
+    case 3:\r
+      break;\r
+\r
+    default:\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+  //\r
+  // Find the end of the packet.\r
+  //\r
+  efi_status = find_opt (Packet, DHCP4_END, 0, &op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Find which buffer the end is in.\r
+  //\r
+  if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.options)) {\r
+    buf_len = (msg_size - ((UINT8 *) &Packet->dhcp4.options - (UINT8 *) &Packet->raw)) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);\r
+  } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.fname)) {\r
+    buf_len = 128;\r
+  } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.sname)) {\r
+    buf_len = 64;\r
+  } else {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Add option to current buffer if there is no overlow.\r
+  //\r
+  if ((UINTN) ((&op->op - buf) + 3 + op->len) < buf_len) {\r
+    CopyMem (op, OpPtr, OpPtr->len + 2);\r
+\r
+    op->data[op->len] = DHCP4_END;\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Error if there is no space for option.\r
+  //\r
+  return EFI_BUFFER_TOO_SMALL;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER := Private == NULL || Private->PxeBc == NULL\r
+  @return EFI_INVALID_PARAMETER := Only one of StationIp and SubnetMask is given\r
+  @return EFI_SUCCESS := UDP stack is ready\r
+  @return other := Error from PxeBc->SetIpFilter() or PxeBc->SetStationIp()\r
+\r
+**/\r
+EFI_STATUS\r
+start_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *StationIp,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *SubnetMask\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_IP_FILTER bcast_filter;\r
+  EFI_STATUS                  efi_status;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (StationIp != NULL && SubnetMask == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (StationIp == NULL && SubnetMask != NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Setup broadcast receive filter...\r
+  //\r
+  ZeroMem (&bcast_filter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+\r
+  bcast_filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;\r
+  bcast_filter.IpCnt    = 0;\r
+\r
+  efi_status = Private->PxeBc->SetIpFilter (\r
+                                Private->PxeBc,\r
+                                &bcast_filter\r
+                                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Configure station IP address and subnet mask...\r
+  //\r
+  efi_status = Private->PxeBc->SetStationIp (\r
+                                Private->PxeBc,\r
+                                StationIp,\r
+                                SubnetMask\r
+                                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+  }\r
+\r
+  return efi_status;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+stop_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private\r
+  )\r
+{\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (Private->PxeBc);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+start_receive_events (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN UINTN                  SecondsTimeout\r
+  )\r
+{\r
+  EFI_STATUS  efi_status;\r
+  UINTN       random;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (SecondsTimeout);\r
+\r
+  if (Private == NULL || SecondsTimeout == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Need a bettern randomizer...\r
+  // For now adjust the timeout value by the least significant\r
+  // digit in the MAC address.\r
+  //\r
+  random = 0;\r
+\r
+  if (Private->PxeDhcp4.Data != NULL) {\r
+    if (Private->PxeDhcp4.Data->Discover.dhcp4.hlen != 0 && Private->PxeDhcp4.Data->Discover.dhcp4.hlen <= 16) {\r
+      random = 0xFFF & Private->PxeDhcp4.Data->Discover.dhcp4.chaddr[Private->PxeDhcp4.Data->Discover.dhcp4.hlen - 1];\r
+    }\r
+  }\r
+  //\r
+  // Setup timeout event and start timer.\r
+  //\r
+  efi_status = gBS->CreateEvent (\r
+                      EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                      TPL_NOTIFY,\r
+                      &timeout_notify,\r
+                      Private,\r
+                      &Private->TimeoutEvent\r
+                      );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+\r
+  efi_status = gBS->SetTimer (\r
+                      Private->TimeoutEvent,\r
+                      TimerRelative,\r
+                      SecondsTimeout * 10000000 + random\r
+                      );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    gBS->CloseEvent (Private->TimeoutEvent);\r
+    return efi_status;\r
+  }\r
+\r
+  Private->TimeoutOccurred = FALSE;\r
+\r
+  //\r
+  // Setup periodic event for callbacks\r
+  //\r
+  efi_status = gBS->CreateEvent (\r
+                      EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                      TPL_NOTIFY,\r
+                      &periodic_notify,\r
+                      Private,\r
+                      &Private->PeriodicEvent\r
+                      );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    gBS->CloseEvent (Private->TimeoutEvent);\r
+    return efi_status;\r
+  }\r
+\r
+  efi_status = gBS->SetTimer (\r
+                      Private->PeriodicEvent,\r
+                      TimerPeriodic,\r
+                      1000000\r
+                      );  /* 1/10th second */\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    gBS->CloseEvent (Private->TimeoutEvent);\r
+    gBS->CloseEvent (Private->PeriodicEvent);\r
+    return efi_status;\r
+  }\r
+\r
+  Private->PeriodicOccurred = FALSE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+stop_receive_events (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private\r
+  )\r
+{\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+\r
+  if (Private == NULL) {\r
+    return ;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  gBS->CloseEvent (Private->TimeoutEvent);\r
+  Private->TimeoutOccurred = FALSE;\r
+\r
+  //\r
+  //\r
+  //\r
+  gBS->CloseEvent (Private->PeriodicEvent);\r
+  Private->PeriodicOccurred = FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER := Private == NULL || dest_ip == NULL ||\r
+  @return buffer == NULL || BufferSize < 300 || Private->PxeBc == NULL\r
+  @return EFI_SUCCESS := Buffer was transmitted\r
+  @return other := Return from PxeBc->UdpWrite()\r
+\r
+**/\r
+EFI_STATUS\r
+tx_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN EFI_IP_ADDRESS         *dest_ip,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *gateway_ip,\r
+  IN EFI_IP_ADDRESS         *src_ip,\r
+  IN VOID                   *buffer,\r
+  IN UINTN                  BufferSize\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT  dest_port;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  src_port;\r
+  EFI_IP_ADDRESS              zero_ip;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (dest_ip);\r
+  ASSERT (buffer);\r
+  ASSERT (BufferSize >= 300);\r
+\r
+  if (Private == NULL || dest_ip == NULL || buffer == NULL || BufferSize < 300) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Transmit DHCP discover packet...\r
+  //\r
+  ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));\r
+\r
+  if (src_ip == NULL) {\r
+    src_ip = &zero_ip;\r
+  }\r
+\r
+  dest_port = DHCP4_SERVER_PORT;\r
+  src_port  = DHCP4_CLIENT_PORT;\r
+\r
+  return Private->PxeBc->UdpWrite (\r
+                          Private->PxeBc,\r
+                          EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
+                          dest_ip,\r
+                          &dest_port,\r
+                          gateway_ip,\r
+                          src_ip,\r
+                          &src_port,\r
+                          NULL,\r
+                          NULL,\r
+                          &BufferSize,\r
+                          buffer\r
+                          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER :=\r
+  @return EFI_SUCCESS := Packet received\r
+  @return other := Return from PxeBc->UdpRead()\r
+\r
+**/\r
+EFI_STATUS\r
+rx_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  OUT VOID                  *buffer,\r
+  IN OUT UINTN              *BufferSize,\r
+  IN OUT EFI_IP_ADDRESS     *dest_ip,\r
+  IN OUT EFI_IP_ADDRESS     *src_ip,\r
+  IN UINT16                 op_flags\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT  dest_port;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  src_port;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (buffer);\r
+  ASSERT (dest_ip);\r
+  ASSERT (src_ip);\r
+\r
+  if (Private == NULL || buffer == NULL || dest_ip == NULL || src_ip == NULL || BufferSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Check for packet\r
+  //\r
+  *BufferSize = sizeof (DHCP4_PACKET);\r
+\r
+  dest_port   = DHCP4_CLIENT_PORT;\r
+  src_port    = DHCP4_SERVER_PORT;\r
+\r
+  return Private->PxeBc->UdpRead (\r
+                          Private->PxeBc,\r
+                          op_flags,\r
+                          dest_ip,\r
+                          &dest_port,\r
+                          src_ip,\r
+                          &src_port,\r
+                          NULL,\r
+                          NULL,\r
+                          BufferSize,\r
+                          buffer\r
+                          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+tx_rx_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN OUT EFI_IP_ADDRESS     *ServerIp,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *gateway_ip,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *client_ip,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *SubnetMask,\r
+  IN DHCP4_PACKET           *tx_pkt,\r
+  OUT DHCP4_PACKET          *rx_pkt,\r
+  IN INTN (*rx_vfy)(\r
+      IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+      IN DHCP4_PACKET *tx_pkt,\r
+      IN DHCP4_PACKET *rx_pkt,\r
+      IN UINTN rx_pkt_size\r
+    ),\r
+  IN UINTN SecondsTimeout\r
+  )\r
+/*++\r
+Routine description:\r
+  Transmit DHCP packet and wait for replies.\r
+\r
+Parameters:\r
+  Private := Pointer to PxeDhcp4 private data\r
+  ServerIp := Pointer to server IP address\r
+  gateway_ip := Pointer to gateway IP address or NULL\r
+  client_ip := Pointer to client IP address or NULL\r
+  SubnetMask := Pointer to subnet mask or NULL\r
+  tx_pkt := Pointer to DHCP packet to transmit\r
+  rx_pkt := Pointer to DHCP packet receive buffer\r
+  rx_vfy := Pointer to DHCP packet receive verification routine\r
+  SecondsTimeout := Number of seconds until timeout\r
+\r
+Returns:\r
+  EFI_INVALID_PARAMETER := Private == NULL || ServerIp == NULL ||\r
+    tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL || Private->PxeBc == NULL\r
+  EFI_ABORTED := Receive aborted\r
+  EFI_TIMEOUT := No packets received\r
+  EFI_SUCCESS := Packet(s) received\r
+  other := Returns from other PxeDhcp4 support routines\r
+--*/\r
+{\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS CallbackStatus;\r
+  EFI_IP_ADDRESS                dest_ip;\r
+  EFI_IP_ADDRESS                src_ip;\r
+  EFI_STATUS                    efi_status;\r
+  DHCP4_OP                      *msg_size_op;\r
+  UINTN                         pkt_size;\r
+  UINTN                         n;\r
+  UINT16                        msg_size;\r
+  UINT16                        op_flags;\r
+  BOOLEAN                       done_flag;\r
+  BOOLEAN                       got_packet;\r
+\r
+  //\r
+  // Bad programmer check...\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (ServerIp);\r
+  ASSERT (tx_pkt);\r
+  ASSERT (rx_pkt);\r
+  ASSERT (rx_vfy);\r
+\r
+  if (Private == NULL || ServerIp == NULL || tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Enable UDP...\r
+  //\r
+  efi_status = start_udp (Private, client_ip, SubnetMask);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Get length of transmit packet...\r
+  //\r
+  msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;\r
+\r
+  efi_status = find_opt (\r
+                tx_pkt,\r
+                DHCP4_MAX_MESSAGE_SIZE,\r
+                0,\r
+                &msg_size_op\r
+                );\r
+\r
+  if (!EFI_ERROR (efi_status)) {\r
+    CopyMem (&msg_size, msg_size_op->data, 2);\r
+\r
+    if ((msg_size = htons (msg_size)) < 328) {\r
+      msg_size = 328;\r
+    }\r
+  }\r
+  //\r
+  // Transmit packet...\r
+  //\r
+  efi_status = tx_udp (\r
+                Private,\r
+                ServerIp,\r
+                gateway_ip,\r
+                client_ip,\r
+                tx_pkt,\r
+                msg_size - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE)\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    stop_udp (Private);\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Enable periodic and timeout events...\r
+  //\r
+  efi_status = start_receive_events (Private, SecondsTimeout);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    stop_udp (Private);\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Wait for packet(s)...\r
+  //\r
+#if 0\r
+  if (!client_ip) {\r
+    Aprint ("client_ip == NULL    ");\r
+  } else {\r
+    Aprint (\r
+      "client_ip == %d.%d.%d.%d    ",\r
+      client_ip->v4.Addr[0],\r
+      client_ip->v4.Addr[1],\r
+      client_ip->v4.Addr[2],\r
+      client_ip->v4.Addr[3]\r
+      );\r
+  }\r
+\r
+  if (!ServerIp) {\r
+    Aprint ("ServerIp == NULL\n");\r
+  } else {\r
+    Aprint (\r
+      "ServerIp == %d.%d.%d.%d\n",\r
+      ServerIp->v4.Addr[0],\r
+      ServerIp->v4.Addr[1],\r
+      ServerIp->v4.Addr[2],\r
+      ServerIp->v4.Addr[3]\r
+      );\r
+  }\r
+#endif\r
+\r
+  done_flag   = FALSE;\r
+  got_packet  = FALSE;\r
+\r
+  while (!done_flag) {\r
+    //\r
+    // Check for timeout event...\r
+    //\r
+    if (Private->TimeoutOccurred) {\r
+      efi_status = EFI_SUCCESS;\r
+      break;\r
+    }\r
+    //\r
+    // Check for periodic event...\r
+    //\r
+    if (Private->PeriodicOccurred && Private->callback != NULL) {\r
+      CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE;\r
+\r
+      if (Private->callback->Callback != NULL) {\r
+        CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, 0, NULL);\r
+      }\r
+\r
+      switch (CallbackStatus) {\r
+      case EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE:\r
+        break;\r
+\r
+      case EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT:\r
+      default:\r
+        stop_receive_events (Private);\r
+        stop_udp (Private);\r
+        return EFI_ABORTED;\r
+      }\r
+\r
+      Private->PeriodicOccurred = FALSE;\r
+    }\r
+    //\r
+    // Check for packet...\r
+    //\r
+    if (client_ip == NULL) {\r
+      SetMem (&dest_ip, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+    } else {\r
+      CopyMem (&dest_ip, client_ip, sizeof (EFI_IP_ADDRESS));\r
+    }\r
+\r
+    SetMem (&src_ip, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+\r
+    if (CompareMem (&src_ip, &ServerIp, sizeof (EFI_IP_ADDRESS))) {\r
+      ZeroMem (&src_ip, sizeof (EFI_IP_ADDRESS));\r
+      op_flags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP;\r
+    } else {\r
+      op_flags = 0;\r
+    }\r
+\r
+    efi_status = rx_udp (\r
+                  Private,\r
+                  rx_pkt,\r
+                  &pkt_size,\r
+                  &dest_ip,\r
+                  &src_ip,\r
+                  op_flags\r
+                  );\r
+\r
+    if (efi_status == EFI_TIMEOUT) {\r
+      efi_status = EFI_SUCCESS;\r
+      continue;\r
+    }\r
+\r
+    if (EFI_ERROR (efi_status)) {\r
+      break;\r
+    }\r
+    //\r
+    // Some basic packet sanity checks..\r
+    //\r
+    if (pkt_size < 300) {\r
+      continue;\r
+    }\r
+\r
+    if (rx_pkt->dhcp4.op != BOOTP_REPLY) {\r
+      continue;\r
+    }\r
+\r
+    if (tx_pkt->dhcp4.htype != rx_pkt->dhcp4.htype) {\r
+      continue;\r
+    }\r
+\r
+    if ((n = tx_pkt->dhcp4.hlen) != rx_pkt->dhcp4.hlen) {\r
+      continue;\r
+    }\r
+\r
+    if (CompareMem (&tx_pkt->dhcp4.xid, &rx_pkt->dhcp4.xid, 4)) {\r
+      continue;\r
+    }\r
+\r
+    if (n != 0) {\r
+      if (n >= 16) {\r
+        n = 16;\r
+      }\r
+\r
+      if (CompareMem (tx_pkt->dhcp4.chaddr, rx_pkt->dhcp4.chaddr, n)) {\r
+        continue;\r
+      }\r
+    }\r
+    //\r
+    // Internal callback packet verification...\r
+    //\r
+    switch ((*rx_vfy) (Private, tx_pkt, rx_pkt, pkt_size)) {\r
+    case -2:  /* ignore and stop */\r
+      stop_receive_events (Private);\r
+      stop_udp (Private);\r
+      return EFI_ABORTED;\r
+\r
+    case -1:  /* ignore and wait */\r
+      continue;\r
+\r
+    case 0:   /* accept and wait */\r
+      break;\r
+\r
+    case 1:   /* accept and stop */\r
+      done_flag = TRUE;\r
+      break;\r
+\r
+    default:\r
+      ASSERT (0);\r
+    }\r
+    //\r
+    // External callback packet verification...\r
+    //\r
+    CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE;\r
+\r
+    if (Private->callback != NULL) {\r
+      if (Private->callback->Callback != NULL) {\r
+        CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, (UINT32) pkt_size, rx_pkt);\r
+      }\r
+    }\r
+\r
+    switch (CallbackStatus) {\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE:\r
+      continue;\r
+\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT:\r
+      done_flag = TRUE;\r
+      break;\r
+\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT:\r
+      stop_receive_events (Private);\r
+      stop_udp (Private);\r
+      return EFI_ABORTED;\r
+\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE:\r
+    default:\r
+      break;\r
+    }\r
+    //\r
+    // We did!  We did get a packet!\r
+    //\r
+    got_packet = TRUE;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  stop_receive_events (Private);\r
+  stop_udp (Private);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+\r
+  if (got_packet) {\r
+    return EFI_SUCCESS;\r
+  } else {\r
+    return EFI_TIMEOUT;\r
+  }\r
+}\r
+\r
+/* eof - support.c */\r