MdeModulePkg Ip4Dxe: Ip4Config2 to request DHCP Option6 DNS server IP

Ip4Config2 protocol implementation must request for DNS server info when the
policy is set to DHCP. And when a DHCP server responds to it with a list of
DNS server addresses, it must parse it and set it for the instance. Without
this, nobody can do a Ip4Config->GetData for DNS server IPs before calling
Dns->Configure(). This will mean a DHCP is initiated when calling
Dns->Configure(), thus causing serious performance issues. This patch
attempts to address this issue.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Samer El-Haj-Mahmoud <samer.el-haj-mahmoud@hpe.com>
Reviewed-by: Ye Ting <ting.ye@intel.com>
Reviewed-by: Samer El-Haj-Mahmoud <elhaj@hpe.com>
Reviewed-by: Fu Siyuan <siyuan.fu@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18560 6f19259b-4bc3-4df7-8a09-765794883524
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c
index 637d7cd..edbddba 100644
--- a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c
@@ -2,6 +2,7 @@
   The implementation of EFI IPv4 Configuration II Protocol.

 

   Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>

+  (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>

 

   This program and the accompanying materials

   are licensed and made available under the terms and conditions of the BSD License

@@ -677,6 +678,126 @@
   }

 }

 

+/**

+  This worker function sets the DNS server list for the EFI IPv4 network

+  stack running on the communication device that this EFI_IP4_CONFIG2_PROTOCOL

+  manages. The DNS server addresses must be unicast IPv4 addresses. 

+

+  @param[in]     Instance        The pointer to the IP4 config2 instance data.

+  @param[in]     DataSize        The size of the buffer pointed to by Data in bytes.

+  @param[in]     Data            The data buffer to set, points to an array of

+                                 EFI_IPv4_ADDRESS instances.

+

+  @retval EFI_BAD_BUFFER_SIZE    The DataSize does not match the size of the type.

+  @retval EFI_INVALID_PARAMETER  One or more fields in Data is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to complete the operation.

+  @retval EFI_ABORTED            The DNS server addresses to be set equal the current

+                                 configuration.

+  @retval EFI_SUCCESS            The specified configuration data for the EFI IPv4

+                                 network stack was set.

+

+**/

+EFI_STATUS

+Ip4Config2SetDnsServerWorker (

+  IN IP4_CONFIG2_INSTANCE    *Instance,

+  IN UINTN                   DataSize,

+  IN VOID                    *Data

+  )

