Skip to content

Commit 9f3438c

Browse files
committed
Adding Ethernet.maintain() to update DHCP leases (Peter Magnusson)
returns: 0: nothing happened 1: renew failed 2: renew success 3: rebind fail 4: rebind success http://code.google.com/p/arduino/issues/detail?id=716
1 parent a6eb9ea commit 9f3438c

File tree

4 files changed

+193
-25
lines changed

4 files changed

+193
-25
lines changed

libraries/Ethernet/Dhcp.cpp

Lines changed: 139 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,33 @@
1111

1212
int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout)
1313
{
14-
uint8_t dhcp_state = STATE_DHCP_START;
15-
uint8_t messageType = 0;
16-
17-
// zero out _dhcpMacAddr, _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp
18-
memset(_dhcpMacAddr, 0, 26);
14+
_dhcpLeaseTime=0;
15+
_dhcpT1=0;
16+
_dhcpT2=0;
17+
_lastCheck=0;
18+
_timeout = timeout;
19+
_responseTimeout = responseTimeout;
20+
21+
// zero out _dhcpMacAddr
22+
memset(_dhcpMacAddr, 0, 6);
23+
reset_DHCP_lease();
1924

2025
memcpy((void*)_dhcpMacAddr, (void*)mac, 6);
26+
_dhcp_state = STATE_DHCP_START;
27+
return request_DHCP_lease();
28+
}
29+
30+
void DhcpClass::reset_DHCP_lease(){
31+
// zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp
32+
memset(_dhcpLocalIp, 0, 20);
33+
}
34+
35+
//return:0 on error, 1 if request is sent and response is received
36+
int DhcpClass::request_DHCP_lease(){
37+
38+
uint8_t messageType = 0;
39+
40+
2141

2242
// Pick an initial transaction ID
2343
_dhcpTransactionId = random(1UL, 2000UL);
@@ -35,55 +55,75 @@ int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long
3555

3656
unsigned long startTime = millis();
3757

38-
while(dhcp_state != STATE_DHCP_LEASED)
58+
while(_dhcp_state != STATE_DHCP_LEASED)
3959
{
40-
if(dhcp_state == STATE_DHCP_START)
60+
if(_dhcp_state == STATE_DHCP_START)
4161
{
4262
_dhcpTransactionId++;
4363

4464
send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000));
45-
dhcp_state = STATE_DHCP_DISCOVER;
65+
_dhcp_state = STATE_DHCP_DISCOVER;
66+
}
67+
else if(_dhcp_state == STATE_DHCP_REREQUEST){
68+
_dhcpTransactionId++;
69+
send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000));
70+
_dhcp_state = STATE_DHCP_REQUEST;
4671
}
47-
else if(dhcp_state == STATE_DHCP_DISCOVER)
72+
else if(_dhcp_state == STATE_DHCP_DISCOVER)
4873
{
4974
uint32_t respId;
50-
messageType = parseDHCPResponse(responseTimeout, respId);
75+
messageType = parseDHCPResponse(_responseTimeout, respId);
5176
if(messageType == DHCP_OFFER)
5277
{
5378
// We'll use the transaction ID that the offer came with,
5479
// rather than the one we were up to
5580
_dhcpTransactionId = respId;
5681
send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000));
57-
dhcp_state = STATE_DHCP_REQUEST;
82+
_dhcp_state = STATE_DHCP_REQUEST;
5883
}
5984
}
60-
else if(dhcp_state == STATE_DHCP_REQUEST)
85+
else if(_dhcp_state == STATE_DHCP_REQUEST)
6186
{
6287
uint32_t respId;
63-
messageType = parseDHCPResponse(responseTimeout, respId);
88+
messageType = parseDHCPResponse(_responseTimeout, respId);
6489
if(messageType == DHCP_ACK)
6590
{
66-
dhcp_state = STATE_DHCP_LEASED;
91+
_dhcp_state = STATE_DHCP_LEASED;
6792
result = 1;
93+
//use default lease time if we didn't get it
94+
if(_dhcpLeaseTime == 0){
95+
_dhcpLeaseTime = DEFAULT_LEASE;
96+
}
97+
//calculate T1 & T2 if we didn't get it
98+
if(_dhcpT1 == 0){
99+
//T1 should be 50% of _dhcpLeaseTime
100+
_dhcpT1 = _dhcpLeaseTime >> 1;
101+
}
102+
if(_dhcpT2 == 0){
103+
//T2 should be 87.5% (7/8ths) of _dhcpLeaseTime
104+
_dhcpT2 = _dhcpT1 << 1;
105+
}
106+
_renewInSec = _dhcpT1;
107+
_rebindInSec = _dhcpT2;
68108
}
69109
else if(messageType == DHCP_NAK)
70-
dhcp_state = STATE_DHCP_START;
110+
_dhcp_state = STATE_DHCP_START;
71111
}
72112

