Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: oldheating gps motorhome heating
udp/dhcp.c
- Committer:
- andrewboyson
- Date:
- 2018-01-19
- Revision:
- 66:18a10c0b6d93
- Parent:
- 65:37acccf2752f
- Child:
- 83:08c983006a6e
File content as of revision 66:18a10c0b6d93:
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "log.h"
#include "clock.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 60
static uint32_t delayTime = 10000000; //Reset whenever a message is sent and blocks another send until count exceeds REPEAT_DELAY_TIME
uint32_t DhcpElapsedTime = 0; //Reset whenever an IP address request has been acknowledged
void DhcpMain()
{
if (ClockTicked)
{
DhcpElapsedTime++;
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];
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
delayTime = 0;
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;
DhcpElapsedTime = 0;
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 (delayTime < REPEAT_DELAY_TIME) return DO_NOTHING; //Don't retry within the delay time
if (DhcpLocalIp && DhcpElapsedTime < (DhcpLeaseTime >> 1)) return 0; //Do nothing if have address and within T1
*pSize = 0;
int dest = DO_NOTHING;
if (DhcpLocalIp && DhcpElapsedTime < DhcpLeaseTime)
{
*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);
}