+{

+  UINTN                 OldIndex;

+  UINTN                 NewIndex;

+  UINTN                 Index1;

+  EFI_IPv4_ADDRESS      *OldDns;

+  EFI_IPv4_ADDRESS      *NewDns;

+  UINTN                 OldDnsCount;

+  UINTN                 NewDnsCount;

+  IP4_CONFIG2_DATA_ITEM *Item;

+  BOOLEAN               OneAdded;

+  VOID                  *Tmp;

+  IP4_ADDR              DnsAddress;

+

+  if ((DataSize % sizeof (EFI_IPv4_ADDRESS) != 0) || (DataSize == 0)) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  Item        = &Instance->DataItem[Ip4Config2DataTypeDnsServer];

+  NewDns      = (EFI_IPv4_ADDRESS *) Data;

+  OldDns      = Item->Data.DnsServers;

+  NewDnsCount = DataSize / sizeof (EFI_IPv4_ADDRESS);  

+  OldDnsCount = Item->DataSize / sizeof (EFI_IPv4_ADDRESS);

+  OneAdded    = FALSE;

+

+  if (NewDnsCount != OldDnsCount) {

+    Tmp = AllocatePool (DataSize);

+    if (Tmp == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  } else {

+    Tmp = NULL;

+  }

+

+  for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) {

+    CopyMem (&DnsAddress, NewDns + NewIndex, sizeof (IP4_ADDR));

+

+    if (!NetIp4IsUnicast (NTOHL (DnsAddress), 0)) {

+      //

+      // The dns server address must be unicast.

+      //

+      FreePool (Tmp);

+      return EFI_INVALID_PARAMETER;

+    }

+

+    for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {

+      if (EFI_IP4_EQUAL (NewDns + NewIndex, NewDns + Index1)) {

+        FreePool (Tmp);

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+

+    if (OneAdded) {

+      //

+      // If any address in the new setting is not in the old settings, skip the

+      // comparision below.

+      //

+      continue;

+    }

+

+    for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) {

+      if (EFI_IP4_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) {

+        //

+        // If found break out.

+        //

+        break;

+      }

+    }

+

+    if (OldIndex == OldDnsCount) {

+      OneAdded = TRUE;

+    }

+  }

+

+  if (!OneAdded && (DataSize == Item->DataSize)) {

+    //

+    // No new item is added and the size is the same.

+    //

+    Item->Status = EFI_SUCCESS;

+    return EFI_ABORTED;

+  } else {

+    if (Tmp != NULL) {

+      if (Item->Data.Ptr != NULL) {

+        FreePool (Item->Data.Ptr);

+      }      

+      Item->Data.Ptr = Tmp;

+    }

+

+    CopyMem (Item->Data.Ptr, Data, DataSize);

+    Item->DataSize = DataSize;

+    Item->Status   = EFI_SUCCESS;

+    return EFI_SUCCESS;

+  }

+}

+

+

 

 /**

   Callback function when DHCP process finished. It will save the

@@ -701,6 +822,9 @@
   IP4_ADDR                  StationAddress;

   IP4_ADDR                  SubnetMask;

   IP4_ADDR                  GatewayAddress;

+  UINT32                    Index;

+  UINT32                    OptionCount;

+  EFI_DHCP4_PACKET_OPTION   **OptionList;

 

   Instance = (IP4_CONFIG2_INSTANCE *) Context;

   ASSERT (Instance->Dhcp4 != NULL);

@@ -724,6 +848,44 @@
       goto Exit;

     }

   

+    //

+    // Parse the ACK to get required DNS server information.

+    //

+    OptionCount = 0;

+    OptionList  = NULL;

+

+    Status      = Instance->Dhcp4->Parse (Instance->Dhcp4, Dhcp4Mode.ReplyPacket, &OptionCount, OptionList);

+    if (Status != EFI_BUFFER_TOO_SMALL) {

+      goto Exit;

+    }

+

+    OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));

+    if (OptionList == NULL) {

+      goto Exit;

+    }

+

+    Status = Instance->Dhcp4->Parse (Instance->Dhcp4, Dhcp4Mode.ReplyPacket, &OptionCount, OptionList);

+    if (EFI_ERROR (Status)) {

+      FreePool (OptionList);

+      goto Exit;

+    }

+

+    for (Index = 0; Index < OptionCount; Index++) {

+      //

+      // Look for DNS Server opcode (6).

+      //

+      if (OptionList[Index]->OpCode == DHCP_TAG_DNS_SERVER) {

+        if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {

+          break;

+        }

+

+        Ip4Config2SetDnsServerWorker (Instance, OptionList[Index]->Length, &OptionList[Index]->Data[0]);

+        break;

+      }

+    }

+

+    FreePool (OptionList);

+

     Instance->DhcpSuccess = TRUE;

   }

 

@@ -831,9 +993,10 @@
   // yields the control of this DHCP service to us.

   //

   ParaList.Head.OpCode             = DHCP_TAG_PARA_LIST;

-  ParaList.Head.Length             = 2;

+  ParaList.Head.Length             = 3;

   ParaList.Head.Data[0]            = DHCP_TAG_NETMASK;

   ParaList.Route                   = DHCP_TAG_ROUTER;

+  ParaList.Dns                     = DHCP_TAG_DNS_SERVER;

   OptionList[0]                    = &ParaList.Head;

   Dhcp4Mode.ConfigData.OptionCount = 1;

   Dhcp4Mode.ConfigData.OptionList  = OptionList;

@@ -1293,102 +1456,11 @@
   IN VOID                 *Data

   )

 {

-  UINTN                 OldIndex;

-  UINTN                 NewIndex;

-  UINTN                 Index1;

-  EFI_IPv4_ADDRESS      *OldDns;

-  EFI_IPv4_ADDRESS      *NewDns;

-  UINTN                 OldDnsCount;

-  UINTN                 NewDnsCount;

-  IP4_CONFIG2_DATA_ITEM *Item;

-  BOOLEAN               OneAdded;

-  VOID                  *Tmp;

-  IP4_ADDR              DnsAddress;

-

-  if ((DataSize % sizeof (EFI_IPv4_ADDRESS) != 0) || (DataSize == 0)) {

-    return EFI_BAD_BUFFER_SIZE;

-  }

-

   if (Instance->Policy != Ip4Config2PolicyStatic) {

     return EFI_WRITE_PROTECTED;

   }

 

-  Item        = &Instance->DataItem[Ip4Config2DataTypeDnsServer];

-  NewDns      = (EFI_IPv4_ADDRESS *) Data;

-  OldDns      = Item->Data.DnsServers;

-  NewDnsCount = DataSize / sizeof (EFI_IPv4_ADDRESS);

-  OldDnsCount = Item->DataSize / sizeof (EFI_IPv4_ADDRESS);

-  OneAdded    = FALSE;

-

-  if (NewDnsCount != OldDnsCount) {

-    Tmp = AllocatePool (DataSize);

-    if (Tmp == NULL) {

-      return EFI_OUT_OF_RESOURCES;

-    }

-  } else {

-    Tmp = NULL;

-  }

-

-  for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) {

-    CopyMem (&DnsAddress, NewDns + NewIndex, sizeof (IP4_ADDR));

-

-    if (!NetIp4IsUnicast (NTOHL (DnsAddress), 0)) {

-      //

-      // The dns server address must be unicast.

-      //

-      FreePool (Tmp);

-      return EFI_INVALID_PARAMETER;

-    }

-

-    for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {

-      if (EFI_IP4_EQUAL (NewDns + NewIndex, NewDns + Index1)) {

-        FreePool (Tmp);

-        return EFI_INVALID_PARAMETER;

-      }

-    }

-

-    if (OneAdded) {

-      //

-      // If any address in the new setting is not in the old settings, skip the

-      // comparision below.

-      //

-      continue;

-    }

-

-    for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) {

-      if (EFI_IP4_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) {

-        //

-        // If found break out.

-        //

-        break;

-      }

-    }

-

-    if (OldIndex == OldDnsCount) {

-      OneAdded = TRUE;

-    }

-  }

-

-  if (!OneAdded && (DataSize == Item->DataSize)) {

-    //

-    // No new item is added and the size is the same.

-    //

-    Item->Status = EFI_SUCCESS;

-    return EFI_ABORTED;

-  } else {

-    if (Tmp != NULL) {

-      if (Item->Data.Ptr != NULL) {

-        FreePool (Item->Data.Ptr);

-      }      

-      Item->Data.Ptr = Tmp;

-    }

-

-    CopyMem (Item->Data.Ptr, Data, DataSize);

-    Item->DataSize = DataSize;

-    Item->Status   = EFI_SUCCESS;

-    return EFI_SUCCESS;

-  }

-

+  return Ip4Config2SetDnsServerWorker (Instance, DataSize, Data);

 }

 

 /**

diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h
index e74b9ae..ab72525 100644
--- a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h
@@ -2,6 +2,7 @@
   Definitions for EFI IPv4 Configuration II Protocol implementation.

 

   Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>

+  (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>

 

   This program and the accompanying materials

   are licensed and made available under the terms and conditions of the BSD License

@@ -27,7 +28,7 @@
 #define DHCP_TAG_PARA_LIST             55

 #define DHCP_TAG_NETMASK               1

 #define DHCP_TAG_ROUTER                3

-

+#define DHCP_TAG_DNS_SERVER            6

 

 #define DATA_ATTRIB_SET(Attrib, Bits)       (BOOLEAN)((Attrib) & (Bits))

 #define SET_DATA_ATTRIB(Attrib, Bits)       ((Attrib) |= (Bits))

@@ -207,6 +208,7 @@
 typedef struct {

   EFI_DHCP4_PACKET_OPTION Head;

   UINT8                   Route;

+  UINT8                   Dns;

 } IP4_CONFIG2_DHCP4_OPTION;

 #pragma pack()