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

Committer:
andrewboyson
Date:
Sun Sep 29 18:51:58 2019 +0000
Revision:
160:6a1d1d368f80
Parent:
136:8a65abb0dc63
Child:
174:60e5ab296671
Corrected some minor bugs

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 61:aad055f1b0d1 1 #include <stdint.h>
andrewboyson 61:aad055f1b0d1 2 #include <stdbool.h>
andrewboyson 61:aad055f1b0d1 3 #include <string.h>
andrewboyson 61:aad055f1b0d1 4
andrewboyson 128:79052cb4a41c 5 #include "log.h"
andrewboyson 128:79052cb4a41c 6 #include "mstimer.h"
andrewboyson 128:79052cb4a41c 7 #include "net.h"
andrewboyson 128:79052cb4a41c 8 #include "action.h"
andrewboyson 128:79052cb4a41c 9 #include "eth.h"
andrewboyson 128:79052cb4a41c 10 #include "ip4addr.h"
andrewboyson 128:79052cb4a41c 11 #include "mac.h"
andrewboyson 128:79052cb4a41c 12 #include "udp.h"
andrewboyson 128:79052cb4a41c 13 #include "dnslabel.h"
andrewboyson 136:8a65abb0dc63 14 #include "dhcphdr.h"
andrewboyson 10:f0854784e960 15
andrewboyson 37:793b39683406 16 bool DhcpTrace = false;
andrewboyson 10:f0854784e960 17
andrewboyson 10:f0854784e960 18 #define REQUEST 1
andrewboyson 10:f0854784e960 19 #define REPLY 2
andrewboyson 10:f0854784e960 20
andrewboyson 10:f0854784e960 21 #define DHCPDISCOVER 1
andrewboyson 10:f0854784e960 22 #define DHCPOFFER 2
andrewboyson 10:f0854784e960 23 #define DHCPREQUEST 3
andrewboyson 10:f0854784e960 24 #define DHCPDECLINE 4
andrewboyson 10:f0854784e960 25 #define DHCPACK 5
andrewboyson 10:f0854784e960 26 #define DHCPNAK 6
andrewboyson 10:f0854784e960 27 #define DHCPRELEASE 7
andrewboyson 10:f0854784e960 28 #define DHCPINFORM 8
andrewboyson 10:f0854784e960 29
andrewboyson 10:f0854784e960 30 #define ID 75648
andrewboyson 10:f0854784e960 31 #define COOKIE 0x63825363
andrewboyson 10:f0854784e960 32
andrewboyson 116:60521b29e4c9 33 #define MAX_REPEAT_DELAY_TIME_MS 60000
andrewboyson 119:8e1a7805b801 34 #define MIN_REPEAT_DELAY_TIME_MS 900
andrewboyson 119:8e1a7805b801 35 static uint32_t repeatDelayMsTimer = (uint32_t)-MIN_REPEAT_DELAY_TIME_MS; //Initial value ensures no delay at startup
andrewboyson 119:8e1a7805b801 36 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
andrewboyson 116:60521b29e4c9 37
andrewboyson 136:8a65abb0dc63 38 static uint32_t elapsedLifeMsTimer = 0; //Started whenever an IP address request has been acknowledged
andrewboyson 93:580fc113d9e9 39
andrewboyson 93:580fc113d9e9 40 uint32_t DhcpGetElapsedLife()
andrewboyson 10:f0854784e960 41 {
andrewboyson 119:8e1a7805b801 42 if (!elapsedLifeMsTimer) return 0;
andrewboyson 119:8e1a7805b801 43 return (MsTimerCount - elapsedLifeMsTimer) / 1000;
andrewboyson 10:f0854784e960 44 }
andrewboyson 10:f0854784e960 45
andrewboyson 10:f0854784e960 46 static uint8_t dhcpMessageType = 0;
andrewboyson 10:f0854784e960 47 uint32_t DhcpLeaseTime = 0;
andrewboyson 116:60521b29e4c9 48 uint32_t DhcpServerIp = 0;
andrewboyson 116:60521b29e4c9 49 uint32_t DhcpRouterIp = 0;
andrewboyson 10:f0854784e960 50 uint32_t DhcpSubnetMask = 0;
andrewboyson 116:60521b29e4c9 51 uint32_t DhcpNtpIp = 0;
andrewboyson 10:f0854784e960 52 uint32_t DhcpRenewalT1 = 0;
andrewboyson 10:f0854784e960 53 uint32_t DhcpRenewalT2 = 0;
andrewboyson 10:f0854784e960 54 uint32_t DhcpBroadcastIp = 0;
andrewboyson 10:f0854784e960 55 uint32_t DhcpLocalIp = 0;
andrewboyson 116:60521b29e4c9 56 uint32_t DhcpDnsServerIp = 0;
andrewboyson 128:79052cb4a41c 57 char DhcpDomainName[DNS_MAX_LABEL_LENGTH+1];
andrewboyson 128:79052cb4a41c 58 char DhcpHostName [DNS_MAX_LABEL_LENGTH+1];
andrewboyson 44:83ce5ace337b 59
andrewboyson 44:83ce5ace337b 60 bool DhcpIpNeedsToBeRouted(uint32_t ip)
andrewboyson 136:8a65abb0dc63 61 {
andrewboyson 160:6a1d1d368f80 62 return (ip & DhcpSubnetMask) != (DhcpBroadcastIp & DhcpSubnetMask);
andrewboyson 44:83ce5ace337b 63 }
andrewboyson 44:83ce5ace337b 64
andrewboyson 10:f0854784e960 65 static uint32_t readOption32(char** pp)
andrewboyson 10:f0854784e960 66 {
andrewboyson 10:f0854784e960 67 uint32_t value = 0;
andrewboyson 10:f0854784e960 68 char* p = *pp;
andrewboyson 10:f0854784e960 69 int len = *++p;
andrewboyson 10:f0854784e960 70 if (len >= 4)
andrewboyson 10:f0854784e960 71 {
andrewboyson 10:f0854784e960 72 value = *++p << 24;
andrewboyson 10:f0854784e960 73 value |= *++p << 16;
andrewboyson 10:f0854784e960 74 value |= *++p << 8;
andrewboyson 10:f0854784e960 75 value |= *++p << 0;
andrewboyson 10:f0854784e960 76 }
andrewboyson 10:f0854784e960 77 *pp += len + 1;
andrewboyson 10:f0854784e960 78 return value;
andrewboyson 10:f0854784e960 79 }
andrewboyson 10:f0854784e960 80 static uint32_t readIp(char** pp)
andrewboyson 10:f0854784e960 81 {
andrewboyson 10:f0854784e960 82 uint32_t value = 0;
andrewboyson 10:f0854784e960 83 char* p = *pp;
andrewboyson 10:f0854784e960 84 int len = *++p;
andrewboyson 10:f0854784e960 85 if (len >= 4)
andrewboyson 10:f0854784e960 86 {
andrewboyson 10:f0854784e960 87 value = *++p << 0;
andrewboyson 10:f0854784e960 88 value |= *++p << 8;
andrewboyson 10:f0854784e960 89 value |= *++p << 16;
andrewboyson 10:f0854784e960 90 value |= *++p << 24;
andrewboyson 10:f0854784e960 91 }
andrewboyson 10:f0854784e960 92 *pp += len + 1;
andrewboyson 10:f0854784e960 93 return value;
andrewboyson 10:f0854784e960 94 }
andrewboyson 10:f0854784e960 95 static void readString(char** pp, char* pText)
andrewboyson 10:f0854784e960 96 {
andrewboyson 10:f0854784e960 97 char* p = *pp;
andrewboyson 10:f0854784e960 98 int len = *++p;
andrewboyson 10:f0854784e960 99 for (int i = 0; i < len; i++) pText[i] = *++p;
andrewboyson 10:f0854784e960 100 *pp += len + 1;
andrewboyson 10:f0854784e960 101 }
andrewboyson 10:f0854784e960 102 static void readOptions(int size, char * pOptions)
andrewboyson 10:f0854784e960 103 {
andrewboyson 10:f0854784e960 104 int len = 0;
andrewboyson 10:f0854784e960 105 char* p = pOptions;
andrewboyson 10:f0854784e960 106 char* pE = pOptions + size;
andrewboyson 10:f0854784e960 107 while( p < pE)
andrewboyson 10:f0854784e960 108 {
andrewboyson 10:f0854784e960 109 switch (*p)
andrewboyson 10:f0854784e960 110 {
andrewboyson 10:f0854784e960 111 case 0: break; //NOP
andrewboyson 10:f0854784e960 112 case 255: return; //End of options
andrewboyson 136:8a65abb0dc63 113 case 1: DhcpSubnetMask = readIp(&p); break; //Subnet Mask
andrewboyson 136:8a65abb0dc63 114 case 3: DhcpRouterIp = readIp(&p); break; //Router
andrewboyson 136:8a65abb0dc63 115 case 6: DhcpDnsServerIp = readIp(&p); break; //DNS server
andrewboyson 10:f0854784e960 116 case 12: readString(&p, DhcpHostName); break; //Host name
andrewboyson 10:f0854784e960 117 case 15: readString(&p, DhcpDomainName); break; //Domain name
andrewboyson 10:f0854784e960 118 case 19: len = *++p; p+= len; break; //IP forwarding yes/no
andrewboyson 136:8a65abb0dc63 119 case 28: DhcpBroadcastIp = readIp(&p); break; //Broadcast IP
andrewboyson 136:8a65abb0dc63 120 case 42: DhcpNtpIp = readIp(&p); break; //NTP
andrewboyson 10:f0854784e960 121 case 44: len = *++p; p+= len; break; //NetBIOS name server
andrewboyson 10:f0854784e960 122 case 45: len = *++p; p+= len; break; //NetBIOS datagram server
andrewboyson 10:f0854784e960 123 case 46: len = *++p; p+= len; break; //NetBIOS node type
andrewboyson 10:f0854784e960 124 case 47: len = *++p; p+= len; break; //NetBIOS scope
andrewboyson 10:f0854784e960 125 case 53: len = *++p; dhcpMessageType = *++p; break; //DHCP message type
andrewboyson 10:f0854784e960 126 case 51: DhcpLeaseTime = readOption32(&p); break; //Address lease time
andrewboyson 116:60521b29e4c9 127 case 54: DhcpServerIp = readIp(&p); break; //DHCP server
andrewboyson 10:f0854784e960 128 case 58: DhcpRenewalT1 = readOption32(&p); break; //T1
andrewboyson 10:f0854784e960 129 case 59: DhcpRenewalT2 = readOption32(&p); break; //T2
andrewboyson 10:f0854784e960 130 default:
andrewboyson 37:793b39683406 131 if (DhcpTrace) LogTimeF("Ignoring option %d\r\n", *p);
andrewboyson 10:f0854784e960 132 len = *++p;
andrewboyson 10:f0854784e960 133 p += len;
andrewboyson 10:f0854784e960 134 return;
andrewboyson 10:f0854784e960 135 }
andrewboyson 10:f0854784e960 136 p++;
andrewboyson 10:f0854784e960 137 }
andrewboyson 10:f0854784e960 138 }
andrewboyson 10:f0854784e960 139 static void writeIp(uint8_t code, uint32_t value, char** pp)
andrewboyson 10:f0854784e960 140 {
andrewboyson 10:f0854784e960 141 if (!value) return;
andrewboyson 136:8a65abb0dc63 142
andrewboyson 10:f0854784e960 143 char* p = *pp;
andrewboyson 136:8a65abb0dc63 144
andrewboyson 10:f0854784e960 145 *p++ = code;
andrewboyson 10:f0854784e960 146 *p++ = 4;
andrewboyson 10:f0854784e960 147 *p++ = (value & 0x000000FF) >> 0;
andrewboyson 10:f0854784e960 148 *p++ = (value & 0x0000FF00) >> 8;
andrewboyson 10:f0854784e960 149 *p++ = (value & 0x00FF0000) >> 16;
andrewboyson 10:f0854784e960 150 *p++ = (value & 0xFF000000) >> 24;
andrewboyson 136:8a65abb0dc63 151
andrewboyson 10:f0854784e960 152 *pp += 6;
andrewboyson 10:f0854784e960 153 }
andrewboyson 10:f0854784e960 154 int sendRequest(void* pPacket, uint8_t code, uint32_t srvIp, uint32_t reqIp)
andrewboyson 10:f0854784e960 155 {
andrewboyson 136:8a65abb0dc63 156
andrewboyson 10:f0854784e960 157 switch (code)
andrewboyson 10:f0854784e960 158 {
andrewboyson 10:f0854784e960 159 case DHCPDISCOVER:
andrewboyson 37:793b39683406 160 if (DhcpTrace) LogTimeF("DHCP -> discover");
andrewboyson 10:f0854784e960 161 break;
andrewboyson 10:f0854784e960 162 case DHCPREQUEST:
andrewboyson 37:793b39683406 163 if (DhcpTrace) LogTimeF("DHCP -> request");
andrewboyson 10:f0854784e960 164 break;
andrewboyson 10:f0854784e960 165 default:
andrewboyson 10:f0854784e960 166 LogTimeF("DHCP -> unknown message %d", code);
andrewboyson 10:f0854784e960 167 break;
andrewboyson 10:f0854784e960 168 }
andrewboyson 37:793b39683406 169 if (DhcpTrace)
andrewboyson 10:f0854784e960 170 {
andrewboyson 47:73af5c0b0dc2 171 Log(" server=" ); Ip4AddressLog(srvIp);
andrewboyson 47:73af5c0b0dc2 172 Log(" request="); Ip4AddressLog(reqIp);
andrewboyson 47:73af5c0b0dc2 173 Log(" local=" ); Ip4AddressLog(DhcpLocalIp);
andrewboyson 47:73af5c0b0dc2 174 Log("\r\n");
andrewboyson 10:f0854784e960 175 }
andrewboyson 136:8a65abb0dc63 176
andrewboyson 10:f0854784e960 177 bool broadcast = DhcpLocalIp == 0;
andrewboyson 10:f0854784e960 178 uint16_t flags = 0;
andrewboyson 136:8a65abb0dc63 179 if (broadcast) flags |= 0x8000;
andrewboyson 136:8a65abb0dc63 180 DhcpHdrSetOp (pPacket, REQUEST );
andrewboyson 136:8a65abb0dc63 181 DhcpHdrSetHtype (pPacket, ETHERNET );
andrewboyson 136:8a65abb0dc63 182 DhcpHdrSetHlen (pPacket, 6 );
andrewboyson 136:8a65abb0dc63 183 DhcpHdrSetHops (pPacket, 0 );
andrewboyson 136:8a65abb0dc63 184 DhcpHdrSetXid (pPacket, ID ); //Randomly chosed transaction id used to associate messages to responses
andrewboyson 136:8a65abb0dc63 185 DhcpHdrSetSecs (pPacket, 0 ); //Seconds since started to boot
andrewboyson 136:8a65abb0dc63 186 DhcpHdrSetFlags (pPacket, flags ); //Broadcast (1) Unicast (0)
andrewboyson 136:8a65abb0dc63 187 DhcpHdrSetCiaddr(pPacket, DhcpLocalIp ); //'Client' address set by client or 0 if don't know address
andrewboyson 136:8a65abb0dc63 188 DhcpHdrSetYiaddr(pPacket, 0 ); //'Your' address returned by server
andrewboyson 136:8a65abb0dc63 189 DhcpHdrSetSiaddr(pPacket, srvIp ); //'Server' address to use if required
andrewboyson 136:8a65abb0dc63 190 DhcpHdrSetGiaddr(pPacket, 0 ); //'Gateway' address
andrewboyson 136:8a65abb0dc63 191 memcpy(DhcpHdrPtrChaddr(pPacket), MacLocal, 6); //'Client hardware' address. 6 bytes for ethernet
andrewboyson 136:8a65abb0dc63 192 memset(DhcpHdrPtrLegacy(pPacket), 0, 192 ); //BootP legacy fill with zeros
andrewboyson 136:8a65abb0dc63 193 DhcpHdrSetCookie(pPacket, COOKIE ); //Magic cookie
andrewboyson 136:8a65abb0dc63 194
andrewboyson 136:8a65abb0dc63 195 char* pOptions = (char*)pPacket + DHCP_HEADER_LENGTH;
andrewboyson 10:f0854784e960 196 char* p = pOptions;
andrewboyson 10:f0854784e960 197 *p++ = 53; //Message code
andrewboyson 10:f0854784e960 198 *p++ = 1;
andrewboyson 10:f0854784e960 199 *p++ = code;
andrewboyson 136:8a65abb0dc63 200
andrewboyson 10:f0854784e960 201 writeIp(50, reqIp, &p); //Requested IP
andrewboyson 10:f0854784e960 202 writeIp(54, srvIp, &p); //Server ip
andrewboyson 136:8a65abb0dc63 203
andrewboyson 10:f0854784e960 204 *p++ = 255; //End of options
andrewboyson 10:f0854784e960 205
andrewboyson 136:8a65abb0dc63 206 return DHCP_HEADER_LENGTH + p - pOptions;
andrewboyson 136:8a65abb0dc63 207 }
andrewboyson 136:8a65abb0dc63 208 int DhcpHandleResponse(void (*traceback)(void), int sizeRx, char* pPacketRx, int* pSizeTx, char* pPacketTx)
andrewboyson 136:8a65abb0dc63 209 {
andrewboyson 136:8a65abb0dc63 210 uint8_t op = DhcpHdrGetOp (pPacketRx);
andrewboyson 136:8a65abb0dc63 211 uint8_t htype = DhcpHdrGetHtype (pPacketRx);
andrewboyson 136:8a65abb0dc63 212 uint8_t hlen = DhcpHdrGetHlen (pPacketRx);
andrewboyson 136:8a65abb0dc63 213 uint32_t xid = DhcpHdrGetXid (pPacketRx); //Randomly chosen transaction id used to associate messages to responses
andrewboyson 136:8a65abb0dc63 214 uint32_t yiaddr = DhcpHdrGetYiaddr(pPacketRx);
andrewboyson 136:8a65abb0dc63 215 uint32_t siaddr = DhcpHdrGetSiaddr(pPacketRx);
andrewboyson 136:8a65abb0dc63 216 uint32_t cookie = DhcpHdrGetCookie(pPacketRx);
andrewboyson 136:8a65abb0dc63 217
andrewboyson 136:8a65abb0dc63 218 if (op != REPLY) return DO_NOTHING;
andrewboyson 136:8a65abb0dc63 219 if (htype != ETHERNET) return DO_NOTHING;
andrewboyson 136:8a65abb0dc63 220 if (hlen != 6) return DO_NOTHING;
andrewboyson 136:8a65abb0dc63 221 if (memcmp(DhcpHdrPtrChaddr(pPacketRx), MacLocal, 6)) return DO_NOTHING;
andrewboyson 136:8a65abb0dc63 222 if (xid != ID) return DO_NOTHING;
andrewboyson 136:8a65abb0dc63 223 if (cookie != COOKIE) return DO_NOTHING;
andrewboyson 136:8a65abb0dc63 224
andrewboyson 136:8a65abb0dc63 225 char* pOptions = (char*)pPacketRx + DHCP_HEADER_LENGTH;
andrewboyson 136:8a65abb0dc63 226 readOptions(sizeRx - DHCP_HEADER_LENGTH, pOptions);
andrewboyson 136:8a65abb0dc63 227
andrewboyson 10:f0854784e960 228 switch (dhcpMessageType)
andrewboyson 10:f0854784e960 229 {
andrewboyson 10:f0854784e960 230 case DHCPOFFER:
andrewboyson 47:73af5c0b0dc2 231 if (DhcpTrace) { LogTime("DHCP <- offer ip "); Ip4AddressLog(yiaddr); Log("\r\n"); }
andrewboyson 59:e0e556c8bd46 232 *pSizeTx = sendRequest(pPacketTx, DHCPREQUEST, siaddr, yiaddr);
andrewboyson 10:f0854784e960 233 return BROADCAST;
andrewboyson 10:f0854784e960 234 case DHCPACK:
andrewboyson 47:73af5c0b0dc2 235 if (DhcpTrace) { LogTime("DHCP <- ack ip "); Ip4AddressLog(yiaddr); Log("\r\n"); }
andrewboyson 10:f0854784e960 236 DhcpLocalIp = yiaddr;
andrewboyson 119:8e1a7805b801 237 elapsedLifeMsTimer = MsTimerCount; //Start the life timer
andrewboyson 116:60521b29e4c9 238 delayMs = MIN_REPEAT_DELAY_TIME_MS; //Set the delay time back to minimum
andrewboyson 10:f0854784e960 239 break;
andrewboyson 10:f0854784e960 240 case DHCPNAK:
andrewboyson 47:73af5c0b0dc2 241 if (DhcpTrace) { LogTime("DHCP <- nack ip "); Ip4AddressLog(yiaddr); Log("\r\n"); }
andrewboyson 10:f0854784e960 242 break;
andrewboyson 10:f0854784e960 243 default:
andrewboyson 10:f0854784e960 244 LogTimeF("DHCP <- unknown message %d\r\n", dhcpMessageType);
andrewboyson 10:f0854784e960 245 break;
andrewboyson 10:f0854784e960 246 }
andrewboyson 10:f0854784e960 247 return DO_NOTHING;
andrewboyson 10:f0854784e960 248 }
andrewboyson 10:f0854784e960 249
andrewboyson 10:f0854784e960 250 int DhcpPollForRequestToSend(void* pPacket, int* pSize)
andrewboyson 116:60521b29e4c9 251 {
andrewboyson 119:8e1a7805b801 252 //Check if time to update
andrewboyson 119:8e1a7805b801 253 uint32_t elapsedTimeMs = MsTimerCount - elapsedLifeMsTimer;
andrewboyson 93:580fc113d9e9 254 uint32_t leaseTimeMs = DhcpLeaseTime * 1000;
andrewboyson 93:580fc113d9e9 255
andrewboyson 119:8e1a7805b801 256 if (DhcpLocalIp && elapsedTimeMs < (leaseTimeMs >> 1)) return DO_NOTHING; //Do nothing if have address and within T1
andrewboyson 136:8a65abb0dc63 257
andrewboyson 119:8e1a7805b801 258 //Limit retries with a backoff delay
andrewboyson 133:a37eb35a03f1 259 if (!MsTimerRelative(repeatDelayMsTimer, delayMs)) return DO_NOTHING; //Don't retry within the delay time
andrewboyson 119:8e1a7805b801 260 delayMs <<= 1; //Backoff (double) the delay time after each attempt
andrewboyson 119:8e1a7805b801 261 if (delayMs > MAX_REPEAT_DELAY_TIME_MS) delayMs = MAX_REPEAT_DELAY_TIME_MS; //Don't go beyond a maximum
andrewboyson 119:8e1a7805b801 262 repeatDelayMsTimer = MsTimerCount; //Start the delay timer
andrewboyson 136:8a65abb0dc63 263
andrewboyson 119:8e1a7805b801 264 //Send the renewal request
andrewboyson 10:f0854784e960 265 *pSize = 0;
andrewboyson 37:793b39683406 266 int dest = DO_NOTHING;
andrewboyson 93:580fc113d9e9 267 if (DhcpLocalIp && elapsedTimeMs < leaseTimeMs)
andrewboyson 10:f0854784e960 268 {
andrewboyson 116:60521b29e4c9 269 *pSize = sendRequest(pPacket, DHCPREQUEST, DhcpServerIp, DhcpLocalIp); //if within T2 then send request to the server - not broadcast
andrewboyson 37:793b39683406 270 dest = UNICAST_DHCP;
andrewboyson 10:f0854784e960 271 }
andrewboyson 10:f0854784e960 272 else
andrewboyson 10:f0854784e960 273 {
andrewboyson 37:793b39683406 274 if (DhcpTrace) LogTimeF("DHCP lease has expired\r\n");
andrewboyson 10:f0854784e960 275 DhcpLocalIp = 0;
andrewboyson 116:60521b29e4c9 276 DhcpServerIp = 0;
andrewboyson 10:f0854784e960 277 *pSize = sendRequest(pPacket, DHCPDISCOVER, 0, 0); //If outside T2 then start from scratch to do a full DHCP
andrewboyson 37:793b39683406 278 dest = BROADCAST;
andrewboyson 10:f0854784e960 279 }
andrewboyson 37:793b39683406 280 return ActionMakeFromDestAndTrace(dest, DhcpTrace);
andrewboyson 136:8a65abb0dc63 281 }