Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.
Dependents: mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more
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 thread_sleep_for(50); 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 Tue Jul 12 2022 18:47:59 by 1.7.2