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