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

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 "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 }