1 // DHCP Library v0.3 - April 25, 2009
2 // Author: Jordan Terrell - blog.jordanterrell.com
3
4 #include "utility/w5100.h"
5
6 #include <string.h>
7 #include <stdlib.h>
8 #include "Dhcp.h"
9 #include "Arduino.h"
10 #include "utility/util.h"
11
beginWithDHCP(uint8_t * mac,unsigned long timeout,unsigned long responseTimeout)12 int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout)
13 {
14 _dhcpLeaseTime=0;
15 _dhcpT1=0;
16 _dhcpT2=0;
17 _timeout = timeout;
18 _responseTimeout = responseTimeout;
19
20 // zero out _dhcpMacAddr
21 memset(_dhcpMacAddr, 0, 6);
22 reset_DHCP_lease();
23
24 memcpy((void*)_dhcpMacAddr, (void*)mac, 6);
25 _dhcp_state = STATE_DHCP_START;
26 return request_DHCP_lease();
27 }
28
reset_DHCP_lease()29 void DhcpClass::reset_DHCP_lease(){
30 // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp
31 memset(_dhcpLocalIp, 0, 20);
32 }
33
34 //return:0 on error, 1 if request is sent and response is received
request_DHCP_lease()35 int DhcpClass::request_DHCP_lease(){
36
37 uint8_t messageType = 0;
38
39
40
41 // Pick an initial transaction ID
42 _dhcpTransactionId = random(1UL, 2000UL);
43 _dhcpInitialTransactionId = _dhcpTransactionId;
44
45 _dhcpUdpSocket.stop();
46 if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0)
47 {
48 // Couldn't get a socket
49 return 0;
50 }
51
52 presend_DHCP();
53
54 int result = 0;
55
56 unsigned long startTime = millis();
57
58 while(_dhcp_state != STATE_DHCP_LEASED)
59 {
60 if(_dhcp_state == STATE_DHCP_START)
61 {
62 _dhcpTransactionId++;
63
64 send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000));
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;
71 }
72 else if(_dhcp_state == STATE_DHCP_DISCOVER)
73 {
74 uint32_t respId;
75 messageType = parseDHCPResponse(_responseTimeout, respId);
76 if(messageType == DHCP_OFFER)
77 {
78 // We'll use the transaction ID that the offer came with,
79 // rather than the one we were up to
80 _dhcpTransactionId = respId;
81 send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000));
82 _dhcp_state = STATE_DHCP_REQUEST;
83 }
84 }
85 else if(_dhcp_state == STATE_DHCP_REQUEST)
86 {
87 uint32_t respId;
88 messageType = parseDHCPResponse(_responseTimeout, respId);
89 if(messageType == DHCP_ACK)
90 {
91 _dhcp_state = STATE_DHCP_LEASED;
92 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 = _dhcpLeaseTime - (_dhcpLeaseTime >> 3);
105 }
106 _renewInSec = _dhcpT1;
107 _rebindInSec = _dhcpT2;
108 }
109 else if(messageType == DHCP_NAK)
110 _dhcp_state = STATE_DHCP_START;
111 }
112
113 if(messageType == 255)
114 {
115 messageType = 0;
116 _dhcp_state = STATE_DHCP_START;
117 }
118
119 if(result != 1 && ((millis() - startTime) > _timeout))
120 break;
121 }
122
123 // We're done with the socket now
124 _dhcpUdpSocket.stop();
125 _dhcpTransactionId++;
126
127 _lastCheckLeaseMillis = millis();
128 return result;
129 }
130
presend_DHCP()131 void DhcpClass::presend_DHCP()
132 {
133 }
134
send_DHCP_MESSAGE(uint8_t messageType,uint16_t secondsElapsed)135 void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed)
136 {
137 uint8_t buffer[32];
138 memset(buffer, 0, 32);
139 IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address
140
141 if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT))
142 {
143 // FIXME Need to return errors
144 return;
145 }
146
147 buffer[0] = DHCP_BOOTREQUEST; // op
148 buffer[1] = DHCP_HTYPE10MB; // htype
149 buffer[2] = DHCP_HLENETHERNET; // hlen
150 buffer[3] = DHCP_HOPS; // hops
151
152 // xid
153 unsigned long xid = htonl(_dhcpTransactionId);
154 memcpy(buffer + 4, &(xid), 4);
155
156 // 8, 9 - seconds elapsed
157 buffer[8] = ((secondsElapsed & 0xff00) >> 8);
158 buffer[9] = (secondsElapsed & 0x00ff);
159
160 // flags
161 unsigned short flags = htons(DHCP_FLAGSBROADCAST);
162 memcpy(buffer + 10, &(flags), 2);
163
164 // ciaddr: already zeroed
165 // yiaddr: already zeroed
166 // siaddr: already zeroed
167 // giaddr: already zeroed
168
169 //put data in W5100 transmit buffer
170 _dhcpUdpSocket.write(buffer, 28);
171
172 memset(buffer, 0, 32); // clear local buffer
173
174 memcpy(buffer, _dhcpMacAddr, 6); // chaddr
175
176 //put data in W5100 transmit buffer
177 _dhcpUdpSocket.write(buffer, 16);
178
179 memset(buffer, 0, 32); // clear local buffer
180
181 // leave zeroed out for sname && file
182 // put in W5100 transmit buffer x 6 (192 bytes)
183
184 for(int i = 0; i < 6; i++) {
185 _dhcpUdpSocket.write(buffer, 32);
186 }
187
188 // OPT - Magic Cookie
189 buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF);
190 buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF);
191 buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF);
192 buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF);
193
194 // OPT - message type
195 buffer[4] = dhcpMessageType;
196 buffer[5] = 0x01;
197 buffer[6] = messageType; //DHCP_REQUEST;
198
199 // OPT - client identifier
200 buffer[7] = dhcpClientIdentifier;
201 buffer[8] = 0x07;
202 buffer[9] = 0x01;
203 memcpy(buffer + 10, _dhcpMacAddr, 6);
204
205 // OPT - host name
206 buffer[16] = hostName;
207 buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address
208 strcpy((char*)&(buffer[18]), HOST_NAME);
209
210 printByte((char*)&(buffer[24]), _dhcpMacAddr[3]);
211 printByte((char*)&(buffer[26]), _dhcpMacAddr[4]);
212 printByte((char*)&(buffer[28]), _dhcpMacAddr[5]);
213
214 //put data in W5100 transmit buffer
215 _dhcpUdpSocket.write(buffer, 30);
216
217 if(messageType == DHCP_REQUEST)
218 {
219 buffer[0] = dhcpRequestedIPaddr;
220 buffer[1] = 0x04;
221 buffer[2] = _dhcpLocalIp[0];
222 buffer[3] = _dhcpLocalIp[1];
223 buffer[4] = _dhcpLocalIp[2];
224 buffer[5] = _dhcpLocalIp[3];
225
226 buffer[6] = dhcpServerIdentifier;
227 buffer[7] = 0x04;
228 buffer[8] = _dhcpDhcpServerIp[0];
229 buffer[9] = _dhcpDhcpServerIp[1];
230 buffer[10] = _dhcpDhcpServerIp[2];
231 buffer[11] = _dhcpDhcpServerIp[3];
232
233 //put data in W5100 transmit buffer
234 _dhcpUdpSocket.write(buffer, 12);
235 }
236
237 buffer[0] = dhcpParamRequest;
238 buffer[1] = 0x06;
239 buffer[2] = subnetMask;
240 buffer[3] = routersOnSubnet;
241 buffer[4] = dns;
242 buffer[5] = domainName;
243 buffer[6] = dhcpT1value;
244 buffer[7] = dhcpT2value;
245 buffer[8] = endOption;
246
247 //put data in W5100 transmit buffer
248 _dhcpUdpSocket.write(buffer, 9);
249
250 _dhcpUdpSocket.endPacket();
251 }
252
parseDHCPResponse(unsigned long responseTimeout,uint32_t & transactionId)253 uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId)
254 {
255 uint8_t type = 0;
256 uint8_t opt_len = 0;
257
258 unsigned long startTime = millis();
259
260 while(_dhcpUdpSocket.parsePacket() <= 0)
261 {
262 if((millis() - startTime) > responseTimeout)
263 {
264 return 255;
265 }
266 delay(50);
267 }
268 // start reading in the packet
269 RIP_MSG_FIXED fixedMsg;
270 _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED));
271
272 if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT)
273 {
274 transactionId = ntohl(fixedMsg.xid);
275 if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId))
276 {
277 // Need to read the rest of the packet here regardless
278 _dhcpUdpSocket.flush();
279 return 0;
280 }
281
282 memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4);
283
284 // Skip to the option part
285 // Doing this a byte at a time so we don't have to put a big buffer
286 // on the stack (as we don't have lots of memory lying around)
287 for (int i =0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++)
288 {
289 _dhcpUdpSocket.read(); // we don't care about the returned byte
290 }
291
292 while (_dhcpUdpSocket.available() > 0)
293 {
294 switch (_dhcpUdpSocket.read())
295 {
296 case endOption :
297 break;
298
299 case padOption :
300 break;
301
302 case dhcpMessageType :
303 opt_len = _dhcpUdpSocket.read();
304 type = _dhcpUdpSocket.read();
305 break;
306
307 case subnetMask :
308 opt_len = _dhcpUdpSocket.read();
309 _dhcpUdpSocket.read(_dhcpSubnetMask, 4);
310 break;
311
312 case routersOnSubnet :
313 opt_len = _dhcpUdpSocket.read();
314 _dhcpUdpSocket.read(_dhcpGatewayIp, 4);
315 for (int i = 0; i < opt_len-4; i++)
316 {
317 _dhcpUdpSocket.read();
318 }
319 break;
320
321 case dns :
322 opt_len = _dhcpUdpSocket.read();
323 _dhcpUdpSocket.read(_dhcpDnsServerIp, 4);
324 for (int i = 0; i < opt_len-4; i++)
325 {
326 _dhcpUdpSocket.read();
327 }
328 break;
329
330 case dhcpServerIdentifier :
331 opt_len = _dhcpUdpSocket.read();
332 if ((_dhcpDhcpServerIp[0] == 0 && _dhcpDhcpServerIp[1] == 0 &&
333 _dhcpDhcpServerIp[2] == 0 && _dhcpDhcpServerIp[3] == 0) ||
334 IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP())
335 {
336 _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp));
337 }
338 else
339 {
340 // Skip over the rest of this option
341 while (opt_len--)
342 {
343 _dhcpUdpSocket.read();
344 }
345 }
346 break;
347
348 case dhcpT1value :
349 opt_len = _dhcpUdpSocket.read();
350 _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1));
351 _dhcpT1 = ntohl(_dhcpT1);
352 break;
353
354 case dhcpT2value :
355 opt_len = _dhcpUdpSocket.read();
356 _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2));
357 _dhcpT2 = ntohl(_dhcpT2);
358 break;
359
360 case dhcpIPaddrLeaseTime :
361 opt_len = _dhcpUdpSocket.read();
362 _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime));
363 _dhcpLeaseTime = ntohl(_dhcpLeaseTime);
364 _renewInSec = _dhcpLeaseTime;
365 break;
366
367 default :
368 opt_len = _dhcpUdpSocket.read();
369 // Skip over the rest of this option
370 while (opt_len--)
371 {
372 _dhcpUdpSocket.read();
373 }
374 break;
375 }
376 }
377 }
378
379 // Need to skip to end of the packet regardless here
380 _dhcpUdpSocket.flush();
381
382 return type;
383 }
384
385
386 /*
387 returns:
388 0/DHCP_CHECK_NONE: nothing happened
389 1/DHCP_CHECK_RENEW_FAIL: renew failed
390 2/DHCP_CHECK_RENEW_OK: renew success
391 3/DHCP_CHECK_REBIND_FAIL: rebind fail
392 4/DHCP_CHECK_REBIND_OK: rebind success
393 */
checkLease()394 int DhcpClass::checkLease(){
395 int rc = DHCP_CHECK_NONE;
396
397 unsigned long now = millis();
398 unsigned long elapsed = now - _lastCheckLeaseMillis;
399
400 // if more then one sec passed, reduce the counters accordingly
401 if (elapsed >= 1000) {
402 // set the new timestamps
403 _lastCheckLeaseMillis = now - (elapsed % 1000);
404 elapsed = elapsed / 1000;
405
406 // decrease the counters by elapsed seconds
407 // we assume that the cycle time (elapsed) is fairly constant
408 // if the remainder is less than cycle time * 2
409 // do it early instead of late
410 if (_renewInSec < elapsed * 2)
411 _renewInSec = 0;
412 else
413 _renewInSec -= elapsed;
414
415 if (_rebindInSec < elapsed * 2)
416 _rebindInSec = 0;
417 else
418 _rebindInSec -= elapsed;
419 }
420
421 // if we have a lease but should renew, do it
422 if (_renewInSec == 0 &&_dhcp_state == STATE_DHCP_LEASED) {
423 _dhcp_state = STATE_DHCP_REREQUEST;
424 rc = 1 + request_DHCP_lease();
425 }
426
427 // if we have a lease or is renewing but should bind, do it
428 if (_rebindInSec == 0 && (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START)) {
429 // this should basically restart completely
430 _dhcp_state = STATE_DHCP_START;
431 reset_DHCP_lease();
432 rc = 3 + request_DHCP_lease();
433 }
434 return rc;
435 }
436
getLocalIp()437 IPAddress DhcpClass::getLocalIp()
438 {
439 return IPAddress(_dhcpLocalIp);
440 }
441
getSubnetMask()442 IPAddress DhcpClass::getSubnetMask()
443 {
444 return IPAddress(_dhcpSubnetMask);
445 }
446
getGatewayIp()447 IPAddress DhcpClass::getGatewayIp()
448 {
449 return IPAddress(_dhcpGatewayIp);
450 }
451
getDhcpServerIp()452 IPAddress DhcpClass::getDhcpServerIp()
453 {
454 return IPAddress(_dhcpDhcpServerIp);
455 }
456
getDnsServerIp()457 IPAddress DhcpClass::getDnsServerIp()
458 {
459 return IPAddress(_dhcpDnsServerIp);
460 }
461
printByte(char * buf,uint8_t n)462 void DhcpClass::printByte(char * buf, uint8_t n ) {
463 char *str = &buf[1];
464 buf[0]='0';
465 do {
466 unsigned long m = n;
467 n /= 16;
468 char c = m - 16 * n;
469 *str-- = c < 10 ? c + '0' : c + 'A' - 10;
470 } while(n);
471 }
472