/*++ | |
Copyright (c) 2006, 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 | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
Module Name: | |
PxeDhcp4RenewRebind.c | |
Abstract: | |
--*/ | |
#include "PxeDhcp4.h" | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
STATIC | |
INTN | |
acknak_verify ( | |
IN PXE_DHCP4_PRIVATE_DATA *Private, | |
IN DHCP4_PACKET *tx_pkt, | |
IN DHCP4_PACKET *rx_pkt, | |
IN UINTN rx_pkt_size | |
) | |
/*++ | |
Routine Description: | |
Parameters: | |
Returns: | |
-2 = ignore, stop waiting | |
-1 = ignore, keep waiting | |
0 = accept, keep waiting | |
1 = accept, stop waiting | |
--*/ | |
{ | |
EFI_STATUS efi_status; | |
DHCP4_OP *msg_type_op; | |
DHCP4_OP *srvid_op; | |
DHCP4_OP *renew_op; | |
DHCP4_OP *rebind_op; | |
DHCP4_OP *lease_time_op; | |
UINT32 magik; | |
// | |
// Verify parameters. Unused parameters are also touched | |
// to make the compiler happy. | |
// | |
ASSERT (Private); | |
ASSERT (rx_pkt); | |
if (Private == NULL || rx_pkt == NULL) { | |
return -2; | |
} | |
tx_pkt = tx_pkt; | |
rx_pkt_size = rx_pkt_size; | |
// | |
// This must be a DHCP Ack message. | |
// | |
magik = htonl (DHCP4_MAGIK_NUMBER); | |
if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) { | |
return -1; | |
} | |
efi_status = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op); | |
if (EFI_ERROR (efi_status)) { | |
return -1; | |
} | |
if (msg_type_op->len != 1) { | |
return -1; | |
} | |
if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) { | |
return -1; | |
} | |
// | |
// There must be a server identifier. | |
// | |
efi_status = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op); | |
if (EFI_ERROR (efi_status)) { | |
return -1; | |
} | |
if (srvid_op->len != 4) { | |
return -1; | |
} | |
// | |
// There should be a renewal time. | |
// If there is not, we will default to the 7/8 of the rebinding time. | |
// | |
efi_status = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op); | |
if (EFI_ERROR (efi_status)) { | |
renew_op = NULL; | |
} else if (renew_op->len != 4) { | |
renew_op = NULL; | |
} | |
// | |
// There should be a rebinding time. | |
// If there is not, we will default to 7/8 of the lease time. | |
// | |
efi_status = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op); | |
if (EFI_ERROR (efi_status)) { | |
rebind_op = NULL; | |
} else if (rebind_op->len != 4) { | |
rebind_op = NULL; | |
} | |
// | |
// There should be a lease time. | |
// If there is not, we will default to one week. | |
// | |
efi_status = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op); | |
if (EFI_ERROR (efi_status)) { | |
lease_time_op = NULL; | |
} else if (lease_time_op->len != 4) { | |
lease_time_op = NULL; | |
} | |
// | |
// Packet looks good. Double check the renew, rebind and lease times. | |
// | |
CopyMem (&Private->ServerIp, srvid_op->data, 4); | |
if (renew_op != NULL) { | |
CopyMem (&Private->RenewTime, renew_op->data, 4); | |
Private->RenewTime = htonl (Private->RenewTime); | |
} else { | |
Private->RenewTime = 0; | |
} | |
if (rebind_op != NULL) { | |
CopyMem (&Private->RebindTime, rebind_op->data, 4); | |
Private->RebindTime = htonl (Private->RebindTime); | |
} else { | |
Private->RebindTime = 0; | |
} | |
if (lease_time_op != NULL) { | |
CopyMem (&Private->LeaseTime, lease_time_op->data, 4); | |
Private->LeaseTime = htonl (Private->LeaseTime); | |
} else { | |
Private->LeaseTime = 0; | |
} | |
if (Private->LeaseTime < 60) { | |
Private->LeaseTime = 7 * 86400; | |
} | |
if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) { | |
Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8; | |
} | |
if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) { | |
Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8; | |
} | |
return 1; | |
} | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
renew_rebind ( | |
IN EFI_PXE_DHCP4_PROTOCOL *This, | |
IN UINTN seconds_timeout, | |
IN BOOLEAN renew | |
) | |
{ | |
PXE_DHCP4_PRIVATE_DATA *Private; | |
EFI_IP_ADDRESS ServerIp; | |
EFI_IP_ADDRESS client_ip; | |
EFI_IP_ADDRESS subnet_mask; | |
EFI_IP_ADDRESS gateway_ip; | |
DHCP4_PACKET Request; | |
DHCP4_PACKET AckNak; | |
DHCP4_OP *op; | |
EFI_STATUS efi_status; | |
// | |
// Check for invalid parameters. | |
// | |
if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check for proper protocol state. | |
// | |
if (This->Data == NULL) { | |
return EFI_NOT_STARTED; | |
} | |
if (!This->Data->SelectCompleted) { | |
return EFI_NOT_READY; | |
} | |
if (This->Data->IsBootp) { | |
return EFI_SUCCESS; | |
} | |
if (!This->Data->IsAck) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Get pointer to instance data. | |
// | |
Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This); | |
if (Private == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Private->PxeBc == NULL) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Copy Discover packet to temporary request packet | |
// to be used for Renew/Rebind operation. | |
// | |
CopyMem (&Request, &This->Data->Discover, sizeof (DHCP4_PACKET)); | |
CopyMem (&Request.dhcp4.ciaddr, &This->Data->AckNak.dhcp4.yiaddr, 4); | |
Request.dhcp4.flags = 0; /* Reply does not need to be broadcast. */ | |
// | |
// Change message type from discover to request. | |
// | |
efi_status = find_opt (&Request, DHCP4_MESSAGE_TYPE, 0, &op); | |
if (EFI_ERROR (efi_status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (op->len != 1) { | |
return EFI_INVALID_PARAMETER; | |
} | |
op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST; | |
// | |
// Need a subnet mask. | |
// | |
efi_status = find_opt ( | |
&This->Data->AckNak, | |
DHCP4_SUBNET_MASK, | |
0, | |
&op | |
); | |
if (EFI_ERROR (efi_status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (op->len != 4) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ZeroMem (&subnet_mask, sizeof (EFI_IP_ADDRESS)); | |
CopyMem (&subnet_mask, op->data, 4); | |
// | |
// Need a server IP address (renew) or a broadcast | |
// IP address (rebind). | |
// | |
ZeroMem (&gateway_ip, sizeof (EFI_IP_ADDRESS)); | |
if (renew) { | |
efi_status = find_opt ( | |
&This->Data->AckNak, | |
DHCP4_SERVER_IDENTIFIER, | |
0, | |
&op | |
); | |
if (EFI_ERROR (efi_status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (op->len != 4) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS)); | |
CopyMem (&ServerIp, op->data, 4); | |
// | |
// | |
// | |
if (CompareMem (&This->Data->AckNak.dhcp4.giaddr, &gateway_ip, 4)) { | |
CopyMem (&gateway_ip, &This->Data->AckNak.dhcp4.giaddr, 4); | |
} | |
} else { | |
SetMem (&ServerIp, sizeof (EFI_IP_ADDRESS), 0xFF); | |
} | |
// | |
// Need a client IP address. | |
// | |
ZeroMem (&client_ip, sizeof (EFI_IP_ADDRESS)); | |
CopyMem (&client_ip, &Request.dhcp4.ciaddr, 4); | |
// | |
// | |
// | |
efi_status = gBS->HandleProtocol ( | |
Private->Handle, | |
&gEfiPxeDhcp4CallbackProtocolGuid, | |
(VOID *) &Private->callback | |
); | |
if (EFI_ERROR (efi_status)) { | |
Private->callback = NULL; | |
} | |
Private->function = renew ? EFI_PXE_DHCP4_FUNCTION_RENEW : EFI_PXE_DHCP4_FUNCTION_REBIND; | |
// | |
// Transimit DHCP request and wait for DHCP ack... | |
// | |
efi_status = tx_rx_udp ( | |
Private, | |
&ServerIp, | |
&gateway_ip, | |
&client_ip, | |
&subnet_mask, | |
&Request, | |
&AckNak, | |
&acknak_verify, | |
seconds_timeout | |
); | |
if (EFI_ERROR (efi_status)) { | |
Private->callback = NULL; | |
return efi_status; | |
} | |
// | |
// Copy server identifier, renewal time and rebinding time | |
// from temporary ack/nak packet into cached ack/nak packet. | |
// | |
efi_status = find_opt ( | |
&This->Data->AckNak, | |
DHCP4_SERVER_IDENTIFIER, | |
0, | |
&op | |
); | |
if (!EFI_ERROR (efi_status)) { | |
if (op->len == 4) { | |
CopyMem (op->data, &Private->ServerIp, 4); | |
} | |
} | |
efi_status = find_opt (&This->Data->AckNak, DHCP4_RENEWAL_TIME, 0, &op); | |
if (!EFI_ERROR (efi_status)) { | |
if (op->len == 4) { | |
CopyMem (op->data, &Private->RenewTime, 4); | |
} | |
} | |
efi_status = find_opt (&This->Data->AckNak, DHCP4_REBINDING_TIME, 0, &op); | |
if (!EFI_ERROR (efi_status)) { | |
if (op->len == 4) { | |
CopyMem (op->data, &Private->RebindTime, 4); | |
} | |
} | |
Private->callback = NULL; | |
return efi_status; | |
} | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
EFI_STATUS | |
EFIAPI | |
PxeDhcp4Renew ( | |
IN EFI_PXE_DHCP4_PROTOCOL *This, | |
IN UINTN seconds_timeout | |
) | |
{ | |
return renew_rebind (This, seconds_timeout, TRUE); | |
} | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
EFI_STATUS | |
EFIAPI | |
PxeDhcp4Rebind ( | |
IN EFI_PXE_DHCP4_PROTOCOL *This, | |
IN UINTN seconds_timeout | |
) | |
{ | |
return renew_rebind (This, seconds_timeout, FALSE); | |
} | |
/* eof - PxeDhcp4RenewRebind.c */ |