mbed OS5

Fork of UIPEthernet by Zoltan Hudak

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Dhcp.cpp Source File

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 }