]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
Sync the latest version from R8.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Impl.c
index e8e9b383f88d1f086e64e016dcaebe7fe81d4fab..50f1a2fbbec979d46a32d601c63782df0389c2cb 100644 (file)
@@ -57,11 +57,7 @@ EfiDhcp4GetModeData (
   }\r
 \r
   Instance = DHCP_INSTANCE_FROM_THIS (This);\r
-\r
-  if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
-\r
+  \r
   OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
   DhcpSb  = Instance->Service;\r
 \r
@@ -766,6 +762,226 @@ EfiDhcp4Build (
            );\r
 }\r
 \r
+STATIC\r
+EFI_STATUS\r
+Dhcp4InstanceConfigUdpIo (\r
+  IN UDP_IO_PORT  *UdpIo,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  DHCP_PROTOCOL                     *Instance;\r
+  DHCP_SERVICE                      *DhcpSb;\r
+  EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;\r
+  EFI_UDP4_CONFIG_DATA              UdpConfigData;\r
+  IP4_ADDR                          Ip;\r
+\r
+  Instance = (DHCP_PROTOCOL *) Context;\r
+  DhcpSb   = Instance->Service;\r
+  Token    = Instance->Token;\r
+\r
+  NetZeroMem (&UdpConfigData, sizeof (EFI_UDP4_CONFIG_DATA));\r
+\r
+  UdpConfigData.AcceptBroadcast    = TRUE;\r
+  UdpConfigData.AllowDuplicatePort = TRUE;\r
+  UdpConfigData.TimeToLive         = 64;\r
+  UdpConfigData.DoNotFragment      = TRUE;\r
+\r
+  Ip = HTONL (DhcpSb->ClientAddr);\r
+  NetCopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Ip = HTONL (DhcpSb->Netmask);\r
+  NetCopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {\r
+    UdpConfigData.StationPort = DHCP_CLIENT_PORT;\r
+  } else {\r
+    UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;\r
+  }\r
+\r
+  return UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+Dhcp4InstanceCreateUdpIo (\r
+  IN DHCP_PROTOCOL  *Instance\r
+  )\r
+{\r
+  DHCP_SERVICE  *DhcpSb;\r
+\r
+  ASSERT (Instance->Token != NULL);\r
+\r
+  DhcpSb          = Instance->Service;\r
+  Instance->UdpIo = UdpIoCreatePort (DhcpSb->Controller, DhcpSb->Image, Dhcp4InstanceConfigUdpIo, Instance);\r
+  if (Instance->UdpIo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  } else {\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+STATIC\r
+VOID\r
+DhcpDummyExtFree (\r
+  IN VOID                   *Arg\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Release the packet.\r
+\r
+Arguments:\r
+\r
+  Arg - The packet to release\r
+\r
+Returns:\r
+\r
+  None\r
+\r
+--*/\r
+{  \r
+}\r
+\r
+VOID\r
+PxeDhcpInput (\r
+  NET_BUF                   *UdpPacket,\r
+  UDP_POINTS                *Points,\r
+  EFI_STATUS                IoStatus,\r
+  VOID                      *Context\r
+  )\r
+{\r
+  DHCP_PROTOCOL                     *Instance;\r
+  DHCP_SERVICE                      *DhcpSb;\r
+  EFI_DHCP4_HEADER                  *Head;\r
+  NET_BUF                           *Wrap;\r
+  EFI_DHCP4_PACKET                  *Packet;\r
+  EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;\r
+  UINT32                            Len;\r
+  EFI_STATUS                        Status;\r
+\r
+  Wrap     = NULL;\r
+  Instance = (DHCP_PROTOCOL *) Context;\r
+  Token    = Instance->Token;\r
+  DhcpSb   = Instance->Service;\r
+\r
+  //\r
+  // Don't restart receive if error occurs or DHCP is destoried.\r
+  //\r
+  if (EFI_ERROR (IoStatus)) {\r
+    return ;\r
+  }\r
+\r
+  ASSERT (UdpPacket != NULL);\r
+  \r
+  //\r
+  // Validate the packet received\r
+  //\r
+  if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {\r
+    goto RESTART;\r
+  }\r
+  \r
+  //\r
+  // Copy the DHCP message to a continuous memory block, make the buffer size\r
+  // of the EFI_DHCP4_PACKET a multiple of 4-byte.\r
+  //\r
+  Len  = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);\r
+  Wrap = NetbufAlloc (Len);\r
+\r
+  if (Wrap == NULL) {\r
+    goto RESTART;\r
+  }\r
+\r
+  Packet         = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);\r
+  Packet->Size   = Len;\r
+  Head           = &Packet->Dhcp4.Header;\r
+  Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);\r
+\r
+  if (Packet->Length != UdpPacket->TotalSize) {\r
+    goto RESTART;\r
+  }\r
+  \r
+  //\r
+  // Is this packet the answer to our packet?\r
+  //\r
+  if ((Head->OpCode != BOOTP_REPLY) ||\r
+      (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||\r
+      !NET_MAC_EQUAL (&DhcpSb->Mac, Head->ClientHwAddr, DhcpSb->HwLen)) {\r
+    goto RESTART;\r
+  }\r
+  \r
+  //\r
+  // Validate the options and retrieve the interested options\r
+  //\r
+  if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&\r
+      (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&\r
+      EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {\r
+\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Keep this packet in the ResponseQueue.\r
+  //\r
+  NET_GET_REF (Wrap);\r
+  NetbufQueAppend (&Instance->ResponseQueue, Wrap);\r
+\r
+RESTART:\r
+\r
+  NetbufFree (UdpPacket);\r
+\r
+  if (Wrap != NULL) {\r
+    NetbufFree (Wrap);\r
+  }\r
+\r
+  Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);\r
+  if (EFI_ERROR (Status)) {\r
+    PxeDhcpDone (Instance);\r
+  }\r
+}\r
+\r
+VOID\r
+PxeDhcpDone (\r
+  IN DHCP_PROTOCOL  *Instance\r
+  )\r
+{\r
+  EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;\r
+\r
+  Token = Instance->Token;\r
+\r
+  Token->ResponseCount = Instance->ResponseQueue.BufNum;\r
+  if (Token->ResponseCount != 0) {\r
+    Token->ResponseList = (EFI_DHCP4_PACKET *) NetAllocatePool (Instance->ResponseQueue.BufSize);\r
+    if (Token->ResponseList == NULL) {\r
+      Token->Status = EFI_OUT_OF_RESOURCES;\r
+      goto SIGNAL_USER;\r
+    }\r
+\r
+    //\r
+    // Copy the recieved DHCP responses.\r
+    //\r
+    NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);\r
+    Token->Status = EFI_SUCCESS;\r
+  } else {\r
+    Token->ResponseList = NULL;\r
+    Token->Status       = EFI_TIMEOUT;\r
+  }\r
+\r
+SIGNAL_USER:\r
+  //\r
+  // Clean the resources dedicated for this transmit receive transaction.\r
+  //\r
+  NetbufQueFlush (&Instance->ResponseQueue);\r
+  UdpIoCleanPort (Instance->UdpIo);\r
+  UdpIoFreePort (Instance->UdpIo);\r
+  Instance->UdpIo = NULL;\r
+  Instance->Token = NULL;\r
+\r
+  if (Token->CompletionEvent != NULL) {\r
+    gBS->SignalEvent (Token->CompletionEvent);\r
+  }  \r
+}\r
+\r
 \r
 /**\r
   Transmit and receive a packet through this DHCP service.\r
@@ -785,10 +1001,144 @@ EfiDhcp4TransmitReceive (
   IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token\r
   )\r
 {\r
+  DHCP_PROTOCOL  *Instance;\r
+  EFI_TPL        OldTpl;\r
+  EFI_STATUS     Status;\r
+  NET_FRAGMENT   Frag;\r
+  NET_BUF        *Wrap;\r
+  UDP_POINTS     EndPoint;\r
+  IP4_ADDR       Ip;\r
+  DHCP_SERVICE   *DhcpSb;\r
+  IP4_ADDR       Gateway;\r
+  IP4_ADDR       SubnetMask;\r
+\r
+  if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP_INSTANCE_FROM_THIS (This);\r
+  DhcpSb   = Instance->Service;\r
+\r
+  if (Instance->Token != NULL) {\r
+    //\r
+    // The previous call to TransmitReceive is not finished.\r
+    //\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||\r
+    (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||\r
+    (Token->TimeoutValue == 0) ||\r
+    ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL)) ||\r
+    EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL)) ||\r
+    EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)) {\r
+    //\r
+    // The DHCP packet isn't well-formed, the Transaction ID is already used\r
+    // , the timeout value is zero, the ListenPoint is invalid,\r
+    // or the RemoteAddress is zero.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DhcpSb->ClientAddr == 0) {\r
+\r
+    return EFI_NO_MAPPING;\r
+  }\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  //\r
+  // Save the token and the timeout value.\r
+  //\r
+  Instance->Token   = Token;\r
+  Instance->Timeout = Token->TimeoutValue;\r
+\r
+  //\r
+  // Create a UDP IO for this transmit receive transaction.\r
+  //\r
+  Status = Dhcp4InstanceCreateUdpIo (Instance);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
   //\r
-  // This function is for PXE, leave it for now\r
+  // Wrap the DHCP packet into a net buffer.\r
   //\r
-  return EFI_UNSUPPORTED;\r
+  Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;\r
+  Frag.Len  = Token->Packet->Length;\r
+  Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);\r
+  if (Wrap == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Set the local address and local port.\r
+  //\r
+  EndPoint.LocalAddr = 0;\r
+  EndPoint.LocalPort = 0;\r
+\r
+  //\r
+  // Set the destination address and destination port.\r
+  //\r
+  NetCopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  EndPoint.RemoteAddr = NTOHL (Ip);\r
+\r
+  if (Token->RemotePort == 0) {\r
+    EndPoint.RemotePort = DHCP_SERVER_PORT;\r
+  } else {\r
+    EndPoint.RemotePort = Token->RemotePort;\r
+  }\r
+\r
+  //\r
+  // Get the gateway.\r
+  //\r
+  SubnetMask = DhcpSb->Netmask;\r
+  Gateway    = 0;\r
+  if (!IP4_NET_EQUAL (DhcpSb->ClientAddr, EndPoint.RemoteAddr, SubnetMask)) {\r
+    NetCopyMem (&Gateway, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    Gateway = NTOHL (Gateway);\r
+  }\r
+\r
+  //\r
+  // Transmit the DHCP packet.\r
+  //\r
+  Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, Gateway, DhcpOnPacketSent, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (Wrap);\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Start to receive the DHCP response.\r
+  //\r
+  Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+ON_ERROR:\r
+\r
+  if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {\r
+    UdpIoCleanPort (Instance->UdpIo);\r
+    UdpIoFreePort (Instance->UdpIo);\r
+    Instance->UdpIo = NULL;\r
+    Instance->Token = NULL;\r
+  }\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {\r
+    //\r
+    // Keep polling until timeout if no error happens and the CompletionEvent\r
+    // is NULL.\r
+    //\r
+    while (Instance->Timeout != 0) {\r
+      Instance->UdpIo->Udp->Poll (Instance->UdpIo->Udp);\r
+    }\r
+  }\r
+\r
+  return Status;\r
 }\r
 \r
 \r
@@ -910,3 +1260,4 @@ EFI_DHCP4_PROTOCOL  mDhcp4ProtocolTemplate = {
   EfiDhcp4TransmitReceive,\r
   EfiDhcp4Parse\r
 };\r
+\r