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
udp/dhcp/dhcp.c
- Committer:
- andrewboyson
- Date:
- 2020-12-30
- Revision:
- 179:55342264fca1
- Parent:
- 178:52714fef5ca1
- Child:
- 180:0a30cff6b36a
File content as of revision 179:55342264fca1:
#include <stdint.h> #include <stdbool.h> #include <string.h> #include "log.h" #include "mstimer.h" #include "net.h" #include "net-this.h" #include "action.h" #include "eth.h" #include "ip4addr.h" #include "mac.h" #include "udp.h" #include "dnslabel.h" #include "dhcphdr.h" bool DhcpTrace = false; #define REQUEST 1 #define REPLY 2 #define DHCPDISCOVER 1 #define DHCPOFFER 2 #define DHCPREQUEST 3 #define DHCPDECLINE 4 #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 #define DHCPINFORM 8 #define ID 75648 #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 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; } static uint8_t dhcpMessageType = 0; uint32_t DhcpLeaseTime = 0; uint32_t DhcpServerIp = 0; uint32_t DhcpRouterIp = 0; uint32_t DhcpSubnetMask = 0; uint32_t DhcpNtpIp = 0; uint32_t DhcpRenewalT1 = 0; uint32_t DhcpRenewalT2 = 0; uint32_t DhcpBroadcastIp = 0; uint32_t DhcpLocalIp = 0; uint32_t DhcpDnsServerIp = 0; char DhcpDomainName[DNS_MAX_LABEL_LENGTH+1]; char DhcpHostName [DNS_MAX_LABEL_LENGTH+1]; static void clearAll() { DhcpLeaseTime = 0; DhcpServerIp = 0; DhcpRouterIp = 0; DhcpSubnetMask = 0; DhcpNtpIp = 0; DhcpRenewalT1 = 0; DhcpRenewalT2 = 0; DhcpBroadcastIp = 0; DhcpLocalIp = 0; DhcpDnsServerIp = 0; DhcpDomainName[0] = 0; DhcpHostName [0] = 0; } bool DhcpIpNeedsToBeRouted(uint32_t ip) { if ((ip & 0x000000FF) == 0xFF) return false; //Broadcast 255.xxx.xxx.xxx if ((ip & 0x000000FF) == 0xE0) return false; //Multicast 224.xxx.xxx.xxx if (ip == (DhcpLocalIp | 0xFF000000)) return false; //Local broadcast ip == 192.168.0.255 return (ip & DhcpSubnetMask) != (DhcpLocalIp & DhcpSubnetMask); //ip != 192.168.0.xxx } static uint32_t readOption32(char** pp) { uint32_t value = 0; char* p = *pp; int len = *++p; if (len >= 4) { value = *++p << 24; value |= *++p << 16; value |= *++p << 8; value |= *++p << 0; } *pp += len + 1; return value; } static uint32_t readIp(char** pp) { uint32_t value = 0; char* p = *pp; int len = *++p; if (len >= 4) { value = *++p << 0; value |= *++p << 8; value |= *++p << 16; value |= *++p << 24; } *pp += len + 1; return value; } static void readString(char** pp, char* pText) { char* p = *pp; int len = *++p; for (int i = 0; i < len; i++) pText[i] = *++p; *pp += len + 1; } static void readOptions(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 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 default: if (DhcpTrace) LogTimeF("Ignoring option %d\r\n", *p); len = *++p; p += len; return; } p++; } } static void writeIp(uint8_t code, uint32_t value, char** pp) { if (!value) return; char* p = *pp; *p++ = code; *p++ = 4; *p++ = (value & 0x000000FF) >> 0; *p++ = (value & 0x0000FF00) >> 8; *p++ = (value & 0x00FF0000) >> 16; *p++ = (value & 0xFF000000) >> 24; *pp += 6; } static void writeText(uint8_t code, const char* pText, char** pp) { if (!pText) return; if (!*pText) return; char* p = *pp; *p++ = code; char* pLength = p++; while (true) //Copy pText without the end zero { *p = *pText; pText++; if (!*pText) break; p++; } *pLength = p - pLength; *pp += *pLength + 2; } int sendRequest(void* pPacket, uint8_t code, uint32_t srvIp, uint32_t reqIp) { switch (code) { case DHCPDISCOVER: if (DhcpTrace) LogTimeF("DHCP -> discover"); break; case DHCPREQUEST: if (DhcpTrace) LogTimeF("DHCP -> request"); break; default: LogTimeF("DHCP -> unknown message %d", code); break; } if (DhcpTrace) { Log(" server=" ); Ip4AddressLog(srvIp); Log(" request="); Ip4AddressLog(reqIp); Log(" local=" ); Ip4AddressLog(DhcpLocalIp); Log("\r\n"); } bool broadcast = DhcpLocalIp == 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 DhcpHdrSetSecs (pPacket, 0 ); //Seconds since started to boot DhcpHdrSetFlags (pPacket, flags ); //Broadcast (1) Unicast (0) DhcpHdrSetCiaddr(pPacket, DhcpLocalIp ); //'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 DhcpHdrSetGiaddr(pPacket, 0 ); //'Gateway' address memcpy(DhcpHdrPtrChaddr(pPacket), MacLocal, 6); //'Client hardware' address. 6 bytes for ethernet memset(DhcpHdrPtrLegacy(pPacket), 0, 192 ); //BootP legacy fill with zeros DhcpHdrSetCookie(pPacket, COOKIE ); //Magic cookie char* pOptions = (char*)pPacket + DHCP_HEADER_LENGTH; char* p = pOptions; *p++ = 53; //Message code *p++ = 1; *p++ = code; writeIp(50, reqIp, &p); //Requested IP writeIp(54, srvIp, &p); //Server ip 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) { uint8_t op = DhcpHdrGetOp (pPacketRx); uint8_t htype = DhcpHdrGetHtype (pPacketRx); uint8_t hlen = DhcpHdrGetHlen (pPacketRx); uint32_t xid = DhcpHdrGetXid (pPacketRx); //Randomly chosen transaction id used to associate messages to responses uint32_t yiaddr = DhcpHdrGetYiaddr(pPacketRx); uint32_t siaddr = DhcpHdrGetSiaddr(pPacketRx); uint32_t cookie = DhcpHdrGetCookie(pPacketRx); if (op != REPLY) return DO_NOTHING; 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 (cookie != COOKIE) return DO_NOTHING; char* pOptions = (char*)pPacketRx + DHCP_HEADER_LENGTH; readOptions(sizeRx - DHCP_HEADER_LENGTH, pOptions); switch (dhcpMessageType) { case DHCPOFFER: if (DhcpTrace) { LogTime("DHCP <- offer ip "); Ip4AddressLog(yiaddr); Log("\r\n"); } *pSizeTx = sendRequest(pPacketTx, DHCPREQUEST, siaddr, yiaddr); return BROADCAST; case DHCPACK: if (DhcpTrace) { LogTime("DHCP <- ack ip "); Ip4AddressLog(yiaddr); Log("\r\n"); } DhcpLocalIp = yiaddr; elapsedLifeMsTimer = MsTimerCount; //Start the life timer delayMs = MIN_REPEAT_DELAY_TIME_MS; //Set the delay time back to minimum break; case DHCPNAK: if (DhcpTrace) { LogTime("DHCP <- nack ip "); Ip4AddressLog(yiaddr); Log("\r\n"); } clearAll(); break; default: LogTimeF("DHCP <- unknown message %d\r\n", dhcpMessageType); break; } return DO_NOTHING; } int DhcpPollForRequestToSend(void* pPacket, int* pSize) { //Check if time to update 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 //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 //Send the renewal request *pSize = 0; int dest = DO_NOTHING; if (DhcpLocalIp && elapsedTimeMs < leaseTimeMs) { *pSize = sendRequest(pPacket, DHCPREQUEST, DhcpServerIp, DhcpLocalIp); //if within T2 then send request to the server - not broadcast dest = UNICAST_DHCP; } 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; } return ActionMakeFromDestAndTrace(dest, DhcpTrace); }