73113
if(messageType == 255)
74114
{
75115
messageType = 0;
76-
dhcp_state = STATE_DHCP_START;
116+
_dhcp_state = STATE_DHCP_START;
77117
}
78118

79-
if(result != 1 && ((millis() - startTime) > timeout))
119+
if(result != 1 && ((millis() - startTime) > _timeout))
80120
break;
81121
}
82122

83123
// We're done with the socket now
84124
_dhcpUdpSocket.stop();
85125
_dhcpTransactionId++;
86-
126+
87127
return result;
88128
}
89129

@@ -302,8 +342,26 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr
302342
}
303343
}
304344
break;
305-
345+
346+
case dhcpT1value :
347+
opt_len = _dhcpUdpSocket.read();
348+
_dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1));
349+
_dhcpT1 = ntohl(_dhcpT1);
350+
break;
351+
352+
case dhcpT2value :
353+
opt_len = _dhcpUdpSocket.read();
354+
_dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2));
355+
_dhcpT2 = ntohl(_dhcpT2);
356+
break;
357+
306358
case dhcpIPaddrLeaseTime :
359+
opt_len = _dhcpUdpSocket.read();
360+
_dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime));
361+
_dhcpLeaseTime = ntohl(_dhcpLeaseTime);
362+
_renewInSec = _dhcpLeaseTime;
363+
break;
364+
307365
default :
308366
opt_len = _dhcpUdpSocket.read();
309367
// Skip over the rest of this option
@@ -322,6 +380,68 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr
322380
return type;
323381
}
324382

