X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=NetworkPkg%2FUefiPxeBcDxe%2FPxeBcDhcp6.c;h=07f1724365f4dc7078d2c833ec4abefe9808ae93;hb=07f986f134abf85b4b1f360ca3c86f22cd9f92da;hp=0a2dd6d13ebc1eb6f20fd9491faa860056edcd6b;hpb=d40002bab9958da3439a2548aa3003d8d68672ff;p=mirror_edk2.git diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c index 0a2dd6d13e..07f1724365 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c @@ -1,7 +1,8 @@ /** @file Functions implementation related with DHCPv6 for UefiPxeBc Driver. - Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ 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 @@ -488,7 +489,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]; @@ -664,7 +665,6 @@ PxeBcRequestBootService ( { EFI_PXE_BASE_CODE_UDP_PORT SrcPort; EFI_PXE_BASE_CODE_UDP_PORT DestPort; - EFI_PXE_BASE_CODE_MODE *Mode; EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover; UINTN DiscoverLen; @@ -682,7 +682,6 @@ PxeBcRequestBootService ( UINT8 *Option; PxeBc = &Private->PxeBc; - Mode = PxeBc->Mode; Request = Private->Dhcp6Request; ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer; SrcPort = PXEBC_BS_DISCOVER_PORT; @@ -1235,9 +1234,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 which 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,10 +1374,14 @@ 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; EFI_STATUS Status; + UINT64 DadTriggerTime; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + BOOLEAN NoGateway; Status = EFI_SUCCESS; TimeOutEvt = NULL; @@ -1267,23 +1389,24 @@ PxeBcRegisterIp6Address ( DataSize = sizeof (EFI_IP6_CONFIG_POLICY); Ip6Cfg = Private->Ip6Cfg; Ip6 = Private->Ip6; + NoGateway = FALSE; ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS)); - // - // Get and store the current policy of IP6 driver. - // - Status = Ip6Cfg->GetData ( - Ip6Cfg, - Ip6ConfigDataTypePolicy, - &DataSize, - &Private->Ip6Policy - ); + Status = Ip6->Configure (Ip6, &Private->Ip6CfgData); if (EFI_ERROR (Status)) { goto ON_EXIT; } + // + // Retrieve the gateway address from IP6 route table. + // + Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr); + if (EFI_ERROR (Status)) { + NoGateway = TRUE; + } + // // There is no channel between IP6 and PXE driver about address setting, // so it has to set the new address by Ip6ConfigProtocol manually. @@ -1303,6 +1426,20 @@ PxeBcRegisterIp6Address ( goto ON_EXIT; } + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // // Create a timer as setting address timeout event since DAD in IP6 driver. // @@ -1354,7 +1491,8 @@ PxeBcRegisterIp6Address ( // Start the 5 secondes timer to wait for setting address. // Status = EFI_NO_MAPPING; - gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT); + DadTriggerTime = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY; + gBS->SetTimer (TimeOutEvt, TimerRelative, DadTriggerTime); while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { Ip6->Poll (Ip6); @@ -1364,6 +1502,21 @@ PxeBcRegisterIp6Address ( } } + // + // Set the default gateway address back if needed. + // + if (!NoGateway && !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 ( @@ -1379,6 +1532,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 @@ -1698,9 +1945,17 @@ PxeBcDhcp6Sarr ( UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE]; UINT32 OptCount; EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_STATUS TimerStatus; + EFI_EVENT Timer; + UINT64 GetMappingTimeOut; + UINTN DataSize; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; Status = EFI_SUCCESS; PxeMode = Private->PxeBc.Mode; + Ip6Cfg = Private->Ip6Cfg; + Timer = NULL; // // Build option list for the request packet. @@ -1723,7 +1978,7 @@ PxeBcDhcp6Sarr ( Config.IaInfoEvent = NULL; Config.RapidCommit = FALSE; Config.ReconfigureAccept = FALSE; - Config.IaDescriptor.IaId = 1; + Config.IaDescriptor.IaId = Private->IaId; Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; Config.SolicitRetransmission = Retransmit; Retransmit->Irt = 4; @@ -1735,8 +1990,8 @@ PxeBcDhcp6Sarr ( // Configure the DHCPv6 instance for PXE boot. // Status = Dhcp6->Configure (Dhcp6, &Config); + FreePool (Retransmit); if (EFI_ERROR (Status)) { - FreePool (Retransmit); return Status; } @@ -1754,6 +2009,52 @@ PxeBcDhcp6Sarr ( // Start DHCPv6 S.A.R.R. process to acquire IPv6 address. // Status = Dhcp6->Start (Dhcp6); + if (Status == EFI_NO_MAPPING) { + // + // IP6 Linklocal address is not available for use, so stop current Dhcp process + // and wait for duplicate address detection to finish. + // + Dhcp6->Stop (Dhcp6); + + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY; + Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Timer); + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + do { + + TimerStatus = gBS->CheckEvent (Timer); + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6->Start (Dhcp6); + } + } while (TimerStatus == EFI_NOT_READY); + + gBS->CloseEvent (Timer); + } if (EFI_ERROR (Status)) { if (Status == EFI_ICMP_ERROR) { PxeMode->IcmpErrorReceived = TRUE; @@ -1772,36 +2073,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 = PxeBcFlushStaionIp (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; } -