Fork for fixes

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers DhcpClient.cpp Source File

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