A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.

Dependents:   oldheating gps motorhome heating

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers dhcp.c Source File

dhcp.c

00001 #include <stdint.h>
00002 #include <stdbool.h>
00003 #include <string.h>
00004 
00005 #include      "log.h"
00006 #include  "mstimer.h"
00007 #include      "net.h"
00008 #include "net-this.h"
00009 #include   "action.h"
00010 #include      "eth.h"
00011 #include  "ip4addr.h"
00012 #include      "mac.h"
00013 #include      "udp.h"
00014 #include "dnslabel.h"
00015 #include  "dhcphdr.h"
00016 
00017 bool DhcpTrace = false;
00018 
00019 #define REQUEST   1
00020 #define REPLY     2
00021 
00022 #define DHCPDISCOVER 1
00023 #define DHCPOFFER    2
00024 #define DHCPREQUEST  3
00025 #define DHCPDECLINE  4
00026 #define DHCPACK      5
00027 #define DHCPNAK      6
00028 #define DHCPRELEASE  7
00029 #define DHCPINFORM   8
00030 
00031 #define COOKIE 0x63825363
00032 
00033 #define MAX_REPEAT_DELAY_TIME_MS 64000
00034 #define MIN_REPEAT_DELAY_TIME_MS  4000
00035 
00036 #define STATE_NONE     0
00037 #define STATE_DISCOVER 1
00038 #define STATE_SELECT   2
00039 #define STATE_BOUND    3
00040 #define STATE_RENEW    4
00041 #define STATE_REBIND   5
00042 #define STATE_EXPIRED  6
00043 
00044 static uint32_t       _xid = 0;
00045 static int          _state = STATE_NONE;
00046 static uint32_t _offeredIp = 0;
00047 static uint32_t  _serverId = 0;
00048 static bool   _awaitingResponse = false;
00049 
00050 static uint32_t _elapsedLifeMsTimer  = 0;                       //Started whenever an IP address request has been acknowledged
00051 
00052 uint32_t DhcpGetElapsedLife()
00053 {
00054     if (!_elapsedLifeMsTimer) return 0;
00055     return (MsTimerCount - _elapsedLifeMsTimer) / 1000;
00056 }
00057 
00058 static uint8_t  _dhcpMessageType = 0;
00059 uint32_t DhcpLeaseTime   = 0;
00060 uint32_t DhcpServerIp    = 0;
00061 uint32_t DhcpRouterIp    = 0;
00062 uint32_t DhcpSubnetMask  = 0;
00063 uint32_t DhcpNtpIp       = 0;
00064 uint32_t DhcpRenewalT1   = 0;
00065 uint32_t DhcpRenewalT2   = 0;
00066 uint32_t DhcpBroadcastIp = 0;
00067 uint32_t DhcpLocalIp     = 0;
00068 uint32_t DhcpDnsServerIp = 0;
00069 char     DhcpDomainName[DNS_MAX_LABEL_LENGTH+1];
00070 char     DhcpHostName  [DNS_MAX_LABEL_LENGTH+1];
00071 
00072 static void clearAll()
00073 {
00074     DhcpLeaseTime     = 0;
00075     DhcpServerIp      = 0;
00076     DhcpRouterIp      = 0;
00077     DhcpSubnetMask    = 0;
00078     DhcpNtpIp         = 0;
00079     DhcpRenewalT1     = 0;
00080     DhcpRenewalT2     = 0;
00081     DhcpBroadcastIp   = 0;
00082     DhcpLocalIp       = 0;
00083     DhcpDnsServerIp   = 0;
00084     DhcpDomainName[0] = 0;
00085     DhcpHostName  [0] = 0;
00086     _offeredIp        = 0;
00087     _serverId         = 0;
00088     _awaitingResponse = false;
00089 }
00090 
00091 bool DhcpIpNeedsToBeRouted(uint32_t ip)
00092 {   
00093     if ((ip & 0x000000FF) == 0xFF) return false; //Broadcast 255.xxx.xxx.xxx
00094     if ((ip & 0x000000FF) == 0xE0) return false; //Multicast 224.xxx.xxx.xxx
00095     if (ip == (DhcpLocalIp | 0xFF000000)) return false; //Local broadcast ip == 192.168.0.255
00096     
00097     return (ip & DhcpSubnetMask) != (DhcpLocalIp & DhcpSubnetMask); //ip != 192.168.0.xxx
00098 }
00099 
00100 static uint32_t readOption32(char** pp)
00101 {
00102     uint32_t value = 0;
00103     char* p = *pp;
00104     int len = *++p;
00105     if (len >= 4)
00106     {
00107         value  = *++p << 24;
00108         value |= *++p << 16;
00109         value |= *++p <<  8;
00110         value |= *++p <<  0;
00111     }
00112     *pp += len + 1;
00113     return value;
00114 }
00115 static uint32_t readIp(char** pp)
00116 {
00117     uint32_t value = 0;
00118     char* p = *pp;
00119     int len = *++p;
00120     if (len >= 4)
00121     {
00122         value  = *++p <<  0;
00123         value |= *++p <<  8;
00124         value |= *++p << 16;
00125         value |= *++p << 24;
00126     }
00127     *pp += len + 1;
00128     return value;
00129 }
00130 static void readString(char** pp, char* pText)
00131 {
00132     char* p = *pp;
00133     int len = *++p;
00134     for (int i = 0; i < len; i++) pText[i] = *++p;
00135     *pp += len + 1;
00136 }
00137 static void readOptions(int size, char * pOptions)
00138 {
00139     int len = 0;
00140     char* p  = pOptions;
00141     char* pE = pOptions + size;
00142     while( p < pE)
00143     {
00144         switch (*p)
00145         {
00146             case 0:                                                      break;  //NOP
00147             case 255:                                                    return; //End of options
00148             case 1:                DhcpSubnetMask  = readIp(&p);         break;  //Subnet Mask
00149             case 3:                DhcpRouterIp    = readIp(&p);         break;  //Router
00150             case 6:                DhcpDnsServerIp = readIp(&p);         break;  //DNS server
00151             case 12:               readString(&p, DhcpHostName);         break;  //Host name
00152             case 15:               readString(&p, DhcpDomainName);       break;  //Domain name
00153             case 19:  len = *++p; p+= len;                               break;  //IP forwarding yes/no
00154             case 28:               DhcpBroadcastIp = readIp(&p);         break;  //Broadcast IP
00155             case 42:               DhcpNtpIp       = readIp(&p);         break;  //NTP
00156             case 44:  len = *++p; p+= len;                               break;  //NetBIOS name server
00157             case 45:  len = *++p; p+= len;                               break;  //NetBIOS datagram server
00158             case 46:  len = *++p; p+= len;                               break;  //NetBIOS node type
00159             case 47:  len = *++p; p+= len;                               break;  //NetBIOS scope
00160             case 53:  len = *++p; _dhcpMessageType = *++p;               break;  //DHCP message type
00161             case 51:               DhcpLeaseTime   = readOption32(&p);   break;  //Address lease time
00162             case 54:               _serverId       = readIp(&p);         break;  //DHCP server
00163             case 58:               DhcpRenewalT1   = readOption32(&p);   break;  //T1
00164             case 59:               DhcpRenewalT2   = readOption32(&p);   break;  //T2
00165             default:
00166                 if (DhcpTrace) LogTimeF("Ignoring option %d\r\n", *p);
00167                 len = *++p;
00168                 p += len;
00169                 return;
00170         }
00171         p++;
00172     }
00173 }
00174 static void readOptionMessageType(int size, char * pOptions)
00175 {
00176     int len = 0;
00177     char* p  = pOptions;
00178     char* pE = pOptions + size;
00179     while( p < pE)
00180     {
00181         switch (*p)
00182         {
00183             case 0:                                                      break;  //NOP
00184             case 255:                                                    return; //End of options
00185             case 53:  len = *++p; _dhcpMessageType = *++p;               break;  //DHCP message type
00186             default:  len = *++p; p += len;                              break;
00187         }
00188         p++;
00189     }
00190 }
00191 static void readOptionServerId(int size, char * pOptions)
00192 {
00193     int len = 0;
00194     char* p  = pOptions;
00195     char* pE = pOptions + size;
00196     while( p < pE)
00197     {
00198         switch (*p)
00199         {
00200             case 0:                                                      break;  //NOP
00201             case 255:                                                    return; //End of options
00202             case 54:               _serverId       = readIp(&p);         break;  //DHCP server
00203             default:  len = *++p; p += len;                              break;
00204         }
00205         p++;
00206     }
00207 }
00208 static void writeIp(uint8_t code, uint32_t value, char** pp)
00209 {
00210     if (!value) return;
00211 
00212     char* p = *pp;
00213 
00214     *p++ = code;
00215     *p++ = 4;
00216     *p++ = (value & 0x000000FF) >>  0;
00217     *p++ = (value & 0x0000FF00) >>  8;
00218     *p++ = (value & 0x00FF0000) >> 16;
00219     *p++ = (value & 0xFF000000) >> 24;
00220 
00221     *pp += 6;
00222 }
00223 static void writeText(uint8_t code, const char* pText, char** pp)
00224 {
00225     if (!pText) return;
00226     if (!*pText) return;
00227     
00228     char* p = *pp;
00229     
00230     *p++ = code;
00231     char* pLength = p++;
00232     while (true) //Copy pText without the end zero
00233     {
00234         *p = *pText;
00235         pText++;
00236         if (!*pText) break;
00237         p++;
00238     }
00239     
00240     *pLength = p - pLength;
00241     
00242     *pp += *pLength + 2;
00243 }
00244 int makeRequest(void* pPacket, uint8_t type, uint32_t ciaddr, uint32_t requestedIp, uint32_t serverId)
00245 {
00246     bool broadcast = ciaddr == 0;
00247     uint16_t flags = 0;
00248     if (broadcast) flags |= 0x8000;
00249     DhcpHdrSetOp    (pPacket, REQUEST            );
00250     DhcpHdrSetHtype (pPacket, ETHERNET           );
00251     DhcpHdrSetHlen  (pPacket, 6                  );
00252     DhcpHdrSetHops  (pPacket, 0                  );
00253     DhcpHdrSetXid   (pPacket, _xid               ); //Randomly chosed transaction id used to associate messages to responses
00254     DhcpHdrSetSecs  (pPacket, 0                  ); //Seconds since started to boot
00255     DhcpHdrSetFlags (pPacket, flags              ); //Broadcast (1) Unicast (0)
00256     DhcpHdrSetCiaddr(pPacket, ciaddr             ); //'Client' address set by client or 0 if don't know address
00257     DhcpHdrSetYiaddr(pPacket, 0                  ); //'Your' address returned by server
00258     DhcpHdrSetSiaddr(pPacket, 0                  ); //'Server' address to use if required
00259     DhcpHdrSetGiaddr(pPacket, 0                  ); //'Gateway' address
00260     memcpy(DhcpHdrPtrChaddr(pPacket), MacLocal, 6); //'Client hardware' address. 6 bytes for ethernet
00261     memset(DhcpHdrPtrChaddr(pPacket) + 6, 0, 10  ); //Pad the remainder of the hardware address field with zeros
00262     memset(DhcpHdrPtrLegacy(pPacket), 0, 192     ); //BootP legacy fill with zeros
00263     DhcpHdrSetCookie(pPacket, COOKIE             ); //Magic cookie
00264 
00265     char* pOptions = (char*)pPacket + DHCP_HEADER_LENGTH;
00266     char* p = pOptions;
00267     *p++ = 53; *p++ = 1; *p++ = type;                  //DHCP message type
00268     if (requestedIp) writeIp(50, requestedIp, &p);     //Requested IP
00269     if (   serverId) writeIp(54,    serverId, &p);     //Server ip
00270     writeText(12, NET_NAME, &p);                       //Host name
00271     *p++ = 255;                                        //End of options
00272     
00273     return DHCP_HEADER_LENGTH + p - pOptions;
00274 }
00275 int DhcpHandleResponse(void (*traceback)(void), int sizeRx, char* pPacketRx, int* pSizeTx, char* pPacketTx)
00276 {
00277     uint8_t  op      = DhcpHdrGetOp    (pPacketRx);
00278     uint8_t  htype   = DhcpHdrGetHtype (pPacketRx);
00279     uint8_t  hlen    = DhcpHdrGetHlen  (pPacketRx);
00280     uint32_t xid     = DhcpHdrGetXid   (pPacketRx);    //Randomly chosen transaction id used to associate messages to responses
00281     uint32_t yiaddr  = DhcpHdrGetYiaddr(pPacketRx);
00282     uint32_t siaddr  = DhcpHdrGetSiaddr(pPacketRx);
00283     uint32_t cookie  = DhcpHdrGetCookie(pPacketRx);
00284 
00285     if (op     != REPLY)                                  return DO_NOTHING;
00286     if (htype  != ETHERNET)                               return DO_NOTHING;
00287     if (hlen   != 6)                                      return DO_NOTHING;
00288     if (memcmp(DhcpHdrPtrChaddr(pPacketRx), MacLocal, 6)) return DO_NOTHING;
00289     if (!_xid)                                            return DO_NOTHING;
00290     if (xid    != _xid)                                   return DO_NOTHING;
00291     if (cookie != COOKIE)                                 return DO_NOTHING;
00292 
00293     char* pOptions = (char*)pPacketRx + DHCP_HEADER_LENGTH;
00294     readOptionMessageType(sizeRx - DHCP_HEADER_LENGTH, pOptions);
00295     
00296     switch (_dhcpMessageType)
00297     {
00298         case DHCPOFFER:
00299             if (_state == STATE_DISCOVER)
00300             {
00301                 _offeredIp = yiaddr;
00302                 readOptionServerId(sizeRx - DHCP_HEADER_LENGTH, pOptions);
00303                 if (DhcpTrace) { LogTime("DHCP <- offer  "); Ip4AddrLog(_offeredIp); Log(" from server "); Ip4AddrLog(_serverId); Log("\r\n"); }
00304                 _awaitingResponse = false;
00305             }
00306             else
00307             {
00308                 if (DhcpTrace) { LogTime("DHCP <- offer  "); Ip4AddrLog(_offeredIp); Log(" from server "); Ip4AddrLog(_serverId); Log(" ignored\r\n"); }
00309             }
00310             break;
00311         case DHCPACK:
00312             if (_state == STATE_SELECT || _state == STATE_RENEW || _state == STATE_REBIND)
00313             {
00314                 DhcpLocalIp = yiaddr;
00315                 readOptions(sizeRx - DHCP_HEADER_LENGTH, pOptions);
00316                 DhcpServerIp = _serverId;
00317                 if (DhcpTrace) { LogTime("DHCP <- ack    ");   Ip4AddrLog(DhcpLocalIp); Log(" from server "); Ip4AddrLog(_serverId); Log("\r\n"); }
00318                 _elapsedLifeMsTimer = MsTimerCount;  //Start the life timer
00319                 _awaitingResponse = false;
00320             }
00321             else
00322             {
00323                 if (DhcpTrace) { LogTime("DHCP <- ack    ");   Ip4AddrLog(DhcpLocalIp); Log(" from server "); Ip4AddrLog(_serverId); Log(" ignored\r\n"); }
00324             }
00325             break;
00326         case DHCPNAK:
00327             if (_state == STATE_SELECT || _state == STATE_RENEW || _state == STATE_REBIND)
00328             {
00329                 readOptionServerId(sizeRx - DHCP_HEADER_LENGTH, pOptions);
00330                 if (DhcpTrace) { LogTime("DHCP <- nack   ");  Ip4AddrLog(yiaddr); Log(" from server "); Ip4AddrLog(_serverId); Log("\r\n"); }
00331                 clearAll();
00332                 _awaitingResponse = false;
00333             }
00334             else
00335             {
00336                 if (DhcpTrace) { LogTime("DHCP <- nack   ");  Ip4AddrLog(yiaddr); Log(" from server "); Ip4AddrLog(_serverId); Log(" ignored\r\n"); }
00337             }
00338             break;
00339         default:
00340             LogTimeF("DHCP <- unknown message %d\r\n", _dhcpMessageType);
00341             break;
00342     }
00343     return DO_NOTHING;
00344 }
00345 
00346 int DhcpPollForRequestToSend(void* pPacket, int* pSize)
00347 {
00348     
00349     if (!_xid)
00350     {
00351         _xid  = (uint32_t)MacLocal[2] << 24;
00352         _xid += (uint32_t)MacLocal[3] << 16;
00353         _xid += (uint32_t)MacLocal[4] <<  8;
00354         _xid += (uint32_t)MacLocal[5] <<  0;
00355     }
00356     
00357     //Check if time to update
00358     uint32_t elapsedTimeMs = MsTimerCount - _elapsedLifeMsTimer;
00359     uint32_t   leaseTimeMs = DhcpLeaseTime * 1000;
00360     
00361     uint32_t T1 =  leaseTimeMs >> 1;                                            //0.5
00362     uint32_t T2 = (leaseTimeMs >> 1) + (leaseTimeMs >> 2) + (leaseTimeMs >> 3); //0.875 = 0.5 + 0.25 + 0.125
00363     
00364     if (!DhcpLocalIp)
00365     {
00366         if (!_offeredIp) _state = STATE_DISCOVER;
00367         else             _state = STATE_SELECT;
00368     }
00369     else
00370     {
00371         if (elapsedTimeMs < T1)
00372         {
00373             _state = STATE_BOUND;
00374         }
00375         else if (elapsedTimeMs < T2)
00376         {
00377             _state = STATE_RENEW; 
00378         }
00379         else if (elapsedTimeMs < leaseTimeMs)
00380         {
00381             _state = STATE_REBIND;
00382         }
00383         else
00384         {
00385             _state = STATE_EXPIRED;
00386         }
00387     }
00388     static int stateLastScan = STATE_NONE;
00389     bool stateHasChanged = _state != stateLastScan;
00390     stateLastScan = _state;
00391     
00392     static uint32_t delayMs;
00393     static uint32_t repeatDelayMsTimer;
00394     
00395     bool responseTimeout = _awaitingResponse && MsTimerRelative(repeatDelayMsTimer, delayMs);
00396 
00397     bool send = stateHasChanged || responseTimeout;
00398     
00399     if (!send) return DO_NOTHING;
00400 
00401     int             dest = DO_NOTHING;
00402     uint8_t         type = 0; //DISCOVER or REQUEST
00403     uint32_t      ciaddr = 0; //goes in ciaddr only for BOUND/RENEW/REBIND
00404     uint32_t requestedIp = 0; //goes in option 54 only for SELECTING
00405     uint32_t    serverId = 0; //goes in option 50 and is the server id for the accepted offer
00406 
00407     //Send the renewal request
00408     *pSize = 0;
00409 
00410 
00411     switch (_state)
00412     {
00413         case STATE_NONE:
00414             if (DhcpTrace) { LogTimeF("DHCP -- none\r\n"); }
00415             clearAll();
00416             return DO_NOTHING;
00417             
00418         case STATE_DISCOVER:
00419             if (DhcpTrace) { LogTimeF("DHCP -> discover\r\n"); }
00420             type = DHCPDISCOVER;
00421             ciaddr = 0;
00422             requestedIp = 0;
00423             serverId = 0;
00424             dest = BROADCAST;
00425             break;
00426             
00427         case STATE_SELECT:
00428             if (DhcpTrace) { LogTimeF("DHCP -> select "); Ip4AddrLog(_offeredIp); Log(" from server "); Ip4AddrLog(_serverId); Log("\r\n"); }
00429             type = DHCPREQUEST;
00430             ciaddr = 0;
00431             requestedIp = _offeredIp;
00432             serverId = _serverId;
00433             dest = BROADCAST;
00434             break;
00435             
00436         case STATE_BOUND:
00437             if (DhcpTrace) { LogTimeF("DHCP -- bound  "); Ip4AddrLog(DhcpLocalIp); Log("\r\n"); }
00438             return DO_NOTHING;
00439             
00440         case STATE_RENEW:
00441             if (DhcpTrace) { LogTimeF("DHCP -> renew (T1) "); Ip4AddrLog(DhcpLocalIp); Log("\r\n"); }
00442             type = DHCPREQUEST;
00443             ciaddr = DhcpLocalIp;
00444             requestedIp = 0;
00445             serverId = 0;
00446             dest = UNICAST_DHCP;
00447             break;
00448             
00449         case STATE_REBIND:
00450             if (DhcpTrace) { LogTimeF("DHCP -> rebind (T2) "); Ip4AddrLog(DhcpLocalIp); Log("\r\n"); }
00451             type = DHCPREQUEST;
00452             ciaddr = DhcpLocalIp;
00453             requestedIp = 0;
00454             serverId = 0;
00455             dest = BROADCAST;
00456             break;
00457             
00458         case STATE_EXPIRED:
00459             if (DhcpTrace) { LogTimeF("DHCP -- expired "); Ip4AddrLog(DhcpLocalIp); Log("\r\n"); }
00460             clearAll();
00461             return DO_NOTHING;
00462             
00463         default:
00464             LogTimeF("DHCP -- unknown state %d\r\n", _state);
00465             return 0;
00466     }
00467     
00468     if (!_awaitingResponse)
00469     {
00470         delayMs = MIN_REPEAT_DELAY_TIME_MS;
00471     }
00472     else
00473     {
00474         delayMs <<= 1;                                                                //Backoff (double) the delay time after each attempt
00475         if (delayMs > MAX_REPEAT_DELAY_TIME_MS) delayMs = MAX_REPEAT_DELAY_TIME_MS;   //Don't go beyond a maximum
00476     }
00477     _awaitingResponse = true;
00478     repeatDelayMsTimer = MsTimerCount;                                                //Start the repeat delay timer
00479     *pSize = makeRequest(pPacket, type, ciaddr, requestedIp, serverId);
00480     
00481     return ActionMakeFromDestAndTrace(dest, DhcpTrace && NetTraceStack);
00482 }