383+
384+
/*
385+
returns:
386+
0/DHCP_CHECK_NONE: nothing happened
387+
1/DHCP_CHECK_RENEW_FAIL: renew failed
388+
2/DHCP_CHECK_RENEW_OK: renew success
389+
3/DHCP_CHECK_REBIND_FAIL: rebind fail
390+
4/DHCP_CHECK_REBIND_OK: rebind success
391+
*/
392+
int DhcpClass::checkLease(){
393+
//this uses a signed / unsigned trick to deal with millis overflow
394+
unsigned long now = millis();
395+
signed long snow = (long)now;
396+
int rc=DHCP_CHECK_NONE;
397+
if (_lastCheck != 0){
398+
signed long factor;
399+
//calc how many ms past the timeout we are
400+
factor = snow - (long)_secTimeout;
401+
//if on or passed the timeout, reduce the counters
402+
if ( factor >= 0 ){
403+
//next timeout should be now plus 1000 ms minus parts of second in factor
404+
_secTimeout = snow + 1000 - factor % 1000;
405+
//how many seconds late are we, minimum 1
406+
factor = factor / 1000 +1;
407+
408+
//reduce the counters by that mouch
409+
//if we can assume that the cycle time (factor) is fairly constant
410+
//and if the remainder is less than cycle time * 2
411+
//do it early instead of late
412+
if(_renewInSec < factor*2 )
413+
_renewInSec = 0;
414+
else
415+
_renewInSec -= factor;
416+
417+
if(_rebindInSec < factor*2 )
418+
_rebindInSec = 0;
419+
else
420+
_rebindInSec -= factor;
421+
}
422+
423+
//if we have a lease but should renew, do it
424+
if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){
425+
_dhcp_state = STATE_DHCP_REREQUEST;
426+
rc = 1 + request_DHCP_lease();
427+
}
428+
429+
//if we have a lease or is renewing but should bind, do it
430+
if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){
431+
//this should basically restart completely
432+
_dhcp_state = STATE_DHCP_START;
433+
reset_DHCP_lease();
434+
rc = 3 + request_DHCP_lease();
435+
}
436+
}
437+
else{
438+
_secTimeout = snow + 1000;
439+
}
440+
441+
_lastCheck = now;
442+
return rc;
443+
}
444+
325445
IPAddress DhcpClass::getLocalIp()
326446
{
327447
return IPAddress(_dhcpLocalIp);

libraries/Ethernet/Dhcp.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@
4545
#define MAX_DHCP_OPT 16
4646

4747
#define HOST_NAME "WIZnet"
48+
#define DEFAULT_LEASE (900) //default lease time in seconds
49+
50+
#define DHCP_CHECK_NONE (0)
51+
#define DHCP_CHECK_RENEW_FAIL (1)
52+
#define DHCP_CHECK_RENEW_OK (2)
53+
#define DHCP_CHECK_REBIND_FAIL (3)
54+
#define DHCP_CHECK_REBIND_OK (4)
4855

4956
enum
5057
{
@@ -139,8 +146,19 @@ class DhcpClass {
139146
uint8_t _dhcpGatewayIp[4];
140147
uint8_t _dhcpDhcpServerIp[4];
141148
uint8_t _dhcpDnsServerIp[4];
149+
uint32_t _dhcpLeaseTime;
150+
uint32_t _dhcpT1, _dhcpT2;
151+
signed long _renewInSec;
152+
signed long _rebindInSec;
153+
signed long _lastCheck;
154+
unsigned long _timeout;
155+
unsigned long _responseTimeout;
156+
unsigned long _secTimeout;
157+
uint8_t _dhcp_state;
142158
EthernetUDP _dhcpUdpSocket;
143159

160+
int request_DHCP_lease();
161+
void reset_DHCP_lease();
144162
void presend_DHCP();
145163
void send_DHCP_MESSAGE(uint8_t, uint16_t);
146164
void printByte(char *, uint8_t);
@@ -154,6 +172,7 @@ class DhcpClass {
154172
IPAddress getDnsServerIp();
155173

156174
int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
175+
int checkLease();
157176
};
158177

159178
#endif

libraries/Ethernet/Ethernet.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,24 @@ uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = {
1010

1111
int EthernetClass::begin(uint8_t *mac_address)
1212
{
13-
DhcpClass dhcp;
13+
_dhcp = new DhcpClass();
14+
1415

1516
// Initialise the basic info
1617
W5100.init();
1718
W5100.setMACAddress(mac_address);
1819
W5100.setIPAddress(IPAddress(0,0,0,0).raw_address());
1920

2021
// Now try to get our config info from a DHCP server
21-
int ret = dhcp.beginWithDHCP(mac_address);
22+
int ret = _dhcp->beginWithDHCP(mac_address);
2223
if(ret == 1)
2324
{
2425
// We've successfully found a DHCP server and got our configuration info, so set things
2526
// accordingly
26-
W5100.setIPAddress(dhcp.getLocalIp().raw_address());
27-
W5100.setGatewayIp(dhcp.getGatewayIp().raw_address());
28-
W5100.setSubnetMask(dhcp.getSubnetMask().raw_address());
29-
_dnsServerAddress = dhcp.getDnsServerIp();
27+
W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
28+
W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
29+
W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
30+
_dnsServerAddress = _dhcp->getDnsServerIp();
3031
}
3132

3233
return ret;
@@ -66,6 +67,31 @@ void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server
6667
_dnsServerAddress = dns_server;
6768
}
6869

70+
int EthernetClass::maintain(){
71+
int rc = DHCP_CHECK_NONE;
72+
if(_dhcp != NULL){
73+
//we have a pointer to dhcp, use it
74+
rc = _dhcp->checkLease();
75+
switch ( rc ){
76+
case DHCP_CHECK_NONE:
77+
//nothing done
78+
break;
79+
case DHCP_CHECK_RENEW_OK:
80+
case DHCP_CHECK_REBIND_OK:
81+
//we might have got a new IP.
82+
W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
83+
W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
84+
W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
85+
_dnsServerAddress = _dhcp->getDnsServerIp();
86+
break;
87+
default:
88+
//this is actually a error, it will retry though
89+
break;
90+
}
91+
}
92+
return rc;
93+
}
94+
6995
IPAddress EthernetClass::localIP()
7096
{
7197
IPAddress ret;

libraries/Ethernet/Ethernet.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
#include "IPAddress.h"
77
#include "EthernetClient.h"
88
#include "EthernetServer.h"
9+
#include "Dhcp.h"
910

1011
#define MAX_SOCK_NUM 4
1112

1213
class EthernetClass {
1314
private:
1415
IPAddress _dnsServerAddress;
16+
DhcpClass* _dhcp;
1517
public:
1618
static uint8_t _state[MAX_SOCK_NUM];
1719
static uint16_t _server_port[MAX_SOCK_NUM];
@@ -23,6 +25,7 @@ class EthernetClass {
2325
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server);
2426
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
2527
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);
28+
int maintain();
2629

2730
IPAddress localIP();
2831
IPAddress subnetMask();

0 commit comments

Comments
 (0)