From ae97201c55603b69289c5d2349238ea97b2cd35e Mon Sep 17 00:00:00 2001 From: "Fu, Siyuan" Date: Thu, 18 Sep 2014 11:44:36 +0000 Subject: [PATCH] 1. Update PXE driver to support PXEv6 boot cross subnet. 2. Update IP6 driver to use previous configured prefix length if a pre-exist IP6 address with unspecified prefix length. 3. Add NULL check for Dhcp protocol pointer before it decline the address in Ip6ConfigSetStatefulAddrCallback() function. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Fu, Siyuan Reviewed-By: Ye, Ting (ting.ye@intel.com) Reviewed-By: Wu, Jiaxin git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16131 6f19259b-4bc3-4df7-8a09-765794883524 --- NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c | 22 ++- NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c | 8 + NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c | 2 +- NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c | 280 ++++++++++++++++++++++++--- NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h | 30 ++- NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c | 8 + NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h | 1 + 7 files changed, 311 insertions(+), 40 deletions(-) diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c index ddb8dcd379..9a1e3d076f 100644 --- a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c +++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c @@ -1,7 +1,7 @@ /** @file The implementation of EFI IPv6 Configuration Protocol. - Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
+ Copyright (c) 2009 - 2014, 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 @@ -1012,6 +1012,14 @@ Ip6ConfigSetMaunualAddress ( 128 ); + // + // If the new address's prefix length is not specified, just use the previous configured + // prefix length for this address. + // + if (NewAddress->PrefixLength == 0) { + NewAddress->PrefixLength = CurrentAddrInfo->PrefixLength; + } + // // This manual address is already in use, see whether prefix length is changed. // @@ -1594,11 +1602,13 @@ Ip6ConfigSetStatefulAddrCallback ( // // Decline those duplicates. // - Instance->Dhcp6->Decline ( - Instance->Dhcp6, - Instance->DeclineAddressCount, - Instance->DeclineAddress - ); + if (Instance->Dhcp6 != NULL) { + Instance->Dhcp6->Decline ( + Instance->Dhcp6, + Instance->DeclineAddressCount, + Instance->DeclineAddress + ); + } } if (Instance->DeclineAddress != NULL) { diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c index 84031e40e0..253115308e 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c @@ -632,6 +632,14 @@ PxeBcDhcp6BootInfo ( return Status; } + // + // Set the station address to IP layer. + // + Status = PxeBcSetIp6Address (Private); + if (EFI_ERROR (Status)) { + return Status; + } + // // Parse the value of boot file size. // diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c index b113505d97..df171598b1 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c @@ -512,7 +512,7 @@ PxeBcParseDhcp4Packet ( } // - // The offer with "yiaddr" is a proxy offer. + // The offer with zero "yiaddr" is a proxy offer. // if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { IsProxyOffer = TRUE; diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c index c93bad9434..b71998b405 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c @@ -488,7 +488,7 @@ PxeBcParseDhcp6Packet ( } // - // The offer with assigned client address is a proxy offer. + // The offer with assigned client address is NOT a proxy offer. // An ia_na option, embeded with valid ia_addr option and a status_code of success. // Option = Options[PXEBC_DHCP6_IDX_IA_NA]; @@ -1235,9 +1235,128 @@ PxeBcUnregisterIp6Address ( } } +/** + Check whether IP driver could route the message which will be sent to ServerIp address. + + This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid + route is found in IP6 route table, the address will be filed in GatewayAddr and return. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] TimeOutInSecond Timeout value in seconds. + @param[out] GatewayAddr Pointer to store the gateway IP address. + + @retval EFI_SUCCESS Found a valid gateway address successfully. + @retval EFI_TIMEOUT The operation is time out. + @retval Other Unexpect error happened. + +**/ +EFI_STATUS +PxeBcCheckRouteTable ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINTN TimeOutInSecond, + OUT EFI_IPv6_ADDRESS *GatewayAddr + ) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_MODE_DATA Ip6ModeData; + UINTN Index; + EFI_EVENT TimeOutEvt; + UINTN RetryCount; + BOOLEAN GatewayIsFound; + + ASSERT (GatewayAddr != NULL); + ASSERT (Private != NULL); + + Ip6 = Private->Ip6; + GatewayIsFound = FALSE; + RetryCount = 0; + TimeOutEvt = NULL; + ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS)); + + while (TRUE) { + Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Find out the gateway address which can route the message whcih send to ServerIp. + // + for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) { + if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) { + IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway); + GatewayIsFound = TRUE; + break; + } + } + + if (Ip6ModeData.AddressList != NULL) { + FreePool (Ip6ModeData.AddressList); + } + if (Ip6ModeData.GroupTable != NULL) { + FreePool (Ip6ModeData.GroupTable); + } + if (Ip6ModeData.RouteTable != NULL) { + FreePool (Ip6ModeData.RouteTable); + } + if (Ip6ModeData.NeighborCache != NULL) { + FreePool (Ip6ModeData.NeighborCache); + } + if (Ip6ModeData.PrefixTable != NULL) { + FreePool (Ip6ModeData.PrefixTable); + } + if (Ip6ModeData.IcmpTypeList != NULL) { + FreePool (Ip6ModeData.IcmpTypeList); + } + + if (GatewayIsFound || RetryCount == TimeOutInSecond) { + break; + } + + RetryCount++; + + // + // Delay 1 second then recheck it again. + // + if (TimeOutEvt == NULL) { + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + Ip6->Poll (Ip6); + } + } + +ON_EXIT: + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + + if (GatewayIsFound) { + Status = EFI_SUCCESS; + } else if (RetryCount == TimeOutInSecond) { + Status = EFI_TIMEOUT; + } + + return Status; +} /** - Register the ready address by Ip6Config protocol. + Register the ready station address and gateway by Ip6Config protocol. @param[in] Private The pointer to PXEBC_PRIVATE_DATA. @param[in] Address The pointer to the ready address. @@ -1256,6 +1375,7 @@ PxeBcRegisterIp6Address ( EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; EFI_IP6_CONFIG_POLICY Policy; EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr; + EFI_IPv6_ADDRESS GatewayAddr; UINTN DataSize; EFI_EVENT TimeOutEvt; EFI_EVENT MappedEvt; @@ -1273,19 +1393,19 @@ PxeBcRegisterIp6Address ( ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS)); + Status = Ip6->Configure (Ip6, &Private->Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // - // Get and store the current policy of IP6 driver. + // Retrieve the gateway address from IP6 route table. // - Status = Ip6Cfg->GetData ( - Ip6Cfg, - Ip6ConfigDataTypePolicy, - &DataSize, - &Private->Ip6Policy - ); + Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr); if (EFI_ERROR (Status)) { goto ON_EXIT; } - + // // There is no channel between IP6 and PXE driver about address setting, // so it has to set the new address by Ip6ConfigProtocol manually. @@ -1381,6 +1501,21 @@ PxeBcRegisterIp6Address ( } } + // + // Set the default gateway address back if needed. + // + if (!NetIp6IsUnspecifiedAddr (&GatewayAddr)) { + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeGateway, + sizeof (EFI_IPv6_ADDRESS), + &GatewayAddr + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + ON_EXIT: if (MappedEvt != NULL) { Ip6Cfg->UnregisterDataNotify ( @@ -1396,6 +1531,100 @@ ON_EXIT: return Status; } +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +PxeBcSetIp6Policy ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_POLICY Policy; + EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + UINTN DataSize; + + Ip6Cfg = Private->Ip6Cfg; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + + // + // Get and store the current policy of IP6 driver. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + &DataSize, + &Private->Ip6Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Private->Ip6Policy == Ip6ConfigPolicyManual) { + Policy = Ip6ConfigPolicyAutomatic; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + // + // There is no need to recover later. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + } + } + + return Status; +} + +/** + This function will register the station IP address and flush IP instance to start using the new IP address. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +PxeBcSetIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + Dhcp6 = Private->Dhcp6; + + CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + + Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL); + if (EFI_ERROR (Status)) { + PxeBcUnregisterIp6Address (Private); + Dhcp6->Stop (Dhcp6); + return Status; + } + + AsciiPrint ("\n Station IP address is "); + PxeBcShowIp6Addr (&Private->StationIp.v6); + + return EFI_SUCCESS; +} /** EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver @@ -1843,36 +2072,23 @@ PxeBcDhcp6Sarr ( } ASSERT (Mode.Ia->State == Dhcp6Bound); - CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); - CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); - - Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6); - if (EFI_ERROR (Status)) { - Dhcp6->Stop (Dhcp6); - return Status; - } - - Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL); - if (EFI_ERROR (Status)) { - PxeBcUnregisterIp6Address (Private); - Dhcp6->Stop (Dhcp6); - return Status; - } + // + // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the + // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when + // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as + // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery + // to find a valid router address. + // + CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); // // Check the selected offer whether BINL retry is needed. // Status = PxeBcHandleDhcp6Offer (Private); if (EFI_ERROR (Status)) { - PxeBcUnregisterIp6Address (Private); Dhcp6->Stop (Dhcp6); return Status; } - - AsciiPrint ("\n Station IP address is "); - - PxeBcShowIp6Addr (&Private->StationIp.v6); - + return EFI_SUCCESS; } - diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h index bb8ad65b63..38bf26564d 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h @@ -1,7 +1,7 @@ /** @file Functions declaration related with DHCPv6 for UefiPxeBc Driver. - Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ Copyright (c) 2009 - 2014, 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 @@ -20,6 +20,7 @@ #define PXEBC_DHCP6_OPTION_MAX_SIZE 312 #define PXEBC_DHCP6_PACKET_MAX_SIZE 1472 #define PXEBC_IP6_POLICY_MAX 0xff +#define PXEBC_IP6_ROUTE_TABLE_TIMEOUT 10 #define PXEBC_DHCP6_S_PORT 547 #define PXEBC_DHCP6_C_PORT 546 @@ -254,6 +255,33 @@ PxeBcDhcp6Discover ( IN EFI_IP_ADDRESS *DestIp ); +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +PxeBcSetIp6Policy ( + IN PXEBC_PRIVATE_DATA *Private + ); + +/** + This function will register the station IP address and flush IP instance to start using the new IP address. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +PxeBcSetIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ); /** Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information. diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c index be3d248fa9..89d43f8e69 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c @@ -124,6 +124,14 @@ EfiPxeBcStart ( if (EFI_ERROR (Status)) { goto ON_ERROR; } + + // + // Set Ip6 policy to Automatic to start the IP6 router discovery. + // + Status = PxeBcSetIp6Policy (Private); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } } else { AsciiPrint ("\n>>Start PXE over IPv4"); // diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h index 1d4381a1f5..8f754e0b3b 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h @@ -160,6 +160,7 @@ struct _PXEBC_PRIVATE_DATA { BOOLEAN IsProxyRecved; BOOLEAN IsDoDiscover; + EFI_IP_ADDRESS TmpStationIp; EFI_IP_ADDRESS StationIp; EFI_IP_ADDRESS SubnetMask; EFI_IP_ADDRESS GatewayIp; -- 2.39.2