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

Revision:
10:f0854784e960
Child:
13:9cd54f7db57a
diff -r 91dae5300a4d -r f0854784e960 udp/dhcp.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/udp/dhcp.cpp	Sun Apr 16 14:21:55 2017 +0000
@@ -0,0 +1,292 @@
+#include "mbed.h"
+#include  "log.h"
+#include  "net.h"
+#include  "eth.h"
+#include  "udp.h"
+
+#define DEBUG 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 60
+static uint32_t delayTime   = 10000000; //Reset whenever a message is sent and blocks another send until count exceeds REPEAT_DELAY_TIME
+static uint32_t elapsedTime = 0;        //Reset whenever an IP address request has been acknowledged 
+void DhcpTick()
+{
+    elapsedTime++;
+      delayTime++;
+}
+
+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];
+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 (DEBUG) 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 (DEBUG) LogTimeF("DHCP -> discover");
+            break;
+        case DHCPREQUEST:
+            if (DEBUG) LogTimeF("DHCP -> request");
+            break;
+        default:
+            LogTimeF("DHCP -> unknown message %d", code);
+            break;
+    }
+    if (DEBUG)
+    {
+        char text[20];
+        NetIp4AddressToString(srvIp, sizeof(text), text);
+        LogF(" server=%s", text);
+        NetIp4AddressToString(reqIp, sizeof(text), text);
+        LogF(" request=%s", text);
+        NetIp4AddressToString(DhcpLocalIp, sizeof(text), text);
+        LogF(" local=%s\r\n", text);
+    }
+    
+    bool broadcast = DhcpLocalIp == 0;
+    uint16_t flags = 0;
+    if (broadcast) flags |= 0x0080; //0x8000 on the net == 0x0080 in little endian
+    struct header* pHeader = (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, EthLocalMac, 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
+    
+    delayTime = 0;
+    return HEADER_LENGTH + p - pOptions;
+}
+int DhcpHandleResponse(int* pSize, void* pPacket)
+{
+    struct header* pHeader = (header*)pPacket;
+
+    uint8_t  op      = pHeader->op;
+    uint8_t  htype   = pHeader->htype;
+    uint8_t  hlen    = pHeader->hlen;
+    
+    uint32_t xid     = pHeader->xid;    //Randomly chosed transaction id used to associate messages to responses
+    
+    uint16_t secs    = NetToHost16(pHeader->secs);
+    uint16_t flags   = NetToHost16(pHeader->flags);
+    
+    uint32_t yiaddr  = pHeader->yiaddr;
+    uint32_t siaddr  = pHeader->siaddr;
+    uint32_t cookie  = NetToHost32(pHeader->cookie);
+        
+    if (op     != REPLY)    return DO_NOTHING;
+    if (htype  != ETHERNET) return DO_NOTHING;
+    if (hlen   != 6)        return DO_NOTHING;
+    if (xid    != ID)       return DO_NOTHING;
+    if (cookie != COOKIE)   return DO_NOTHING;
+    
+    char* pOptions = (char*)pPacket + HEADER_LENGTH;
+    readOptions(*pSize - HEADER_LENGTH, pOptions);
+    
+    char text[30];
+    switch (dhcpMessageType)
+    {
+        case DHCPOFFER:
+            NetIp4AddressToString(yiaddr, sizeof(text), text);
+            if (DEBUG) LogTimeF("DHCP <- offer ip %s\r\n", text);
+            *pSize =sendRequest(pPacket, DHCPREQUEST, siaddr, yiaddr);
+            return BROADCAST;
+        case DHCPACK:
+            NetIp4AddressToString(yiaddr, sizeof(text), text);
+            if (DEBUG) LogTimeF("DHCP <- ack ip %s\r\n", text);
+            DhcpLocalIp = yiaddr;
+            elapsedTime = 0;
+            break;
+        case DHCPNAK:
+            NetIp4AddressToString(yiaddr, sizeof(text), text);
+            if (DEBUG) LogTimeF("DHCP <- nack ip %s\r\n", text);
+            break;
+        default:
+            LogTimeF("DHCP <- unknown message %d\r\n", dhcpMessageType);
+            break;
+    }
+    return DO_NOTHING;
+}
+
+int DhcpPollForRequestToSend(void* pPacket, int* pSize)
+{    
+    if (delayTime < REPEAT_DELAY_TIME) return DO_NOTHING; //Don't retry within the delay time
+    
+    if (DhcpLocalIp && elapsedTime < (DhcpLeaseTime >> 1)) return 0; //Do nothing if have address and within T1
+    
+    *pSize = 0;
+    
+    if (DhcpLocalIp && elapsedTime <  DhcpLeaseTime)
+    {
+        *pSize = sendRequest(pPacket, DHCPREQUEST, DhcpServer, DhcpLocalIp); //if within T2 then send request to the server - not broadcast
+        return UNICAST_DHCP;
+    }
+    else
+    {
+        if (DEBUG) 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
+        return BROADCAST;
+    }
+    
+}