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:
- 115:5c003909bcf3
- Parent:
- 93:580fc113d9e9
- Child:
- 116:60521b29e4c9
diff -r 13fc2c25d105 -r 5c003909bcf3 udp/dhcp/dhcp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/udp/dhcp/dhcp.c Wed Jan 23 15:42:35 2019 +0000 @@ -0,0 +1,303 @@ +#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" + +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 REPEAT_DELAY_TIME_MS 60 * 1000 +static uint32_t delayStartMs = 0; //Reset whenever a message is sent and blocks another send until count exceeds REPEAT_DELAY_TIME +static uint32_t lifeStartMs = 0; //Reset whenever an IP address request has been acknowledged + +uint32_t DhcpGetElapsedLife() +{ + if (!lifeStartMs) return 0; + return (MsTimerCount - lifeStartMs) / 1000; +} + +static uint8_t dhcpMessageType = 0; +uint32_t DhcpLeaseTime = 0; +uint32_t DhcpServer = 0; +uint32_t DhcpRouter = 0; +uint32_t DhcpSubnetMask = 0; +uint32_t DhcpNtp = 0; +uint32_t DhcpRenewalT1 = 0; +uint32_t DhcpRenewalT2 = 0; +uint32_t DhcpBroadcastIp = 0; +uint32_t DhcpLocalIp = 0; +uint32_t DhcpDnsServer = 0; +char DhcpDomainName[20]; +char DhcpHostName[20]; + +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: DhcpRouter = readIp(&p); break; //Router + case 6: DhcpDnsServer = 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: DhcpNtp = 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: DhcpServer = 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 + + delayStartMs = MsTimerCount; + 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; + lifeStartMs = MsTimerCount; + 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) +{ + if (delayStartMs && !MsTimerHasElapsed(delayStartMs, REPEAT_DELAY_TIME_MS)) return DO_NOTHING; //Don't retry within the delay time + + uint32_t elapsedTimeMs = MsTimerCount - lifeStartMs; + uint32_t leaseTimeMs = DhcpLeaseTime * 1000; + + if (DhcpLocalIp && elapsedTimeMs < (leaseTimeMs >> 1)) return 0; //Do nothing if have address and within T1 + + *pSize = 0; + int dest = DO_NOTHING; + if (DhcpLocalIp && elapsedTimeMs < leaseTimeMs) + { + *pSize = sendRequest(pPacket, DHCPREQUEST, DhcpServer, 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; + DhcpServer = 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); +}