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:
- 2019-01-29
- Revision:
- 119:8e1a7805b801
- Parent:
- 116:60521b29e4c9
- Child:
- 128:79052cb4a41c
File content as of revision 119:8e1a7805b801:
#include <stdint.h> #include <stdbool.h> #include <string.h> #include "log.h" #include "mstimer.h" #include "net.h" #include "action.h" #include "eth.h" #include "ip4addr.h" #include "mac.h" #include "udp.h" #include "dns.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 HEADER_LENGTH 240 __packed struct header { uint8_t op; uint8_t htype; uint8_t hlen; uint8_t hops; uint32_t xid; uint16_t secs; uint16_t flags; uint32_t ciaddr; uint32_t yiaddr; uint32_t siaddr; uint32_t giaddr; uint8_t chaddr[16]; char legacy[192]; uint32_t cookie; }; #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]; char DhcpHostName [DNS_MAX_LABEL_LENGTH]; bool DhcpIpNeedsToBeRouted(uint32_t ip) { return ip & DhcpSubnetMask != DhcpBroadcastIp & DhcpSubnetMask; } 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; } 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 |= 0x0080; //0x8000 on the net == 0x0080 in little endian struct header* pHeader = (struct header*)pPacket; pHeader->op = REQUEST; pHeader->htype = ETHERNET; pHeader->hlen = 6; pHeader->hops = 0; pHeader->xid = ID; //Randomly chosed transaction id used to associate messages to responses pHeader->secs = 0; //Seconds since started to boot pHeader->flags = flags; //Broadcast (1) Unicast (0) pHeader->ciaddr = DhcpLocalIp; //'Client' address set by client or 0 if don't know address pHeader->yiaddr = 0; //'Your' address returned by server pHeader->siaddr = srvIp; //'Server' address to use if required pHeader->giaddr = 0; //'Gateway' address memcpy(pHeader->chaddr, MacLocal, 6); //'Client hardware' address. 6 bytes for ethernet memset(pHeader->legacy, 0, 192); //BootP legacy fill with zeros pHeader->cookie = NetToHost32(COOKIE); //Magic cookie char* pOptions = (char*)pPacket + 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 *p++ = 255; //End of options return HEADER_LENGTH + p - pOptions; } int DhcpHandleResponse(void (*traceback)(void), int sizeRx, void* pPacketRx, int* pSizeTx, void* pPacketTx) { struct header* pHeaderRx = (struct header*)pPacketRx; uint8_t op = pHeaderRx->op; uint8_t htype = pHeaderRx->htype; uint8_t hlen = pHeaderRx->hlen; uint32_t xid = pHeaderRx->xid; //Randomly chosen transaction id used to associate messages to responses uint16_t secs = NetToHost16(pHeaderRx->secs); uint16_t flags = NetToHost16(pHeaderRx->flags); uint32_t yiaddr = pHeaderRx->yiaddr; uint32_t siaddr = pHeaderRx->siaddr; uint32_t cookie = NetToHost32(pHeaderRx->cookie); if (op != REPLY) return DO_NOTHING; if (htype != ETHERNET) return DO_NOTHING; if (hlen != 6) return DO_NOTHING; if (memcmp(pHeaderRx->chaddr, MacLocal, 6)) return DO_NOTHING; if (xid != ID) return DO_NOTHING; if (cookie != COOKIE) return DO_NOTHING; char* pOptions = (char*)pPacketRx + HEADER_LENGTH; readOptions(sizeRx - 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"); } 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 (!MsTimerHasElapsed(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"); DhcpLocalIp = 0; DhcpServerIp = 0; *pSize = sendRequest(pPacket, DHCPDISCOVER, 0, 0); //If outside T2 then start from scratch to do a full DHCP dest = BROADCAST; } return ActionMakeFromDestAndTrace(dest, DhcpTrace); }