/** @file\r
Dhcp6 internal functions implementation.\r
\r
- Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>\r
\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
}\r
\r
//\r
- // Save tx packet pointer, and it will be destoryed when reply received.\r
+ // Save tx packet pointer, and it will be destroyed when reply received.\r
//\r
TxCb->TxPacket = Packet;\r
TxCb->Xid = Packet->Dhcp6.Header.TransactionId;\r
}\r
}\r
\r
+/**\r
+ Check whether the TxCb is still a valid control block in the instance's retry list.\r
+\r
+ @param[in] Instance The pointer to DHCP6_INSTANCE.\r
+ @param[in] TxCb The control block for a transmitted message.\r
+\r
+ @retval TRUE The control block is in Instance's retry list.\r
+ @retval FALSE The control block is NOT in Instance's retry list.\r
+\r
+**/\r
+BOOLEAN\r
+Dhcp6IsValidTxCb (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN DHCP6_TX_CB *TxCb\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Instance->TxList) {\r
+ if (TxCb == NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link)) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
\r
/**\r
Clean up the session of the instance stateful exchange.\r
)\r
{\r
EFI_STATUS Status;\r
- EFI_DHCP6_STATE State;\r
UINT8 *Option;\r
UINT8 *IaInnerOpt;\r
UINT16 IaInnerLen;\r
//\r
// See details in the section-18.1.8 of rfc-3315.\r
//\r
- State = Dhcp6Init;\r
Option = Dhcp6SeekIaOption (\r
Packet->Dhcp6.Option,\r
Packet->Length - sizeof (EFI_DHCP6_HEADER),\r
if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {\r
T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8)));\r
T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12)));\r
+ //\r
+ // Refer to RFC3155 Chapter 22.4. If a client receives an IA_NA with T1 greater than T2,\r
+ // and both T1 and T2 are greater than 0, the client discards the IA_NA option and processes\r
+ // the remainder of the message as though the server had not included the invalid IA_NA option.\r
+ //\r
+ if (T1 > T2 && T2 > 0) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
IaInnerOpt = Option + 16;\r
IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12);\r
} else {\r
&Instance->Config->IaDescriptor\r
);\r
if (*Option == NULL) {\r
- return EFI_DEVICE_ERROR;\r
+ return EFI_SUCCESS;\r
}\r
\r
//\r
Cursor,\r
Instance->IaCb.Ia,\r
Instance->IaCb.T1,\r
- Instance->IaCb.T2\r
+ Instance->IaCb.T2,\r
+ Packet->Dhcp6.Header.MessageType\r
);\r
\r
//\r
// Dhcp6selecting.\r
//\r
Instance->IaCb.Ia->State = Dhcp6Selecting;\r
+ //\r
+ // Clear initial time for current transaction.\r
+ //\r
+ Instance->StartTime = 0;\r
\r
Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
\r
Cursor,\r
Instance->IaCb.Ia,\r
Instance->IaCb.T1,\r
- Instance->IaCb.T2\r
+ Instance->IaCb.T2,\r
+ Packet->Dhcp6.Header.MessageType\r
);\r
\r
//\r
// Dhcp6requesting.\r
//\r
Instance->IaCb.Ia->State = Dhcp6Requesting;\r
+ //\r
+ // Clear initial time for current transaction.\r
+ //\r
+ Instance->StartTime = 0;\r
\r
Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
\r
ServerId->Duid\r
);\r
\r
- Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0);\r
+ Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0, Packet->Dhcp6.Header.MessageType);\r
\r
//\r
// Determine the size/length of packet.\r
// Dhcp6declining.\r
//\r
Instance->IaCb.Ia->State = Dhcp6Declining;\r
+ //\r
+ // Clear initial time for current transaction.\r
+ //\r
+ Instance->StartTime = 0;\r
\r
Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
\r
&Elapsed\r
);\r
\r
- Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0);\r
+ Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0, Packet->Dhcp6.Header.MessageType);\r
\r
//\r
// Determine the size/length of packet\r
Cursor,\r
Instance->IaCb.Ia,\r
Instance->IaCb.T1,\r
- Instance->IaCb.T2\r
+ Instance->IaCb.T2,\r
+ Packet->Dhcp6.Header.MessageType\r
);\r
\r
if (!RebindRequest) {\r
//\r
Instance->IaCb.Ia->State = State;\r
Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1;\r
+ //\r
+ // Clear initial time for current transaction.\r
+ //\r
+ Instance->StartTime = 0;\r
\r
Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
\r
return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
}\r
\r
+/**\r
+ Start the information request process.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] SendClientId If TRUE, the client identifier option will be included in\r
+ information request message. Otherwise, the client identifier\r
+ option will not be included.\r
+ @param[in] OptionRequest The pointer to the option request option.\r
+ @param[in] OptionCount The number options in the OptionList.\r
+ @param[in] OptionList The array pointers to the appended options.\r
+ @param[in] Retransmission The pointer to the retransmission control.\r
+ @param[in] TimeoutEvent The event of timeout.\r
+ @param[in] ReplyCallback The callback function when the reply was received.\r
+ @param[in] CallbackContext The pointer to the parameter passed to the callback.\r
+\r
+ @retval EFI_SUCCESS Start the info-request process successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_NO_MAPPING No source address is available for use.\r
+ @retval Others Failed to start the info-request process.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6StartInfoRequest (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN BOOLEAN SendClientId,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,\r
+ IN UINT32 OptionCount,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,\r
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission,\r
+ IN EFI_EVENT TimeoutEvent OPTIONAL,\r
+ IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,\r
+ IN VOID *CallbackContext OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ DHCP6_INF_CB *InfCb;\r
+ DHCP6_SERVICE *Service;\r
+ EFI_TPL OldTpl;\r
+\r
+ Service = Instance->Service;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance->UdpSts = EFI_ALREADY_STARTED;\r
+ //\r
+ // Create and initialize the control block for the info-request.\r
+ //\r
+ InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB));\r
+\r
+ if (InfCb == NULL) {\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ InfCb->ReplyCallback = ReplyCallback;\r
+ InfCb->CallbackContext = CallbackContext;\r
+ InfCb->TimeoutEvent = TimeoutEvent;\r
+\r
+ InsertTailList (&Instance->InfList, &InfCb->Link);\r
+\r
+ //\r
+ // Send the info-request message to start exchange process.\r
+ //\r
+ Status = Dhcp6SendInfoRequestMsg (\r
+ Instance,\r
+ InfCb,\r
+ SendClientId,\r
+ OptionRequest,\r
+ OptionCount,\r
+ OptionList,\r
+ Retransmission\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Register receive callback for the stateless exchange process.\r
+ //\r
+ Status = UdpIoRecvDatagram(\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ gBS->RestoreTPL (OldTpl);\r
+ RemoveEntryList (&InfCb->Link);\r
+ FreePool (InfCb);\r
+\r
+ return Status;\r
+}\r
\r
/**\r
Create the information request message and send it.\r
Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
ASSERT (Packet->Size > Packet->Length + 8);\r
\r
+ //\r
+ // Clear initial time for current transaction.\r
+ //\r
+ Instance->StartTime = 0;\r
+\r
//\r
// Send info-request packet with no state.\r
//\r
Cursor,\r
Instance->IaCb.Ia,\r
Instance->IaCb.T1,\r
- Instance->IaCb.T2\r
+ Instance->IaCb.T2,\r
+ Packet->Dhcp6.Header.MessageType\r
);\r
\r
//\r
// Dhcp6Confirming.\r
//\r
Instance->IaCb.Ia->State = Dhcp6Confirming;\r
+ //\r
+ // Clear initial time for current transaction.\r
+ //\r
+ Instance->StartTime = 0;\r
\r
Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
\r
ASSERT (Instance->IaCb.Ia != NULL);\r
ASSERT (Packet != NULL);\r
\r
+ Status = EFI_SUCCESS;\r
+\r
if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
return EFI_DEVICE_ERROR;\r
}\r
&Instance->Config->IaDescriptor\r
);\r
if (Option == NULL) {\r
- return EFI_DEVICE_ERROR;\r
+ return EFI_SUCCESS;\r
}\r
}\r
\r
return Status;\r
}\r
\r
- //\r
- // Dequeue the sent packet from retransmit list since reply received.\r
- //\r
- Status = Dhcp6DequeueRetry (\r
- Instance,\r
- Packet->Dhcp6.Header.TransactionId,\r
- FALSE\r
- );\r
-\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
//\r
// When receive a valid reply packet in response to a decline/release packet,\r
// the client considers the decline/release event completed regardless of the\r
//\r
Instance->StartTime = 0;\r
\r
- return EFI_SUCCESS;\r
+ Status = EFI_SUCCESS;\r
+ goto ON_EXIT;\r
}\r
\r
//\r
);\r
\r
if (!EFI_ERROR (Status)) {\r
- //\r
- // Reset start time for next exchange.\r
- //\r
- Instance->StartTime = 0;\r
-\r
//\r
// No status code or no error status code means succeed to reply.\r
//\r
Status = Dhcp6UpdateIaInfo (Instance, Packet);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Reset start time for next exchange.\r
+ //\r
+ Instance->StartTime = 0;\r
\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
- //\r
- // Set bound state and store the reply packet.\r
- //\r
- if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
- FreePool (Instance->IaCb.Ia->ReplyPacket);\r
- }\r
+ //\r
+ // Set bound state and store the reply packet.\r
+ //\r
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+ FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+ }\r
\r
- Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);\r
+ Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);\r
\r
- if (Instance->IaCb.Ia->ReplyPacket == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
+ if (Instance->IaCb.Ia->ReplyPacket == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
\r
- CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);\r
+ CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);\r
\r
- Instance->IaCb.Ia->State = Dhcp6Bound;\r
+ Instance->IaCb.Ia->State = Dhcp6Bound;\r
\r
- //\r
- // For sync, set the success flag out of polling in start/renewrebind.\r
- //\r
- Instance->UdpSts = EFI_SUCCESS;\r
+ //\r
+ // For sync, set the success flag out of polling in start/renewrebind.\r
+ //\r
+ Instance->UdpSts = EFI_SUCCESS;\r
\r
- //\r
- // Maybe this is a new round DHCP process due to some reason, such as NotOnLink\r
- // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that\r
- // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP\r
- // consumers can be notified to flush old address.\r
- //\r
- Dhcp6AppendCacheIa (Instance);\r
+ //\r
+ // Maybe this is a new round DHCP process due to some reason, such as NotOnLink\r
+ // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that\r
+ // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP\r
+ // consumers can be notified to flush old address.\r
+ //\r
+ Dhcp6AppendCacheIa (Instance);\r
\r
- //\r
- // For async, signal the Ia event to inform Ia infomation update.\r
- //\r
- if (Instance->Config->IaInfoEvent != NULL) {\r
- gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+ //\r
+ // For async, signal the Ia event to inform Ia infomation update.\r
+ //\r
+ if (Instance->Config->IaInfoEvent != NULL) {\r
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+ }\r
+ } else if (Status == EFI_NOT_FOUND) {\r
+ //\r
+ // Refer to RFC3315 Chapter 18.1.8, for each IA in the original Renew or Rebind message,\r
+ // the client sends a Renew or Rebind if the IA is not in the Reply message.\r
+ // Return EFI_SUCCESS so we can continue to restart the Renew/Rebind process.\r
+ //\r
+ return EFI_SUCCESS;\r
}\r
+\r
+ goto ON_EXIT;\r
+\r
} else if (Option != NULL) {\r
//\r
// Any error status code option is found.\r
}\r
break;\r
\r
+ case Dhcp6StsNoBinding:\r
+ if (Instance->IaCb.Ia->State == Dhcp6Renewing || Instance->IaCb.Ia->State == Dhcp6Rebinding) {\r
+ //\r
+ // Refer to RFC3315 Chapter 18.1.8, for each IA in the original Renew or Rebind message, the client\r
+ // sends a Request message if the IA contained a Status Code option with the NoBinding status.\r
+ //\r
+ Status = Dhcp6SendRequestMsg(Instance);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ break;\r
+\r
default:\r
//\r
// The other status code, just restart solicitation.\r
}\r
\r
return EFI_SUCCESS;\r
+\r
+ON_EXIT:\r
+\r
+ if (!EFI_ERROR(Status)) {\r
+ Status = Dhcp6DequeueRetry (\r
+ Instance,\r
+ Packet->Dhcp6.Header.TransactionId,\r
+ FALSE\r
+ );\r
+ }\r
+\r
+ return Status;\r
}\r
\r
\r
{\r
EFI_STATUS Status;\r
UINT8 *Option;\r
- UINT16 StsCode;\r
BOOLEAN Timeout;\r
\r
ASSERT(Instance->Config);\r
ASSERT(Instance->IaCb.Ia);\r
\r
Timeout = FALSE;\r
- StsCode = Dhcp6StsSuccess;\r
\r
//\r
// If the client does receives a valid reply message that includes a rapid\r
// display the associated status message to the user.\r
// See the details in the section-17.1.3 of rfc-3315.\r
//\r
- Option = Dhcp6SeekOption(\r
- Packet->Dhcp6.Option,\r
- Packet->Length - 4,\r
- Dhcp6OptStatusCode\r
+ Status = Dhcp6SeekStsOption (\r
+ Instance,\r
+ Packet,\r
+ &Option\r
);\r
-\r
- if (Option != NULL) {\r
- StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));\r
- if (StsCode != Dhcp6StsSuccess) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
}\r
\r
//\r
ClientId = Service->ClientId;\r
Status = EFI_SUCCESS;\r
\r
- if (Instance->InDestory || Instance->Config == NULL) {\r
+ if (Instance->Config == NULL) {\r
goto ON_CONTINUE;\r
}\r
\r
IsMatched = FALSE;\r
InfCb = NULL;\r
\r
- if (Instance->InDestory) {\r
- goto ON_EXIT;\r
- }\r
-\r
if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
goto ON_EXIT;\r
}\r
LIST_ENTRY *Next1;\r
LIST_ENTRY *Entry2;\r
LIST_ENTRY *Next2;\r
+ EFI_STATUS Status;\r
\r
ASSERT (Udp6Wrap != NULL);\r
ASSERT (Context != NULL);\r
return ;\r
}\r
\r
+ if (Udp6Wrap->TotalSize < sizeof (EFI_DHCP6_HEADER)) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
//\r
// Copy the net buffer received from upd6 to a Dhcp6 packet.\r
//\r
\r
ON_CONTINUE:\r
\r
+ if (!IsDispatched) {\r
+ Status = UdpIoRecvDatagram (\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {\r
+ Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);\r
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);\r
+ }\r
+ }\r
+ }\r
+\r
NetbufFree (Udp6Wrap);\r
\r
if (Packet != NULL) {\r
DHCP6_TX_CB *TxCb;\r
DHCP6_IA_CB *IaCb;\r
UINT32 LossTime;\r
+ EFI_STATUS Status;\r
\r
ASSERT (Context != NULL);\r
\r
//\r
// Handle the first rt in the transmission of solicit specially.\r
//\r
- if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {\r
+ if ((TxCb->RetryCnt == 0 || TxCb->SolicitRetry) && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {\r
if (Instance->AdSelect == NULL) {\r
//\r
// Set adpref as 0xff here to indicate select any advertisement\r
//\r
// Select the advertisement received before.\r
//\r
- Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);\r
+ Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);\r
+ if (Status == EFI_ABORTED) {\r
+ goto ON_CLOSE;\r
+ } else if (EFI_ERROR (Status)) {\r
+ TxCb->RetryCnt++;\r
+ }\r
return;\r
}\r
}\r
// Check whether overflow the max retry count limit for this packet\r
//\r
if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {\r
+ Status = EFI_NO_RESPONSE;\r
goto ON_CLOSE;\r
}\r
\r
// Check whether overflow the max retry duration for this packet\r
//\r
if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {\r
+ Status = EFI_NO_RESPONSE;\r
goto ON_CLOSE;\r
}\r
\r
// Retransmit the last sent packet again.\r
//\r
Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);\r
+ TxCb->SolicitRetry = FALSE;\r
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {\r
+ TxCb->SolicitRetry = TRUE;\r
+ }\r
}\r
}\r
\r
\r
ON_CLOSE:\r
\r
- if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||\r
+ if (Dhcp6IsValidTxCb (Instance, TxCb) &&\r
+ TxCb->TxPacket != NULL &&\r
+ (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||\r
TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew ||\r
- TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm\r
+ TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)\r
) {\r
//\r
// The failure of renew/Confirm will still switch to the bound state.\r
//\r
// The failure of the others will terminate current state machine if timeout.\r
//\r
- Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE);\r
+ Dhcp6CleanupSession (Instance, Status);\r
}\r
}\r