Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
DhcpClient.cpp
00001 // DHCP Library v0.3 - April 25, 2009 00002 // Author: Jordan Terrell - blog.jordanterrell.com 00003 #include <string.h> 00004 #include <stdlib.h> 00005 #include "UipEthernet.h" 00006 #include "DhcpClient.h" 00007 #include "utility/util.h" 00008 00009 /** 00010 * @brief 00011 * @note 00012 * @param 00013 * @retval 00014 */ 00015 int DhcpClient::begin(uint8_t* mac, unsigned long timeout, unsigned long responseTimeout) 00016 { 00017 _dhcpLeaseTime = 0; 00018 _dhcpT1 = 0; 00019 _dhcpT2 = 0; 00020 _lastCheck = 0; 00021 _timeout = timeout; 00022 _responseTimeout = responseTimeout; 00023 00024 // zero out _dhcpMacAddr 00025 memset(_dhcpMacAddr, 0, 6); 00026 resetDhcpLease(); 00027 00028 memcpy((void*)_dhcpMacAddr, (void*)mac, 6); 00029 _dhcp_state = STATE_DHCP_START; 00030 return requestDhcpLease(); 00031 } 00032 00033 /** 00034 * @brief 00035 * @note 00036 * @param 00037 * @retval 00038 */ 00039 void DhcpClient::resetDhcpLease() 00040 { 00041 // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp 00042 memset(_dhcpLocalIp, 0, 5 * 4); 00043 } 00044 00045 //return:0 on error, 1 if request is sent and response is received 00046 int DhcpClient::requestDhcpLease() 00047 { 00048 #ifdef UIPETHERNET_DEBUG_UDP 00049 printf("requestDhcpLease(): begin\r\n"); 00050 #endif 00051 uint8_t messageType = 0; 00052 Timer timer; 00053 00054 timer.reset(); 00055 timer.start(); 00056 00057 // Pick an initial transaction ID 00058 srand(time(NULL)+ 1); 00059 _dhcpTransactionId = (rand() % 2000UL) + 1; 00060 _dhcpInitialTransactionId = _dhcpTransactionId; 00061 00062 _dhcpUdpSocket.stop(); 00063 if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) { 00064 // Couldn't get a socket 00065 #ifdef UIPETHERNET_DEBUG_UDP 00066 printf("requestDhcpLease(): Couldn't get a socket\r\n"); 00067 #endif 00068 return 0; 00069 } 00070 00071 presendDhcp(); 00072 00073 volatile int result = 0; 00074 00075 while (_dhcp_state != STATE_DHCP_LEASED) { 00076 if (_dhcp_state == STATE_DHCP_START) { 00077 #ifdef UIPETHERNET_DEBUG_UDP 00078 printf("_dhcp_state: STATE_DHCP_START\r\n"); 00079 #endif 00080 _dhcpTransactionId++; 00081 00082 sendDhcpMessage(DHCP_DISCOVER, (timer.read_ms() / 1000 + 1)); 00083 _dhcp_state = STATE_DHCP_DISCOVER; 00084 } 00085 else 00086 if (_dhcp_state == STATE_DHCP_REREQUEST) { 00087 #ifdef UIPETHERNET_DEBUG_UDP 00088 printf("_dhcp_state: STATE_DHCP_REREQUEST\r\n"); 00089 #endif 00090 _dhcpTransactionId++; 00091 sendDhcpMessage(DHCP_REQUEST, (timer.read_ms() / 1000 + 1)); 00092 _dhcp_state = STATE_DHCP_REQUEST; 00093 } 00094 else 00095 if (_dhcp_state == STATE_DHCP_DISCOVER) { 00096 #ifdef UIPETHERNET_DEBUG_UDP 00097 printf("_dhcp_state: STATE_DHCP_DISCOVER\r\n"); 00098 #endif 00099 uint32_t respId; 00100 messageType = parseDhcpResponse(_responseTimeout, respId); 00101 if (messageType == DHCP_OFFER) { 00102 // We'll use the transaction ID that the offer came with, 00103 // rather than the one we were up to 00104 _dhcpTransactionId = respId; 00105 sendDhcpMessage(DHCP_REQUEST, (timer.read_ms() / 1000 + 1)); 00106 _dhcp_state = STATE_DHCP_REQUEST; 00107 } 00108 } 00109 else 00110 if (_dhcp_state == STATE_DHCP_REQUEST) { 00111 #ifdef UIPETHERNET_DEBUG_UDP 00112 printf("_dhcp_state: STATE_DHCP_REQUEST\r\n"); 00113 #endif 00114 uint32_t respId; 00115 messageType = parseDhcpResponse(_responseTimeout, respId); 00116 if (messageType == DHCP_ACK) { 00117 #ifdef UIPETHERNET_DEBUG_UDP 00118 printf("messageType: DHCP_ACK\r\n"); 00119 #endif 00120 _dhcp_state = STATE_DHCP_LEASED; 00121 result = 1; 00122 00123 //use default lease time if we didn't get it 00124 if (_dhcpLeaseTime == 0) { 00125 _dhcpLeaseTime = DEFAULT_LEASE; 00126 } 00127 00128 //calculate T1 & T2 if we didn't get it 00129 if (_dhcpT1 == 0) { 00130 //T1 should be 50% of _dhcpLeaseTime 00131 _dhcpT1 = _dhcpLeaseTime >> 1; 00132 } 00133 00134 if (_dhcpT2 == 0) { 00135 //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime 00136 _dhcpT2 = _dhcpT1 << 1; 00137 } 00138 00139 _renewInSec = _dhcpT1; 00140 _rebindInSec = _dhcpT2; 00141 } 00142 else 00143 if (messageType == DHCP_NAK) { 00144 #ifdef UIPETHERNET_DEBUG_UDP 00145 printf("messageType: DHCP_NAK\r\n"); 00146 #endif 00147 _dhcp_state = STATE_DHCP_START; 00148 } 00149 } 00150 00151 if (messageType == 255) { 00152 #ifdef UIPETHERNET_DEBUG_UDP 00153 printf("DHCP_NAK: 255\r\n"); 00154 #endif 00155 messageType = 0; 00156 _dhcp_state = STATE_DHCP_START; 00157 } 00158 00159 if ((result != 1) && ((timer.read_ms() / 1000) > _timeout)) { 00160 #ifdef UIPETHERNET_DEBUG_UDP 00161 printf("requestDhcpLease(): timeout\r\n"); 00162 #endif 00163 break; 00164 } 00165 } 00166 00167 // We're done with the socket now 00168 _dhcpUdpSocket.stop(); 00169 _dhcpTransactionId++; 00170 00171 #ifdef UIPETHERNET_DEBUG_UDP 00172 printf("requestDhcpLease(): %d\r\n", result); 00173 #endif 00174 return result; 00175 } 00176 00177 /** 00178 * @brief 00179 * @note 00180 * @param 00181 * @retval 00182 */ 00183 void DhcpClient::presendDhcp() 00184 { } 00185 00186 /** 00187 * @brief 00188 * @note 00189 * @param 00190 * @retval 00191 */ 00192 void DhcpClient::sendDhcpMessage(uint8_t messageType, uint16_t secondsElapsed) 00193 { 00194 uint8_t buffer[32]; 00195 memset(buffer, 0, 32); 00196 00197 IpAddress dest_addr(255, 255, 255, 255); // Broadcast address 00198 00199 if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) { 00200 // FIXME Need to return errors 00201 return; 00202 } 00203 00204 buffer[0] = DHCP_BOOTREQUEST; // op 00205 buffer[1] = DHCP_HTYPE10MB; // htype 00206 buffer[2] = DHCP_HLENETHERNET; // hlen 00207 buffer[3] = DHCP_HOPS; // hops 00208 // xid 00209 unsigned long xid = htonl(_dhcpTransactionId); 00210 memcpy(buffer + 4, &(xid), 4); 00211 00212 // 8, 9 - seconds elapsed 00213 buffer[8] = ((secondsElapsed & 0xff00) >> 8); 00214 buffer[9] = (secondsElapsed & 0x00ff); 00215 00216 // flags 00217 unsigned short flags = htons(DHCP_FLAGSBROADCAST); 00218 memcpy(buffer + 10, &(flags), 2); 00219 00220 // ciaddr: already zeroed 00221 // yiaddr: already zeroed 00222 // siaddr: already zeroed 00223 // giaddr: already zeroed 00224 //put data in ENC28J60 transmit buffer 00225 _dhcpUdpSocket.write(buffer, 28); 00226 00227 memset(buffer, 0, 32); // clear local buffer 00228 memcpy(buffer, _dhcpMacAddr, 6); // chaddr 00229 //put data in ENC28J60 transmit buffer 00230 _dhcpUdpSocket.write(buffer, 16); 00231 00232 memset(buffer, 0, 32); // clear local buffer 00233 // leave zeroed out for sname && file 00234 // put in ENC28J60 transmit buffer x 6 (192 bytes) 00235 for (int i = 0; i < 6; i++) { 00236 _dhcpUdpSocket.write(buffer, 32); 00237 } 00238 00239 // OPT - Magic Cookie 00240 buffer[0] = (uint8_t) ((MAGIC_COOKIE >> 24) & 0xFF); 00241 buffer[1] = (uint8_t) ((MAGIC_COOKIE >> 16) & 0xFF); 00242 buffer[2] = (uint8_t) ((MAGIC_COOKIE >> 8) & 0xFF); 00243 buffer[3] = (uint8_t) (MAGIC_COOKIE & 0xFF); 00244 00245 // OPT - message type 00246 buffer[4] = dhcpMessageType; 00247 buffer[5] = 0x01; 00248 buffer[6] = messageType; //DHCP_REQUEST; 00249 // OPT - client identifier 00250 buffer[7] = dhcpClientIdentifier; 00251 buffer[8] = 0x07; 00252 buffer[9] = 0x01; 00253 memcpy(buffer + 10, _dhcpMacAddr, 6); 00254 00255 // OPT - host name 00256 buffer[16] = hostName; 00257 buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address 00258 strcpy((char*) &(buffer[18]), HOST_NAME); 00259 00260 printByte((char*) &(buffer[24]), _dhcpMacAddr[3]); 00261 printByte((char*) &(buffer[26]), _dhcpMacAddr[4]); 00262 printByte((char*) &(buffer[28]), _dhcpMacAddr[5]); 00263 00264 //put data in ENC28J60 transmit buffer 00265 _dhcpUdpSocket.write(buffer, 30); 00266 00267 if (messageType == DHCP_REQUEST) { 00268 buffer[0] = dhcpRequestedIPaddr; 00269 buffer[1] = 0x04; 00270 buffer[2] = _dhcpLocalIp[0]; 00271 buffer[3] = _dhcpLocalIp[1]; 00272 buffer[4] = _dhcpLocalIp[2]; 00273 buffer[5] = _dhcpLocalIp[3]; 00274 00275 buffer[6] = dhcpServerIdentifier; 00276 buffer[7] = 0x04; 00277 00278 //buffer[8] = _dhcpDhcpServerIp[0]; 00279 buffer[8] = _dhcpLocalIp[0]; 00280 buffer[9] = _dhcpDhcpServerIp[1]; 00281 buffer[10] = _dhcpDhcpServerIp[2]; 00282 buffer[11] = _dhcpDhcpServerIp[3]; 00283 00284 //put data in ENC28J60 transmit buffer 00285 _dhcpUdpSocket.write(buffer, 12); 00286 } 00287 00288 buffer[0] = dhcpParamRequest; 00289 buffer[1] = 0x06; 00290 buffer[2] = subnetMask; 00291 buffer[3] = routersOnSubnet; 00292 buffer[4] = dns; 00293 buffer[5] = domainName; 00294 buffer[6] = dhcpT1value; 00295 buffer[7] = dhcpT2value; 00296 buffer[8] = endOption; 00297 00298 //put data in ENC28J60 transmit buffer 00299 _dhcpUdpSocket.write(buffer, 9); 00300 00301 _dhcpUdpSocket.endPacket(); 00302 } 00303 00304 /** 00305 * @brief 00306 * @note 00307 * @param 00308 * @retval 00309 */ 00310 uint8_t DhcpClient::parseDhcpResponse(time_t responseTimeout, uint32_t& transactionId) 00311 { 00312 volatile uint8_t type = 0; 00313 uint8_t opt_len = 0; 00314 Timer timer; 00315 00316 timer.start(); 00317 00318 while (_dhcpUdpSocket.parsePacket() <= 0) { 00319 if ((time_t)timer.read_ms() > responseTimeout) { 00320 return 255; 00321 } 00322 #if MBED_MAJOR_VERSION == 2 00323 wait_ms(50); 00324 #else 00325 wait_us(50000); 00326 #endif 00327 } 00328 00329 // start reading in the packet 00330 RIP_MSG_FIXED fixedMsg; 00331 _dhcpUdpSocket.read((uint8_t*) &fixedMsg, sizeof(RIP_MSG_FIXED)); 00332 00333 if (fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) { 00334 transactionId = ntohl(fixedMsg.xid); 00335 if 00336 ( 00337 memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || 00338 (transactionId < _dhcpInitialTransactionId) || 00339 (transactionId > _dhcpTransactionId) 00340 ) { 00341 // Need to read the rest of the packet here regardless 00342 _dhcpUdpSocket.flush(); 00343 return 0; 00344 } 00345 00346 memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); 00347 00348 // Skip to the option part 00349 // Doing this a byte at a time so we don't have to put a big buffer 00350 // on the stack (as we don't have lots of memory lying around) 00351 for (int i = 0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++) { 00352 _dhcpUdpSocket.read(); // we don't care about the returned byte 00353 } 00354 00355 while (_dhcpUdpSocket.available() > 0) { 00356 switch (_dhcpUdpSocket.read()) { 00357 case endOption: 00358 break; 00359 00360 case padOption: 00361 break; 00362 00363 case dhcpMessageType: 00364 opt_len = _dhcpUdpSocket.read(); 00365 type = _dhcpUdpSocket.read(); 00366 break; 00367 00368 case subnetMask: 00369 opt_len = _dhcpUdpSocket.read(); 00370 _dhcpUdpSocket.read(_dhcpSubnetMask, 4); 00371 break; 00372 00373 case routersOnSubnet: 00374 opt_len = _dhcpUdpSocket.read(); 00375 _dhcpUdpSocket.read(_dhcpGatewayIp, 4); 00376 for (int i = 0; i < opt_len - 4; i++) { 00377 _dhcpUdpSocket.read(); 00378 } 00379 break; 00380 00381 case dns: 00382 opt_len = _dhcpUdpSocket.read(); 00383 _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); 00384 for (int i = 0; i < opt_len - 4; i++) { 00385 _dhcpUdpSocket.read(); 00386 } 00387 break; 00388 00389 case dhcpServerIdentifier: 00390 opt_len = _dhcpUdpSocket.read(); 00391 if (*((uint32_t*)_dhcpDhcpServerIp) == 0 || IpAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP()) { 00392 _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); 00393 } 00394 else { 00395 // Skip over the rest of this option 00396 while (opt_len--) { 00397 _dhcpUdpSocket.read(); 00398 } 00399 } 00400 break; 00401 00402 case dhcpT1value: 00403 opt_len = _dhcpUdpSocket.read(); 00404 _dhcpUdpSocket.read((uint8_t*) &_dhcpT1, sizeof(_dhcpT1)); 00405 _dhcpT1 = ntohl(_dhcpT1); 00406 break; 00407 00408 case dhcpT2value: 00409 opt_len = _dhcpUdpSocket.read(); 00410 _dhcpUdpSocket.read((uint8_t*) &_dhcpT2, sizeof(_dhcpT2)); 00411 _dhcpT2 = ntohl(_dhcpT2); 00412 break; 00413 00414 case dhcpIPaddrLeaseTime: 00415 opt_len = _dhcpUdpSocket.read(); 00416 _dhcpUdpSocket.read((uint8_t*) &_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); 00417 _dhcpLeaseTime = ntohl(_dhcpLeaseTime); 00418 _renewInSec = _dhcpLeaseTime; 00419 break; 00420 00421 default: 00422 opt_len = _dhcpUdpSocket.read(); 00423 00424 // Skip over the rest of this option 00425 while (opt_len--) { 00426 _dhcpUdpSocket.read(); 00427 } 00428 break; 00429 } 00430 } 00431 } 00432 00433 // Need to skip to end of the packet regardless here 00434 _dhcpUdpSocket.flush(); 00435 00436 return type; 00437 } 00438 00439 /* 00440 returns: 00441 0/DHCP_CHECK_NONE: nothing happened 00442 1/DHCP_CHECK_RENEW_FAIL: renew failed 00443 2/DHCP_CHECK_RENEW_OK: renew success 00444 3/DHCP_CHECK_REBIND_FAIL: rebind fail 00445 4/DHCP_CHECK_REBIND_OK: rebind success 00446 */ 00447 int DhcpClient::checkLease() 00448 { 00449 time_t now = time(NULL); 00450 volatile int rc = DHCP_CHECK_NONE; 00451 if (_lastCheck != 0) { 00452 //calc how many ms past the timeout we are 00453 time_t factor = now - _secTimeout; 00454 00455 //if on or passed the timeout, reduce the counters 00456 if (factor >= 0) { 00457 //next timeout should be now plus 1s 00458 _secTimeout = now + 1; 00459 00460 //reduce the counters by that mouch 00461 //if we can assume that the cycle time (factor) is fairly constant 00462 //and if the remainder is less than cycle time * 2 00463 //do it early instead of late 00464 if (_renewInSec < factor * 2) 00465 _renewInSec = 0; 00466 else 00467 _renewInSec -= factor; 00468 00469 if (_rebindInSec < factor * 2) 00470 _rebindInSec = 0; 00471 else 00472 _rebindInSec -= factor; 00473 } 00474 00475 //if we have a lease but should renew, do it 00476 if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <= 0) { 00477 _dhcp_state = STATE_DHCP_REREQUEST; 00478 rc = 1 + requestDhcpLease(); 00479 } 00480 00481 //if we have a lease or is renewing but should bind, do it 00482 if ((_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <= 0) { 00483 //this should basically restart completely 00484 _dhcp_state = STATE_DHCP_START; 00485 resetDhcpLease(); 00486 rc = 3 + requestDhcpLease(); 00487 } 00488 } 00489 else { 00490 _secTimeout = now + 1; 00491 } 00492 00493 _lastCheck = now; 00494 return rc; 00495 } 00496 00497 /** 00498 * @brief 00499 * @note 00500 * @param 00501 * @retval 00502 */ 00503 IpAddress DhcpClient::getLocalIp() 00504 { 00505 return IpAddress(_dhcpLocalIp); 00506 } 00507 00508 /** 00509 * @brief 00510 * @note 00511 * @param 00512 * @retval 00513 */ 00514 IpAddress DhcpClient::getSubnetMask() 00515 { 00516 return IpAddress(_dhcpSubnetMask); 00517 } 00518 00519 /** 00520 * @brief 00521 * @note 00522 * @param 00523 * @retval 00524 */ 00525 IpAddress DhcpClient::getGatewayIp() 00526 { 00527 return IpAddress(_dhcpGatewayIp); 00528 } 00529 00530 /** 00531 * @brief 00532 * @note 00533 * @param 00534 * @retval 00535 */ 00536 IpAddress DhcpClient::getDhcpServerIp() 00537 { 00538 return IpAddress(_dhcpDhcpServerIp); 00539 } 00540 00541 /** 00542 * @brief 00543 * @note 00544 * @param 00545 * @retval 00546 */ 00547 IpAddress DhcpClient::getDnsServerIp() 00548 { 00549 return IpAddress(_dhcpDnsServerIp); 00550 } 00551 00552 /** 00553 * @brief 00554 * @note 00555 * @param 00556 * @retval 00557 */ 00558 void DhcpClient::printByte(char* buf, uint8_t n) 00559 { 00560 char* str = &buf[1]; 00561 buf[0] = '0'; 00562 do { 00563 unsigned long m = n; 00564 n /= 16; 00565 00566 char c = m - 16 * n; 00567 *str-- = c < 10 ? c + '0' : c + 'A' - 10; 00568 } while (n); 00569 }
Generated on Sun Feb 26 2023 10:14:23 by
1.7.2