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
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 }
Generated on Tue Jul 12 2022 18:53:40 by 1.7.2