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:
Thu Oct 19 20:56:58 2017 +0000
Revision:
43:bc028d5a6424
Parent:
37:793b39683406
Child:
44:83ce5ace337b
Added verbose option to trace

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 37:793b39683406 1 #include "mbed.h"
andrewboyson 37:793b39683406 2 #include "log.h"
andrewboyson 43:bc028d5a6424 3 #include "clock.h"
andrewboyson 37:793b39683406 4 #include "net.h"
andrewboyson 37:793b39683406 5 #include "action.h"
andrewboyson 37:793b39683406 6 #include "eth.h"
andrewboyson 37:793b39683406 7 #include "ip4.h"
andrewboyson 37:793b39683406 8 #include "mac.h"
andrewboyson 37:793b39683406 9 #include "udp.h"
andrewboyson 10:f0854784e960 10
andrewboyson 37:793b39683406 11 bool DhcpTrace = false;
andrewboyson 10:f0854784e960 12
andrewboyson 10:f0854784e960 13 #define REQUEST 1
andrewboyson 10:f0854784e960 14 #define REPLY 2
andrewboyson 10:f0854784e960 15
andrewboyson 10:f0854784e960 16 #define DHCPDISCOVER 1
andrewboyson 10:f0854784e960 17 #define DHCPOFFER 2
andrewboyson 10:f0854784e960 18 #define DHCPREQUEST 3
andrewboyson 10:f0854784e960 19 #define DHCPDECLINE 4
andrewboyson 10:f0854784e960 20 #define DHCPACK 5
andrewboyson 10:f0854784e960 21 #define DHCPNAK 6
andrewboyson 10:f0854784e960 22 #define DHCPRELEASE 7
andrewboyson 10:f0854784e960 23 #define DHCPINFORM 8
andrewboyson 10:f0854784e960 24
andrewboyson 10:f0854784e960 25 #define ID 75648
andrewboyson 10:f0854784e960 26 #define COOKIE 0x63825363
andrewboyson 10:f0854784e960 27 #define HEADER_LENGTH 240
andrewboyson 10:f0854784e960 28
andrewboyson 10:f0854784e960 29 __packed struct header
andrewboyson 10:f0854784e960 30 {
andrewboyson 10:f0854784e960 31 uint8_t op;
andrewboyson 10:f0854784e960 32 uint8_t htype;
andrewboyson 10:f0854784e960 33 uint8_t hlen;
andrewboyson 10:f0854784e960 34 uint8_t hops;
andrewboyson 10:f0854784e960 35
andrewboyson 10:f0854784e960 36 uint32_t xid;
andrewboyson 10:f0854784e960 37
andrewboyson 10:f0854784e960 38 uint16_t secs;
andrewboyson 10:f0854784e960 39 uint16_t flags;
andrewboyson 10:f0854784e960 40
andrewboyson 10:f0854784e960 41 uint32_t ciaddr;
andrewboyson 10:f0854784e960 42 uint32_t yiaddr;
andrewboyson 10:f0854784e960 43 uint32_t siaddr;
andrewboyson 10:f0854784e960 44 uint32_t giaddr;
andrewboyson 10:f0854784e960 45
andrewboyson 10:f0854784e960 46 uint8_t chaddr[16];
andrewboyson 10:f0854784e960 47
andrewboyson 10:f0854784e960 48 char legacy[192];
andrewboyson 10:f0854784e960 49
andrewboyson 10:f0854784e960 50 uint32_t cookie;
andrewboyson 10:f0854784e960 51 };
andrewboyson 10:f0854784e960 52
andrewboyson 10:f0854784e960 53 #define REPEAT_DELAY_TIME 60
andrewboyson 10:f0854784e960 54 static uint32_t delayTime = 10000000; //Reset whenever a message is sent and blocks another send until count exceeds REPEAT_DELAY_TIME
andrewboyson 43:bc028d5a6424 55 uint32_t DhcpElapsedTime = 0; //Reset whenever an IP address request has been acknowledged
andrewboyson 43:bc028d5a6424 56 void DhcpMain()
andrewboyson 10:f0854784e960 57 {
andrewboyson 43:bc028d5a6424 58 if (ClockTicked)
andrewboyson 43:bc028d5a6424 59 {
andrewboyson 43:bc028d5a6424 60 DhcpElapsedTime++;
andrewboyson 43:bc028d5a6424 61 delayTime++;
andrewboyson 43:bc028d5a6424 62 }
andrewboyson 10:f0854784e960 63 }
andrewboyson 10:f0854784e960 64
andrewboyson 10:f0854784e960 65 static uint8_t dhcpMessageType = 0;
andrewboyson 10:f0854784e960 66 uint32_t DhcpLeaseTime = 0;
andrewboyson 10:f0854784e960 67 uint32_t DhcpServer = 0;
andrewboyson 10:f0854784e960 68 uint32_t DhcpRouter = 0;
andrewboyson 10:f0854784e960 69 uint32_t DhcpSubnetMask = 0;
andrewboyson 10:f0854784e960 70 uint32_t DhcpNtp = 0;
andrewboyson 10:f0854784e960 71 uint32_t DhcpRenewalT1 = 0;
andrewboyson 10:f0854784e960 72 uint32_t DhcpRenewalT2 = 0;
andrewboyson 10:f0854784e960 73 uint32_t DhcpBroadcastIp = 0;
andrewboyson 10:f0854784e960 74 uint32_t DhcpLocalIp = 0;
andrewboyson 10:f0854784e960 75 uint32_t DhcpDnsServer = 0;
andrewboyson 10:f0854784e960 76 char DhcpDomainName[20];
andrewboyson 10:f0854784e960 77 char DhcpHostName[20];
andrewboyson 10:f0854784e960 78 static uint32_t readOption32(char** pp)
andrewboyson 10:f0854784e960 79 {
andrewboyson 10:f0854784e960 80 uint32_t value = 0;
andrewboyson 10:f0854784e960 81 char* p = *pp;
andrewboyson 10:f0854784e960 82 int len = *++p;
andrewboyson 10:f0854784e960 83 if (len >= 4)
andrewboyson 10:f0854784e960 84 {
andrewboyson 10:f0854784e960 85 value = *++p << 24;
andrewboyson 10:f0854784e960 86 value |= *++p << 16;
andrewboyson 10:f0854784e960 87 value |= *++p << 8;
andrewboyson 10:f0854784e960 88 value |= *++p << 0;
andrewboyson 10:f0854784e960 89 }
andrewboyson 10:f0854784e960 90 *pp += len + 1;
andrewboyson 10:f0854784e960 91 return value;
andrewboyson 10:f0854784e960 92 }
andrewboyson 10:f0854784e960 93 static uint32_t readIp(char** pp)
andrewboyson 10:f0854784e960 94 {
andrewboyson 10:f0854784e960 95 uint32_t value = 0;
andrewboyson 10:f0854784e960 96 char* p = *pp;
andrewboyson 10:f0854784e960 97 int len = *++p;
andrewboyson 10:f0854784e960 98 if (len >= 4)
andrewboyson 10:f0854784e960 99 {
andrewboyson 10:f0854784e960 100 value = *++p << 0;
andrewboyson 10:f0854784e960 101 value |= *++p << 8;
andrewboyson 10:f0854784e960 102 value |= *++p << 16;
andrewboyson 10:f0854784e960 103 value |= *++p << 24;
andrewboyson 10:f0854784e960 104 }
andrewboyson 10:f0854784e960 105 *pp += len + 1;
andrewboyson 10:f0854784e960 106 return value;
andrewboyson 10:f0854784e960 107 }
andrewboyson 10:f0854784e960 108 static void readString(char** pp, char* pText)
andrewboyson 10:f0854784e960 109 {
andrewboyson 10:f0854784e960 110 char* p = *pp;
andrewboyson 10:f0854784e960 111 int len = *++p;
andrewboyson 10:f0854784e960 112 for (int i = 0; i < len; i++) pText[i] = *++p;
andrewboyson 10:f0854784e960 113 *pp += len + 1;
andrewboyson 10:f0854784e960 114 }
andrewboyson 10:f0854784e960 115 static void readOptions(int size, char * pOptions)
andrewboyson 10:f0854784e960 116 {
andrewboyson 10:f0854784e960 117 int len = 0;
andrewboyson 10:f0854784e960 118 char* p = pOptions;
andrewboyson 10:f0854784e960 119 char* pE = pOptions + size;
andrewboyson 10:f0854784e960 120 while( p < pE)
andrewboyson 10:f0854784e960 121 {
andrewboyson 10:f0854784e960 122 switch (*p)
andrewboyson 10:f0854784e960 123 {
andrewboyson 10:f0854784e960 124 case 0: break; //NOP
andrewboyson 10:f0854784e960 125 case 255: return; //End of options
andrewboyson 10:f0854784e960 126 case 1: DhcpSubnetMask = readIp(&p); break; //Subnet Mask
andrewboyson 10:f0854784e960 127 case 3: DhcpRouter = readIp(&p); break; //Router
andrewboyson 10:f0854784e960 128 case 6: DhcpDnsServer = readIp(&p); break; //DNS server
andrewboyson 10:f0854784e960 129 case 12: readString(&p, DhcpHostName); break; //Host name
andrewboyson 10:f0854784e960 130 case 15: readString(&p, DhcpDomainName); break; //Domain name
andrewboyson 10:f0854784e960 131 case 19: len = *++p; p+= len; break; //IP forwarding yes/no
andrewboyson 10:f0854784e960 132 case 28: DhcpBroadcastIp = readIp(&p); break; //Broadcast IP
andrewboyson 10:f0854784e960 133 case 42: DhcpNtp = readIp(&p); break; //NTP
andrewboyson 10:f0854784e960 134 case 44: len = *++p; p+= len; break; //NetBIOS name server
andrewboyson 10:f0854784e960 135 case 45: len = *++p; p+= len; break; //NetBIOS datagram server
andrewboyson 10:f0854784e960 136 case 46: len = *++p; p+= len; break; //NetBIOS node type
andrewboyson 10:f0854784e960 137 case 47: len = *++p; p+= len; break; //NetBIOS scope
andrewboyson 10:f0854784e960 138 case 53: len = *++p; dhcpMessageType = *++p; break; //DHCP message type
andrewboyson 10:f0854784e960 139 case 51: DhcpLeaseTime = readOption32(&p); break; //Address lease time
andrewboyson 10:f0854784e960 140 case 54: DhcpServer = readIp(&p); break; //DHCP server
andrewboyson 10:f0854784e960 141 case 58: DhcpRenewalT1 = readOption32(&p); break; //T1
andrewboyson 10:f0854784e960 142 case 59: DhcpRenewalT2 = readOption32(&p); break; //T2
andrewboyson 10:f0854784e960 143 default:
andrewboyson 37:793b39683406 144 if (DhcpTrace) LogTimeF("Ignoring option %d\r\n", *p);
andrewboyson 10:f0854784e960 145 len = *++p;
andrewboyson 10:f0854784e960 146 p += len;
andrewboyson 10:f0854784e960 147 return;
andrewboyson 10:f0854784e960 148 }
andrewboyson 10:f0854784e960 149 p++;
andrewboyson 10:f0854784e960 150 }
andrewboyson 10:f0854784e960 151 }
andrewboyson 10:f0854784e960 152 static void writeIp(uint8_t code, uint32_t value, char** pp)
andrewboyson 10:f0854784e960 153 {
andrewboyson 10:f0854784e960 154 if (!value) return;
andrewboyson 10:f0854784e960 155
andrewboyson 10:f0854784e960 156 char* p = *pp;
andrewboyson 10:f0854784e960 157
andrewboyson 10:f0854784e960 158 *p++ = code;
andrewboyson 10:f0854784e960 159 *p++ = 4;
andrewboyson 10:f0854784e960 160 *p++ = (value & 0x000000FF) >> 0;
andrewboyson 10:f0854784e960 161 *p++ = (value & 0x0000FF00) >> 8;
andrewboyson 10:f0854784e960 162 *p++ = (value & 0x00FF0000) >> 16;
andrewboyson 10:f0854784e960 163 *p++ = (value & 0xFF000000) >> 24;
andrewboyson 10:f0854784e960 164
andrewboyson 10:f0854784e960 165 *pp += 6;
andrewboyson 10:f0854784e960 166 }
andrewboyson 10:f0854784e960 167 int sendRequest(void* pPacket, uint8_t code, uint32_t srvIp, uint32_t reqIp)
andrewboyson 10:f0854784e960 168 {
andrewboyson 10:f0854784e960 169
andrewboyson 10:f0854784e960 170 switch (code)
andrewboyson 10:f0854784e960 171 {
andrewboyson 10:f0854784e960 172 case DHCPDISCOVER:
andrewboyson 37:793b39683406 173 if (DhcpTrace) LogTimeF("DHCP -> discover");
andrewboyson 10:f0854784e960 174 break;
andrewboyson 10:f0854784e960 175 case DHCPREQUEST:
andrewboyson 37:793b39683406 176 if (DhcpTrace) LogTimeF("DHCP -> request");
andrewboyson 10:f0854784e960 177 break;
andrewboyson 10:f0854784e960 178 default:
andrewboyson 10:f0854784e960 179 LogTimeF("DHCP -> unknown message %d", code);
andrewboyson 10:f0854784e960 180 break;
andrewboyson 10:f0854784e960 181 }
andrewboyson 37:793b39683406 182 if (DhcpTrace)
andrewboyson 10:f0854784e960 183 {
andrewboyson 10:f0854784e960 184 char text[20];
andrewboyson 14:e75a59c1123d 185 Ip4AddressToString(srvIp, sizeof(text), text);
andrewboyson 10:f0854784e960 186 LogF(" server=%s", text);
andrewboyson 14:e75a59c1123d 187 Ip4AddressToString(reqIp, sizeof(text), text);
andrewboyson 10:f0854784e960 188 LogF(" request=%s", text);
andrewboyson 14:e75a59c1123d 189 Ip4AddressToString(DhcpLocalIp, sizeof(text), text);
andrewboyson 10:f0854784e960 190 LogF(" local=%s\r\n", text);
andrewboyson 10:f0854784e960 191 }
andrewboyson 10:f0854784e960 192
andrewboyson 10:f0854784e960 193 bool broadcast = DhcpLocalIp == 0;
andrewboyson 10:f0854784e960 194 uint16_t flags = 0;
andrewboyson 10:f0854784e960 195 if (broadcast) flags |= 0x0080; //0x8000 on the net == 0x0080 in little endian
andrewboyson 10:f0854784e960 196 struct header* pHeader = (header*)pPacket;
andrewboyson 10:f0854784e960 197 pHeader->op = REQUEST;
andrewboyson 10:f0854784e960 198 pHeader->htype = ETHERNET;
andrewboyson 10:f0854784e960 199 pHeader->hlen = 6;
andrewboyson 10:f0854784e960 200 pHeader->hops = 0;
andrewboyson 10:f0854784e960 201 pHeader->xid = ID; //Randomly chosed transaction id used to associate messages to responses
andrewboyson 10:f0854784e960 202 pHeader->secs = 0; //Seconds since started to boot
andrewboyson 10:f0854784e960 203 pHeader->flags = flags; //Broadcast (1) Unicast (0)
andrewboyson 10:f0854784e960 204 pHeader->ciaddr = DhcpLocalIp; //'Client' address set by client or 0 if don't know address
andrewboyson 10:f0854784e960 205 pHeader->yiaddr = 0; //'Your' address returned by server
andrewboyson 10:f0854784e960 206 pHeader->siaddr = srvIp; //'Server' address to use if required
andrewboyson 10:f0854784e960 207 pHeader->giaddr = 0; //'Gateway' address
andrewboyson 13:9cd54f7db57a 208 memcpy(pHeader->chaddr, MacLocal, 6); //'Client hardware' address. 6 bytes for ethernet
andrewboyson 10:f0854784e960 209 memset(pHeader->legacy, 0, 192); //BootP legacy fill with zeros
andrewboyson 10:f0854784e960 210 pHeader->cookie = NetToHost32(COOKIE); //Magic cookie
andrewboyson 10:f0854784e960 211
andrewboyson 10:f0854784e960 212 char* pOptions = (char*)pPacket + HEADER_LENGTH;
andrewboyson 10:f0854784e960 213 char* p = pOptions;
andrewboyson 10:f0854784e960 214 *p++ = 53; //Message code
andrewboyson 10:f0854784e960 215 *p++ = 1;
andrewboyson 10:f0854784e960 216 *p++ = code;
andrewboyson 10:f0854784e960 217
andrewboyson 10:f0854784e960 218 writeIp(50, reqIp, &p); //Requested IP
andrewboyson 10:f0854784e960 219 writeIp(54, srvIp, &p); //Server ip
andrewboyson 10:f0854784e960 220
andrewboyson 10:f0854784e960 221 *p++ = 255; //End of options
andrewboyson 10:f0854784e960 222
andrewboyson 10:f0854784e960 223 delayTime = 0;
andrewboyson 10:f0854784e960 224 return HEADER_LENGTH + p - pOptions;
andrewboyson 10:f0854784e960 225 }
andrewboyson 37:793b39683406 226 int DhcpHandleResponse(void (*traceback)(void), int* pSize, void* pPacket)
andrewboyson 10:f0854784e960 227 {
andrewboyson 10:f0854784e960 228 struct header* pHeader = (header*)pPacket;
andrewboyson 10:f0854784e960 229
andrewboyson 10:f0854784e960 230 uint8_t op = pHeader->op;
andrewboyson 10:f0854784e960 231 uint8_t htype = pHeader->htype;
andrewboyson 10:f0854784e960 232 uint8_t hlen = pHeader->hlen;
andrewboyson 10:f0854784e960 233
andrewboyson 36:900e24b27bfb 234 uint32_t xid = pHeader->xid; //Randomly chosen transaction id used to associate messages to responses
andrewboyson 10:f0854784e960 235
andrewboyson 10:f0854784e960 236 uint16_t secs = NetToHost16(pHeader->secs);
andrewboyson 10:f0854784e960 237 uint16_t flags = NetToHost16(pHeader->flags);
andrewboyson 10:f0854784e960 238
andrewboyson 10:f0854784e960 239 uint32_t yiaddr = pHeader->yiaddr;
andrewboyson 10:f0854784e960 240 uint32_t siaddr = pHeader->siaddr;
andrewboyson 10:f0854784e960 241 uint32_t cookie = NetToHost32(pHeader->cookie);
andrewboyson 10:f0854784e960 242
andrewboyson 28:edc17eeb4142 243 if (op != REPLY) return DO_NOTHING;
andrewboyson 28:edc17eeb4142 244 if (htype != ETHERNET) return DO_NOTHING;
andrewboyson 28:edc17eeb4142 245 if (hlen != 6) return DO_NOTHING;
andrewboyson 28:edc17eeb4142 246 if (memcmp(pHeader->chaddr, MacLocal, 6)) return DO_NOTHING;
andrewboyson 28:edc17eeb4142 247 if (xid != ID) return DO_NOTHING;
andrewboyson 28:edc17eeb4142 248 if (cookie != COOKIE) return DO_NOTHING;
andrewboyson 10:f0854784e960 249
andrewboyson 10:f0854784e960 250 char* pOptions = (char*)pPacket + HEADER_LENGTH;
andrewboyson 10:f0854784e960 251 readOptions(*pSize - HEADER_LENGTH, pOptions);
andrewboyson 10:f0854784e960 252
andrewboyson 10:f0854784e960 253 char text[30];
andrewboyson 10:f0854784e960 254 switch (dhcpMessageType)
andrewboyson 10:f0854784e960 255 {
andrewboyson 10:f0854784e960 256 case DHCPOFFER:
andrewboyson 14:e75a59c1123d 257 Ip4AddressToString(yiaddr, sizeof(text), text);
andrewboyson 37:793b39683406 258 if (DhcpTrace) LogTimeF("DHCP <- offer ip %s\r\n", text);
andrewboyson 10:f0854784e960 259 *pSize =sendRequest(pPacket, DHCPREQUEST, siaddr, yiaddr);
andrewboyson 10:f0854784e960 260 return BROADCAST;
andrewboyson 10:f0854784e960 261 case DHCPACK:
andrewboyson 14:e75a59c1123d 262 Ip4AddressToString(yiaddr, sizeof(text), text);
andrewboyson 37:793b39683406 263 if (DhcpTrace) LogTimeF("DHCP <- ack ip %s\r\n", text);
andrewboyson 10:f0854784e960 264 DhcpLocalIp = yiaddr;
andrewboyson 29:39277bf2003d 265 DhcpElapsedTime = 0;
andrewboyson 10:f0854784e960 266 break;
andrewboyson 10:f0854784e960 267 case DHCPNAK:
andrewboyson 14:e75a59c1123d 268 Ip4AddressToString(yiaddr, sizeof(text), text);
andrewboyson 37:793b39683406 269 if (DhcpTrace) LogTimeF("DHCP <- nack ip %s\r\n", text);
andrewboyson 10:f0854784e960 270 break;
andrewboyson 10:f0854784e960 271 default:
andrewboyson 10:f0854784e960 272 LogTimeF("DHCP <- unknown message %d\r\n", dhcpMessageType);
andrewboyson 10:f0854784e960 273 break;
andrewboyson 10:f0854784e960 274 }
andrewboyson 10:f0854784e960 275 return DO_NOTHING;
andrewboyson 10:f0854784e960 276 }
andrewboyson 10:f0854784e960 277
andrewboyson 10:f0854784e960 278 int DhcpPollForRequestToSend(void* pPacket, int* pSize)
andrewboyson 10:f0854784e960 279 {
andrewboyson 10:f0854784e960 280 if (delayTime < REPEAT_DELAY_TIME) return DO_NOTHING; //Don't retry within the delay time
andrewboyson 10:f0854784e960 281
andrewboyson 29:39277bf2003d 282 if (DhcpLocalIp && DhcpElapsedTime < (DhcpLeaseTime >> 1)) return 0; //Do nothing if have address and within T1
andrewboyson 10:f0854784e960 283
andrewboyson 10:f0854784e960 284 *pSize = 0;
andrewboyson 37:793b39683406 285 int dest = DO_NOTHING;
andrewboyson 29:39277bf2003d 286 if (DhcpLocalIp && DhcpElapsedTime < DhcpLeaseTime)
andrewboyson 10:f0854784e960 287 {
andrewboyson 10:f0854784e960 288 *pSize = sendRequest(pPacket, DHCPREQUEST, DhcpServer, DhcpLocalIp); //if within T2 then send request to the server - not broadcast
andrewboyson 37:793b39683406 289 dest = UNICAST_DHCP;
andrewboyson 10:f0854784e960 290 }
andrewboyson 10:f0854784e960 291 else
andrewboyson 10:f0854784e960 292 {
andrewboyson 37:793b39683406 293 if (DhcpTrace) LogTimeF("DHCP lease has expired\r\n");
andrewboyson 10:f0854784e960 294 DhcpLocalIp = 0;
andrewboyson 10:f0854784e960 295 DhcpServer = 0;
andrewboyson 10:f0854784e960 296 *pSize = sendRequest(pPacket, DHCPDISCOVER, 0, 0); //If outside T2 then start from scratch to do a full DHCP
andrewboyson 37:793b39683406 297 dest = BROADCAST;
andrewboyson 10:f0854784e960 298 }
andrewboyson 37:793b39683406 299 return ActionMakeFromDestAndTrace(dest, DhcpTrace);
andrewboyson 10:f0854784e960 300 }