* Made the implementation more state driven.

* Now should adhere to the specs with regard to filling the DHCP_REQUEST messages
  in BOUND/RENEWAL/REBINDING states.
* Now take over the times for renewal/rebinding state from the DHCP server, if any,
  or falls back to 2/3 and 5/6 of the original lease time.
* The lease time was accidently shortened twice (to compute the renewal time, in
  case that was missing).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19489 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-12-13 13:57:57 +00:00
parent fdad9c93d7
commit 46ff54007d
2 changed files with 109 additions and 34 deletions

View File

@ -93,7 +93,7 @@ struct dhcp_message {
uint8 hardware_address_length; uint8 hardware_address_length;
uint8 hop_count; uint8 hop_count;
uint32 transaction_id; uint32 transaction_id;
uint16 seconds_since_boot; uint16 seconds_since_start;
uint16 flags; uint16 flags;
in_addr_t client_address; in_addr_t client_address;
in_addr_t your_address; in_addr_t your_address;
@ -301,7 +301,8 @@ DHCPClient::DHCPClient(BMessenger target, const char* device)
fRunner(NULL), fRunner(NULL),
fLeaseTime(0) fLeaseTime(0)
{ {
fTransactionID = system_time(); fStartTime = system_time();
fTransactionID = (uint32)fStartTime;
fStatus = get_mac_address(device, fMAC); fStatus = get_mac_address(device, fMAC);
if (fStatus < B_OK) if (fStatus < B_OK)
@ -328,7 +329,7 @@ DHCPClient::~DHCPClient()
// release lease // release lease
dhcp_message release(DHCP_RELEASE); dhcp_message release(DHCP_RELEASE);
_PrepareMessage(release); _PrepareMessage(release, BOUND);
_SendMessage(socket, release, fServer); _SendMessage(socket, release, fServer);
close(socket); close(socket);
@ -373,9 +374,10 @@ DHCPClient::_Negotiate(dhcp_state state)
int option = 1; int option = 1;
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
dhcp_state initialState = state;
bigtime_t previousLeaseTime = fLeaseTime; bigtime_t previousLeaseTime = fLeaseTime;
fLeaseTime = 0; fLeaseTime = 0;
fRenewalTime = 0;
fRebindingTime = 0;
status_t status = B_ERROR; status_t status = B_ERROR;
time_t timeout; time_t timeout;
@ -383,19 +385,17 @@ DHCPClient::_Negotiate(dhcp_state state)
_ResetTimeout(socket, timeout, tries); _ResetTimeout(socket, timeout, tries);
dhcp_message discover(DHCP_DISCOVER); dhcp_message discover(DHCP_DISCOVER);
_PrepareMessage(discover); _PrepareMessage(discover, state);
dhcp_message request(DHCP_REQUEST); dhcp_message request(DHCP_REQUEST);
_PrepareMessage(request); _PrepareMessage(request, state);
if (state == INIT || state == REQUESTING) { // send discover/request message
// send discover message status = _SendMessage(socket, state == INIT ? discover : request,
status = _SendMessage(socket, state == INIT ? discover : request, state != RENEWAL ? broadcast : fServer);
state == INIT ? broadcast : fServer); if (status < B_OK) {
if (status < B_OK) { close(socket);
close(socket); return status;
return status;
}
} }
// receive loop until we've got an offer and acknowledged it // receive loop until we've got an offer and acknowledged it
@ -414,8 +414,8 @@ printf("recvfrom returned: %ld, %s\n", bytesReceived, strerror(errno));
if (state == INIT) if (state == INIT)
status = _SendMessage(socket, discover, broadcast); status = _SendMessage(socket, discover, broadcast);
if (state == REQUESTING) else
status = _SendMessage(socket, request, initialState == INIT status = _SendMessage(socket, request, state != RENEWAL
? broadcast : fServer); ? broadcast : fServer);
if (status < B_OK) if (status < B_OK)
@ -462,7 +462,7 @@ printf("recvfrom returned: %ld, %s\n", bytesReceived, strerror(errno));
_ResetTimeout(socket, timeout, tries); _ResetTimeout(socket, timeout, tries);
state = REQUESTING; state = REQUESTING;
_PrepareMessage(request); _PrepareMessage(request, state);
status = _SendMessage(socket, request, broadcast); status = _SendMessage(socket, request, broadcast);
// we're sending a broadcast so that all potential offers get an answer // we're sending a broadcast so that all potential offers get an answer
@ -471,7 +471,7 @@ printf("recvfrom returned: %ld, %s\n", bytesReceived, strerror(errno));
case DHCP_ACK: case DHCP_ACK:
{ {
if (state != REQUESTING) if (state != REQUESTING && state != REBINDING && state != RENEWAL)
continue; continue;
// TODO: we might want to configure the stuff, don't we? // TODO: we might want to configure the stuff, don't we?
@ -507,11 +507,24 @@ printf("recvfrom returned: %ld, %s\n", bytesReceived, strerror(errno));
if (status == B_OK && fLeaseTime > 0) { if (status == B_OK && fLeaseTime > 0) {
// notify early enough when the lease is // notify early enough when the lease is
_RestartLease(fLeaseTime * 5/6); if (fRenewalTime == 0)
fLeaseTime += system_time(); fRenewalTime = fLeaseTime * 2/3;
// make lease time absolute if (fRebindingTime == 0)
} else fRebindingTime = fLeaseTime * 5/6;
_RestartLease(fRenewalTime);
bigtime_t now = system_time();
fLeaseTime += now;
fRenewalTime += now;
fRebindingTime += now;
// make lease times absolute
} else {
fLeaseTime = previousLeaseTime; fLeaseTime = previousLeaseTime;
bigtime_t now = system_time();
fRenewalTime = (fLeaseTime - now) * 2/3 + now;
fRebindingTime = (fLeaseTime - now) * 5/6 + now;
}
return status; return status;
} }
@ -523,9 +536,8 @@ DHCPClient::_RestartLease(bigtime_t leaseTime)
if (leaseTime == 0) if (leaseTime == 0)
return; return;
printf("lease expires in %Ld seconds\n", leaseTime / 1000000);
BMessage lease(kMsgLeaseTime); BMessage lease(kMsgLeaseTime);
fRunner = new BMessageRunner(this, &lease, leaseTime * 5/6, 1); fRunner = new BMessageRunner(this, &lease, leaseTime, 1);
} }
@ -563,15 +575,20 @@ DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address)
case OPTION_SERVER_ADDRESS: case OPTION_SERVER_ADDRESS:
fServer.sin_addr.s_addr = *(in_addr_t*)data; fServer.sin_addr.s_addr = *(in_addr_t*)data;
break; break;
case OPTION_ADDRESS_LEASE_TIME: case OPTION_ADDRESS_LEASE_TIME:
printf("lease time of %lu seconds\n", htonl(*(uint32*)data)); printf("lease time of %lu seconds\n", htonl(*(uint32*)data));
fLeaseTime = htonl(*(uint32*)data) * 1000000LL; fLeaseTime = htonl(*(uint32*)data) * 1000000LL;
break; break;
case OPTION_RENEWAL_TIME: case OPTION_RENEWAL_TIME:
printf("renewal time of %lu seconds\n",
htonl(*(uint32*)data));
fRenewalTime = htonl(*(uint32*)data) * 1000000LL;
break;
case OPTION_REBINDING_TIME: case OPTION_REBINDING_TIME:
printf("renewal/rebinding (%lu) time of %lu seconds\n", printf("rebinding time of %lu seconds\n",
(uint32)option, htonl(*(uint32*)data)); htonl(*(uint32*)data));
fRebindingTime = htonl(*(uint32*)data) * 1000000LL;
break; break;
case OPTION_HOST_NAME: case OPTION_HOST_NAME:
@ -604,16 +621,18 @@ DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address)
void void
DHCPClient::_PrepareMessage(dhcp_message& message) DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
{ {
message.opcode = BOOT_REQUEST; message.opcode = BOOT_REQUEST;
message.hardware_type = ARP_HARDWARE_TYPE_ETHER; message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
message.hardware_address_length = 6; message.hardware_address_length = 6;
message.transaction_id = htonl(fTransactionID); message.transaction_id = htonl(fTransactionID);
message.seconds_since_boot = htons(min_c(system_time() / 1000000LL, 65535)); message.seconds_since_start = htons(min_c((fStartTime - system_time()) / 1000000LL, 65535));
memcpy(message.mac_address, fMAC, 6); memcpy(message.mac_address, fMAC, 6);
switch (message.Type()) { message_type type = message.Type();
switch (type) {
case DHCP_REQUEST: case DHCP_REQUEST:
case DHCP_RELEASE: case DHCP_RELEASE:
{ {
@ -624,7 +643,14 @@ DHCPClient::_PrepareMessage(dhcp_message& message)
(uint16)htons(sizeof(dhcp_message))); (uint16)htons(sizeof(dhcp_message)));
next = message.PutOption(next, OPTION_SERVER_ADDRESS, next = message.PutOption(next, OPTION_SERVER_ADDRESS,
(uint32)fServer.sin_addr.s_addr); (uint32)fServer.sin_addr.s_addr);
next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, fAssignedAddress);
// In RENEWAL or REBINDING state, we must set the client_address field, and not
// use OPTION_REQUEST_IP_ADDRESS for DHCP_REQUEST messages
if (type == DHCP_REQUEST && (state == INIT || state == REQUESTING))
next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, fAssignedAddress);
else
message.client_address = fAssignedAddress;
next = message.PutOption(next, OPTION_END); next = message.PutOption(next, OPTION_END);
break; break;
} }
@ -699,14 +725,56 @@ DHCPClient::_SendMessage(int socket, dhcp_message& message, sockaddr_in& address
} }
dhcp_state
DHCPClient::_CurrentState() const
{
bigtime_t now = system_time();
if (now > fLeaseTime || fStatus < B_OK)
return INIT;
if (now >= fRebindingTime)
return REBINDING;
if (now >= fRenewalTime)
return RENEWAL;
return BOUND;
}
void void
DHCPClient::MessageReceived(BMessage* message) DHCPClient::MessageReceived(BMessage* message)
{ {
switch (message->what) { switch (message->what) {
case kMsgLeaseTime: case kMsgLeaseTime:
if (_Negotiate(REQUESTING) != B_OK) {
_RestartLease((fLeaseTime - system_time()) / 10); dhcp_state state = _CurrentState();
bigtime_t next;
if (_Negotiate(state) == B_OK) {
switch (state) {
case RENEWAL:
next = fRebindingTime;
break;
case REBINDING:
default:
next = fRenewalTime;
break;
}
} else {
switch (state) {
case RENEWAL:
next = (fLeaseTime - fRebindingTime) / 4 + system_time();
break;
case REBINDING:
default:
next = (fLeaseTime - fRenewalTime) / 4 + system_time();
break;
}
}
_RestartLease(next - system_time());
break; break;
}
default: default:
BHandler::MessageReceived(message); BHandler::MessageReceived(message);

View File

@ -21,6 +21,9 @@ class dhcp_message;
enum dhcp_state { enum dhcp_state {
INIT, INIT,
REQUESTING, REQUESTING,
BOUND,
RENEWAL,
REBINDING,
ACKNOWLEDGED, ACKNOWLEDGED,
}; };
@ -37,8 +40,9 @@ class DHCPClient : public BHandler {
private: private:
status_t _Negotiate(dhcp_state state); status_t _Negotiate(dhcp_state state);
void _ParseOptions(dhcp_message& message, BMessage& address); void _ParseOptions(dhcp_message& message, BMessage& address);
void _PrepareMessage(dhcp_message& message); void _PrepareMessage(dhcp_message& message, dhcp_state state);
status_t _SendMessage(int socket, dhcp_message& message, sockaddr_in& address) const; status_t _SendMessage(int socket, dhcp_message& message, sockaddr_in& address) const;
dhcp_state _CurrentState() const;
void _ResetTimeout(int socket, time_t& timeout, uint32& tries); void _ResetTimeout(int socket, time_t& timeout, uint32& tries);
bool _TimeoutShift(int socket, time_t& timeout, uint32& tries); bool _TimeoutShift(int socket, time_t& timeout, uint32& tries);
void _RestartLease(bigtime_t lease); void _RestartLease(bigtime_t lease);
@ -53,6 +57,9 @@ class DHCPClient : public BHandler {
uint32 fTransactionID; uint32 fTransactionID;
in_addr_t fAssignedAddress; in_addr_t fAssignedAddress;
sockaddr_in fServer; sockaddr_in fServer;
bigtime_t fStartTime;
bigtime_t fRenewalTime;
bigtime_t fRebindingTime;
bigtime_t fLeaseTime; bigtime_t fLeaseTime;
status_t fStatus; status_t fStatus;
}; };