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
Diff: udp/dhcp/dhcp.c
- Revision:
- 183:ee809769bf89
- Parent:
- 181:169881ecd52f
- Child:
- 184:ad80a63e3002
--- a/udp/dhcp/dhcp.c Sun Jan 10 11:52:19 2021 +0000 +++ b/udp/dhcp/dhcp.c Tue Jan 12 19:10:22 2021 +0000 @@ -28,23 +28,37 @@ #define DHCPRELEASE 7 #define DHCPINFORM 8 -#define ID 0x12345 #define COOKIE 0x63825363 #define MAX_REPEAT_DELAY_TIME_MS 60000 #define MIN_REPEAT_DELAY_TIME_MS 900 -static uint32_t repeatDelayMsTimer = (uint32_t)-MIN_REPEAT_DELAY_TIME_MS; //Initial value ensures no delay at startup -static uint32_t delayMs = MIN_REPEAT_DELAY_TIME_MS; //Doubles on failure up to max; reset to min whenever an IP address request has been acknowledged + +#define STATE_NONE 0 +#define STATE_DISCOVER 1 +#define STATE_SELECT 2 +#define STATE_BOUND 3 +#define STATE_RENEW 4 +#define STATE_REBIND 5 +#define STATE_EXPIRED 6 -static uint32_t elapsedLifeMsTimer = 0; //Started whenever an IP address request has been acknowledged +static uint32_t _xid = 0; +static int _state = STATE_NONE; +static uint32_t _offeredIp = 0; +static uint32_t _serverId = 0; +static bool _awaitResponse = false; + +static uint32_t _repeatDelayMsTimer = (uint32_t)-MIN_REPEAT_DELAY_TIME_MS; //Initial value ensures no delay at startup +static uint32_t _delayMs = MIN_REPEAT_DELAY_TIME_MS; //Doubles on failure up to max; reset to min whenever an IP address request has been acknowledged + +static uint32_t _elapsedLifeMsTimer = 0; //Started whenever an IP address request has been acknowledged uint32_t DhcpGetElapsedLife() { - if (!elapsedLifeMsTimer) return 0; - return (MsTimerCount - elapsedLifeMsTimer) / 1000; + if (!_elapsedLifeMsTimer) return 0; + return (MsTimerCount - _elapsedLifeMsTimer) / 1000; } -static uint8_t dhcpMessageType = 0; +static uint8_t _dhcpMessageType = 0; uint32_t DhcpLeaseTime = 0; uint32_t DhcpServerIp = 0; uint32_t DhcpRouterIp = 0; @@ -72,6 +86,8 @@ DhcpDnsServerIp = 0; DhcpDomainName[0] = 0; DhcpHostName [0] = 0; + _offeredIp = 0; + _serverId = 0; } bool DhcpIpNeedsToBeRouted(uint32_t ip) @@ -129,25 +145,25 @@ { switch (*p) { - case 0: break; //NOP - case 255: return; //End of options - case 1: DhcpSubnetMask = readIp(&p); break; //Subnet Mask - case 3: DhcpRouterIp = readIp(&p); break; //Router - case 6: DhcpDnsServerIp = readIp(&p); break; //DNS server - case 12: readString(&p, DhcpHostName); break; //Host name - case 15: readString(&p, DhcpDomainName); break; //Domain name - case 19: len = *++p; p+= len; break; //IP forwarding yes/no - case 28: DhcpBroadcastIp = readIp(&p); break; //Broadcast IP - case 42: DhcpNtpIp = readIp(&p); break; //NTP - case 44: len = *++p; p+= len; break; //NetBIOS name server - case 45: len = *++p; p+= len; break; //NetBIOS datagram server - case 46: len = *++p; p+= len; break; //NetBIOS node type - case 47: len = *++p; p+= len; break; //NetBIOS scope - case 53: len = *++p; dhcpMessageType = *++p; break; //DHCP message type - case 51: DhcpLeaseTime = readOption32(&p); break; //Address lease time - case 54: DhcpServerIp = readIp(&p); break; //DHCP server - case 58: DhcpRenewalT1 = readOption32(&p); break; //T1 - case 59: DhcpRenewalT2 = readOption32(&p); break; //T2 + case 0: break; //NOP + case 255: return; //End of options + case 1: DhcpSubnetMask = readIp(&p); break; //Subnet Mask + case 3: DhcpRouterIp = readIp(&p); break; //Router + case 6: DhcpDnsServerIp = readIp(&p); break; //DNS server + case 12: readString(&p, DhcpHostName); break; //Host name + case 15: readString(&p, DhcpDomainName); break; //Domain name + case 19: len = *++p; p+= len; break; //IP forwarding yes/no + case 28: DhcpBroadcastIp = readIp(&p); break; //Broadcast IP + case 42: DhcpNtpIp = readIp(&p); break; //NTP + case 44: len = *++p; p+= len; break; //NetBIOS name server + case 45: len = *++p; p+= len; break; //NetBIOS datagram server + case 46: len = *++p; p+= len; break; //NetBIOS node type + case 47: len = *++p; p+= len; break; //NetBIOS scope + case 53: len = *++p; _dhcpMessageType = *++p; break; //DHCP message type + case 51: DhcpLeaseTime = readOption32(&p); break; //Address lease time + case 54: _serverId = readIp(&p); break; //DHCP server + case 58: DhcpRenewalT1 = readOption32(&p); break; //T1 + case 59: DhcpRenewalT2 = readOption32(&p); break; //T2 default: if (DhcpTrace) LogTimeF("Ignoring option %d\r\n", *p); len = *++p; @@ -157,6 +173,40 @@ p++; } } +static void readOptionMessageType(int size, char * pOptions) +{ + int len = 0; + char* p = pOptions; + char* pE = pOptions + size; + while( p < pE) + { + switch (*p) + { + case 0: break; //NOP + case 255: return; //End of options + case 53: len = *++p; _dhcpMessageType = *++p; break; //DHCP message type + default: len = *++p; p += len; break; + } + p++; + } +} +static void readOptionServerId(int size, char * pOptions) +{ + int len = 0; + char* p = pOptions; + char* pE = pOptions + size; + while( p < pE) + { + switch (*p) + { + case 0: break; //NOP + case 255: return; //End of options + case 54: _serverId = readIp(&p); break; //DHCP server + default: len = *++p; p += len; break; + } + p++; + } +} static void writeIp(uint8_t code, uint32_t value, char** pp) { if (!value) return; @@ -193,42 +243,60 @@ *pp += *pLength + 2; } -int sendRequest(void* pPacket, uint8_t code, uint32_t srvIp, uint32_t reqIp) +int sendRequest(void* pPacket) { - - switch (code) + uint8_t type = 0; + uint32_t currentIp = 0; //goes in ciaddr + uint32_t requestedIp = 0; //goes in option 54 + uint32_t serverIp = 0; //goes in option 50 and is the server id for the accepted offer + + switch (_state) { - case DHCPDISCOVER: - if (DhcpTrace) LogTimeF("DHCP -> discover"); + case STATE_DISCOVER: + if (DhcpTrace) LogTimeF("DHCP -> discover\r\n"); + type = DHCPDISCOVER; + currentIp = 0; + requestedIp = 0; + serverIp = 0; + break; + case STATE_SELECT: + if (DhcpTrace) LogTimeF("DHCP -> select "); Ip4AddressLog(_offeredIp); Log(" from server "); Ip4AddressLog(_serverId); Log("\r\n"); + type = DHCPREQUEST; + currentIp = 0; + requestedIp = _offeredIp; + serverIp = _serverId; break; - case DHCPREQUEST: - if (DhcpTrace) LogTimeF("DHCP -> request"); + case STATE_RENEW: + if (DhcpTrace) LogTimeF("DHCP -> renew (T1)\r\n"); + type = DHCPREQUEST; + currentIp = DhcpLocalIp; + requestedIp = 0; + serverIp = 0; + break; + case STATE_REBIND: + if (DhcpTrace) LogTimeF("DHCP -> rebind (T2)\r\n"); + type = DHCPREQUEST; + currentIp = 0; + requestedIp = 0; + serverIp = 0; break; default: - LogTimeF("DHCP -> unknown message %d", code); - break; - } - if (DhcpTrace) - { - Log(" server=" ); Ip4AddressLog(srvIp); - Log(" request="); Ip4AddressLog(reqIp); - Log("\r\n"); + LogTimeF("DHCP -> unknown state %d\r\n", _state); + return 0; } - bool broadcast = DhcpLocalIp == 0; + bool broadcast = currentIp == 0; uint16_t flags = 0; if (broadcast) flags |= 0x8000; DhcpHdrSetOp (pPacket, REQUEST ); DhcpHdrSetHtype (pPacket, ETHERNET ); DhcpHdrSetHlen (pPacket, 6 ); DhcpHdrSetHops (pPacket, 0 ); - DhcpHdrSetXid (pPacket, ID ); //Randomly chosed transaction id used to associate messages to responses + DhcpHdrSetXid (pPacket, _xid ); //Randomly chosed transaction id used to associate messages to responses DhcpHdrSetSecs (pPacket, 0 ); //Seconds since started to boot DhcpHdrSetFlags (pPacket, flags ); //Broadcast (1) Unicast (0) - //DhcpHdrSetCiaddr(pPacket, reqIp ); //'Client' address set by client or 0 if don't know address - DhcpHdrSetCiaddr(pPacket, 0 ); //'Client' address set by client or 0 if don't know address + DhcpHdrSetCiaddr(pPacket, currentIp ); //'Client' address set by client or 0 if don't know address DhcpHdrSetYiaddr(pPacket, 0 ); //'Your' address returned by server - //DhcpHdrSetSiaddr(pPacket, srvIp ); //'Server' address to use if required DhcpHdrSetSiaddr(pPacket, 0 ); //'Server' address to use if required DhcpHdrSetGiaddr(pPacket, 0 ); //'Gateway' address memcpy(DhcpHdrPtrChaddr(pPacket), MacLocal, 6); //'Client hardware' address. 6 bytes for ethernet @@ -237,17 +305,17 @@ char* pOptions = (char*)pPacket + DHCP_HEADER_LENGTH; char* p = pOptions; - *p++ = 53; //Message code - *p++ = 1; - *p++ = code; + *p++ = 53; *p++ = 1; *p++ = type; //DHCP message type + if (requestedIp) writeIp(50, requestedIp, &p); //Requested IP + if ( serverIp) writeIp(54, serverIp, &p); //Server ip + writeText(12, NET_NAME, &p); //Host name + *p++ = 255; //End of options - if (reqIp) writeIp(50, reqIp, &p); //Requested IP - if (srvIp) writeIp(54, srvIp, &p); //Server ip + _awaitResponse = true; + _delayMs <<= 1; //Backoff (double) the delay time after each attempt + if (_delayMs > MAX_REPEAT_DELAY_TIME_MS) _delayMs = MAX_REPEAT_DELAY_TIME_MS; //Don't go beyond a maximum + _repeatDelayMsTimer = MsTimerCount; //Start the delay timer - writeText(12, NET_NAME, &p); - - *p++ = 255; //End of options - return DHCP_HEADER_LENGTH + p - pOptions; } int DhcpHandleResponse(void (*traceback)(void), int sizeRx, char* pPacketRx, int* pSizeTx, char* pPacketTx) @@ -264,31 +332,61 @@ if (htype != ETHERNET) return DO_NOTHING; if (hlen != 6) return DO_NOTHING; if (memcmp(DhcpHdrPtrChaddr(pPacketRx), MacLocal, 6)) return DO_NOTHING; - if (xid != ID) return DO_NOTHING; + if (!_xid) return DO_NOTHING; + if (xid != _xid) return DO_NOTHING; if (cookie != COOKIE) return DO_NOTHING; char* pOptions = (char*)pPacketRx + DHCP_HEADER_LENGTH; - readOptions(sizeRx - DHCP_HEADER_LENGTH, pOptions); - - switch (dhcpMessageType) + readOptionMessageType(sizeRx - DHCP_HEADER_LENGTH, pOptions); + + switch (_dhcpMessageType) { case DHCPOFFER: - if (DhcpTrace) { LogTime("DHCP <- offer ip "); Ip4AddressLog(yiaddr); Log(" server ip "); Ip4AddressLog(DhcpServerIp); Log("\r\n"); } - //*pSizeTx = sendRequest(pPacketTx, DHCPREQUEST, siaddr, yiaddr); - *pSizeTx = sendRequest(pPacketTx, DHCPREQUEST, DhcpServerIp, yiaddr); - return BROADCAST; + if (_state == STATE_DISCOVER) + { + _offeredIp = yiaddr; + readOptionServerId(sizeRx - DHCP_HEADER_LENGTH, pOptions); + if (DhcpTrace) { LogTime("DHCP <- offer "); Ip4AddressLog(_offeredIp); Log(" from server "); Ip4AddressLog(_serverId); Log("\r\n"); } + _awaitResponse = false; + _delayMs = MIN_REPEAT_DELAY_TIME_MS; //Set the delay time back to minimum + } + else + { + if (DhcpTrace) { LogTime("DHCP <- offer "); Ip4AddressLog(_offeredIp); Log(" from server "); Ip4AddressLog(_serverId); Log(" ignored\r\n"); } + } + break; case DHCPACK: - if (DhcpTrace) { LogTime("DHCP <- ack ip "); Ip4AddressLog(yiaddr); Log(" server ip "); Ip4AddressLog(DhcpServerIp); Log("\r\n"); } - DhcpLocalIp = yiaddr; //Save our IP - elapsedLifeMsTimer = MsTimerCount; //Start the life timer - delayMs = MIN_REPEAT_DELAY_TIME_MS; //Set the delay time back to minimum + if (_state == STATE_SELECT || _state == STATE_RENEW || _state == STATE_REBIND) + { + DhcpLocalIp = yiaddr; + readOptions(sizeRx - DHCP_HEADER_LENGTH, pOptions); + DhcpServerIp = _serverId; + if (DhcpTrace) { LogTime("DHCP <- ack "); Ip4AddressLog(DhcpLocalIp); Log(" from server "); Ip4AddressLog(_serverId); Log("\r\n"); } + _elapsedLifeMsTimer = MsTimerCount; //Start the life timer + _awaitResponse = false; + _delayMs = MIN_REPEAT_DELAY_TIME_MS; //Set the delay time back to minimum + } + else + { + if (DhcpTrace) { LogTime("DHCP <- ack "); Ip4AddressLog(DhcpLocalIp); Log(" from server "); Ip4AddressLog(_serverId); Log(" ignored\r\n"); } + } break; case DHCPNAK: - if (DhcpTrace) { LogTime("DHCP <- nack ip "); Ip4AddressLog(yiaddr); Log(" server ip "); Ip4AddressLog(DhcpServerIp); Log("\r\n"); } - clearAll(); + if (_state == STATE_SELECT || _state == STATE_RENEW || _state == STATE_REBIND) + { + readOptionServerId(sizeRx - DHCP_HEADER_LENGTH, pOptions); + if (DhcpTrace) { LogTime("DHCP <- nack "); Ip4AddressLog(yiaddr); Log(" from server "); Ip4AddressLog(_serverId); Log("\r\n"); } + clearAll(); + _awaitResponse = false; + _delayMs = MIN_REPEAT_DELAY_TIME_MS; //Set the delay time back to minimum + } + else + { + if (DhcpTrace) { LogTime("DHCP <- nack "); Ip4AddressLog(yiaddr); Log(" from server "); Ip4AddressLog(_serverId); Log(" ignored\r\n"); } + } break; default: - LogTimeF("DHCP <- unknown message %d\r\n", dhcpMessageType); + LogTimeF("DHCP <- unknown message %d\r\n", _dhcpMessageType); break; } return DO_NOTHING; @@ -296,32 +394,77 @@ int DhcpPollForRequestToSend(void* pPacket, int* pSize) { + + if (!_xid) + { + _xid = (uint32_t)MacLocal[2] << 24; + _xid += (uint32_t)MacLocal[3] << 16; + _xid += (uint32_t)MacLocal[4] << 8; + _xid += (uint32_t)MacLocal[5] << 0; + } + //Check if time to update - uint32_t elapsedTimeMs = MsTimerCount - elapsedLifeMsTimer; + uint32_t elapsedTimeMs = MsTimerCount - _elapsedLifeMsTimer; uint32_t leaseTimeMs = DhcpLeaseTime * 1000; - - if (DhcpLocalIp && elapsedTimeMs < (leaseTimeMs >> 1)) return DO_NOTHING; //Do nothing if have address and within T1 + + uint32_t T1 = leaseTimeMs >> 1; //0.5 + uint32_t T2 = (leaseTimeMs >> 1) + (leaseTimeMs >> 2) + (leaseTimeMs >> 3); //0.875 = 0.5 + 0.25 + 0.125 + + if (!DhcpLocalIp) + { + if (!_offeredIp) _state = STATE_DISCOVER; + else _state = STATE_SELECT; + } + else + { + if (elapsedTimeMs < T1) + { + _state = STATE_BOUND; + } + else if (elapsedTimeMs < T2) + { + _state = STATE_RENEW; + } + else if (elapsedTimeMs < leaseTimeMs) + { + _state = STATE_REBIND; + } + else + { + _state = STATE_EXPIRED; + } + } + static int stateLastScan = STATE_NONE; + bool stateHasChanged = _state != stateLastScan; + stateLastScan = _state; + + bool needToResendRequest = _awaitResponse && MsTimerRelative(_repeatDelayMsTimer, _delayMs); - //Limit retries with a backoff delay - if (!MsTimerRelative(repeatDelayMsTimer, delayMs)) return DO_NOTHING; //Don't retry within the delay time - delayMs <<= 1; //Backoff (double) the delay time after each attempt - if (delayMs > MAX_REPEAT_DELAY_TIME_MS) delayMs = MAX_REPEAT_DELAY_TIME_MS; //Don't go beyond a maximum - repeatDelayMsTimer = MsTimerCount; //Start the delay timer + bool need = stateHasChanged || needToResendRequest; + + if (!need) return DO_NOTHING; //Send the renewal request *pSize = 0; int dest = DO_NOTHING; - if (DhcpLocalIp && elapsedTimeMs < leaseTimeMs) + switch (_state) { - *pSize = sendRequest(pPacket, DHCPREQUEST, DhcpServerIp, DhcpLocalIp); //if within T2 then send request to the server - not broadcast - dest = UNICAST_DHCP; + case STATE_NONE: clearAll(); return DO_NOTHING; + case STATE_DISCOVER: dest = BROADCAST; break; + case STATE_SELECT: dest = BROADCAST; break; + case STATE_BOUND: return DO_NOTHING; + case STATE_RENEW: dest = UNICAST_DHCP; break; + case STATE_REBIND: dest = BROADCAST; break; + case STATE_EXPIRED: + if (DhcpTrace) LogTimeF("DHCP lease has expired\r\n"); + clearAll(); + return DO_NOTHING; + default: + LogTimeF("DHCP -> unknown state %d", _state); + return DO_NOTHING; } - else - { - if (DhcpTrace) LogTimeF("DHCP lease has expired\r\n"); - clearAll(); - *pSize = sendRequest(pPacket, DHCPDISCOVER, 0, 0); //If outside T2 then start from scratch to do a full DHCP - dest = BROADCAST; - } + + *pSize = sendRequest(pPacket); + return ActionMakeFromDestAndTrace(dest, DhcpTrace && NetTraceStack); } \ No newline at end of file