friendev EtherDune TCP/IP library
/home/jander/temp/etherdune/DHCP.cpp
Go to the documentation of this file.
1 // EtherDune DHCP Service
2 // Author: Javier Peletier <jm@friendev.com>
3 // Summary: Implements the basics of DHCP so as to obtain and maintain an IP lease
4 // along with DNS, gateway IP and netmask.
5 //
6 // Copyright (c) 2015 All Rights Reserved, http://friendev.com
7 //
8 // This source is subject to the GPLv2 license.
9 // Please see the License.txt file for more information.
10 // All other rights reserved.
11 //
12 // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
13 // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14 // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
15 // PARTICULAR PURPOSE.
16 
17 #include "DHCP.h"
18 #include "inet.h"
19 #include "DNS.h"
20 
21 #define AC_LOGLEVEL 2
22 #include <ACLog.h>
23 ACROSS_MODULE("DHCP");
24 
26 {
27  remotePort = 67;
28  localPort = 68;
30 }
31 
32 void DHCP::prepareDHCPRequest()
33 {
34  memset(&packet.dhcp, 0, sizeof(packet.dhcp));
35  packet.dhcp.op = 1; //request
36  packet.dhcp.htype = 1; //hardware type=ethernet;
37  packet.dhcp.hlen = sizeof(MACAddress); //size of hardware address
38  packet.dhcp.xid.rawValue = *(uint32_t*)&localMAC; //transaction id, should be a random number. for now, use part of our MAC.
39  packet.dhcp.broadcastFlag = 0x0080;
41  setMagicCookie();
42 
43 }
44 
51 {
52  attempts = DHCP_MAX_ATTEMPTS;
53  initDHCP();
54 
55  while (attempts > 0 && state != DHCP_STATE_BOUND)
56  net::loop();
57 
58  return state == DHCP_STATE_BOUND;
59 }
60 
61 void DHCP::sendDHCPDiscover()
62 {
63  prepareDHCPRequest();
64  write(packet.dhcp);
65 
66  const DHCPDiscoverMessageTypeOption discover;
67 
68  write(discover);
69 
70  if (net::localIP.b[0]!= 0) //if the current IP address looks like it is valid, then try to request that one.
71  {
72  //DHCPRequestedIPOption requestIP;
73  //requestIP.ip = net::localIP;
74  //write(requestIP);
75 
76  //slower and uglier but saves 16bytes of flash.
77  const DHCPOption<DHCP_OPTIONS_REQUESTED_IP, sizeof(IPAddress)> r;
78  write(r);
80 
81  ACINFO("trying to request the same IP: %d.%d.%d.%d", net::localIP.b[0], net::localIP.b[1], net::localIP.b[2], net::localIP.b[3]);
82  }
83 
84  writeAdditionalFields();
85 
86 
88 
90 
91  send();
92 
93 }
94 
95 void DHCP::initDHCP()
96 {
97  attempts--;
98  sendDHCPDiscover();
99  setState(DHCP_STATE_SELECTING, DHCP_TIMEOUT_SELECTING);
100 }
101 
102 uint8_t DHCP::getMessageType()
103 {
104  DHCPMessageTypeOption<0>* mtoption = (DHCPMessageTypeOption<0>*)findOption(DHCP_OPTIONS_MESSAGETYPE);
105 
106  if (mtoption == NULL)
107  return 0;
108  else
109  return mtoption->messageType;
110 }
111 
112 void DHCP::onReceive(uint16_t len)
113 {
114  ACTRACE("DHCP response");
115 
116  if (packet.dhcp.xid.rawValue != *(uint32_t*)&localMAC) //we're using part of our mac address as "transaction id"
117  return;
118 
119  switch (state)
120  {
121 
123  {
124  if (getMessageType()!= DHCP_OFFER)
125  return;
126 
127  DHCPIPOption* sioptionPtr = (DHCPIPOption*)findOption(DHCP_OPTIONS_SERVER_IDENTIFIER);
128  if (sioptionPtr == NULL)
129  return;
130 
131  DHCPIPOption serverIDOption = *sioptionPtr;
132  DHCPRequestedIPOption requestedIPOption;
133  requestedIPOption.ip = packet.dhcp.yiaddr;
134 
135  prepareDHCPRequest();
136  write(packet.dhcp);
137 
138  const DHCPRequestMessageTypeOption request;
139  write(request);
140  write(serverIDOption);
141  write(requestedIPOption);
142  writeAdditionalFields();
144 
146 
147  send();
148  setState(DHCP_STATE_REQUESTING, DHCP_TIMEOUT_REQUESTING);
149 
150  }break;
151 
153  {
154  switch (getMessageType())
155  {
156  case DHCP_ACK:
157  {
158  DHCPIPOption* subnetOpt = (DHCPIPOption*)findOption(DHCP_OPTIONS_SUBNET);
159  DHCPIPOption* dnsOpt = (DHCPIPOption*)findOption(DHCP_OPTIONS_DNS);
160  DHCPIPOption* routerOpt = (DHCPIPOption*)findOption(DHCP_OPTIONS_ROUTER);
161  if (subnetOpt == NULL || dnsOpt==NULL || routerOpt== NULL)
162  return;
163 
164  DHCPTimerOption* timerOpt = (DHCPTimerOption*)findOption(DHCP_OPTIONS_RENEWAL_TIME);
165  if (timerOpt != NULL)
166  {
167  if (timerOpt->timer.h.rawValue != 0)
168  renewalTimer = 0xFFFF;
169  else
170  renewalTimer = timerOpt->timer.l;
171  }
172  else
173  renewalTimer = DHCP_DEFAULT_RENEWAL_TIMER;
174 
175  net::netmask = subnetOpt->ip;
176  net::dnsIP = dnsOpt->ip;
177  net::gatewayIP = routerOpt->ip;
178 
180  setState(DHCP_STATE_BOUND, DHCP_TIMEOUT_BOUND);
181 
182  ACINFO("IP:%d.%d.%d.%d", net::localIP.b[0], net::localIP.b[1], net::localIP.b[2], net::localIP.b[3]);
183  ACINFO("Subnet:%d.%d.%d.%d", net::netmask.b[0], net::netmask.b[1], net::netmask.b[2], net::netmask.b[3]);
184  ACINFO("DNS:%d.%d.%d.%d", net::DNS.serverIP().b[0], net::DNS.serverIP().b[1], net::DNS.serverIP().b[2], net::DNS.serverIP().b[3]);
185  ACINFO("Gateway:%d.%d.%d.%d", net::gatewayIP.b[0], net::gatewayIP.b[1], net::gatewayIP.b[2], net::gatewayIP.b[3]);
186 
187  }break;
188 
189  case DHCP_NACK:
190  {
191  initDHCP();//start over
192  }break;
193 
194  }
195 
196 
197  }break;
198  }
199 
200 
201 }
202 
203 void DHCP::setMagicCookie()
204 {
205  packet.dhcp.magicCookie = IPADDR_P(99, 130, 83, 99);
206 }
207 
208 DHCPOptionHeader* DHCP::findOption(uint8_t searchCode)
209 {
210 
211  DHCPOptionHeader* header = (DHCPOptionHeader*)&packet.dhcpOptions;
212  for (;
213  header->code != DHCP_OPTIONS_END &&
214  (uint8_t*)header < (uint8_t*)&packet.dhcpOptions + sizeof(packet.dhcpOptions);
215  header = (DHCPOptionHeader*)(((uint8_t*)header) + header->length+sizeof(DHCPOptionHeader)))
216  {
217  if (header->code == searchCode)
218  return header;
219 
220  }
221 
222  return ((searchCode==header->code) ? header : NULL);
223 
224 }
225 
226 void DHCP::tick()
227 {
228 
229  ACDEBUG("tick/state=%S", getStateString());
230 
231  if (stateTimer == 1) //handle timeouts
232  {
233  switch (state)
234  {
235 
236  case DHCP_STATE_BOUND:
237  {
238  stateTimer = DHCP_TIMEOUT_BOUND;
239  renewalTimer--;
240  if (renewalTimer > 0)
241  goto tick_end;
242 
243  //renewal timer ran out, fall back bellow to initDHCP();
244 
245  }
248  {
249  initDHCP();
250  goto tick_end;
251 
252  }break;
253  }
254  }
255 
256  if (stateTimer>0)
257  stateTimer--;
258 
259  switch (state)
260  {
262  {
263  sendDHCPDiscover(); //resend message every tick.
264  }break;
265 
266  }
267 
268  tick_end:
269  UDPSocket::tick();
270 }
271 
272 void DHCP::writeAdditionalFields()
273 {
274  const DHCPClientIdentifierOptionHeader clientIdentifierHeader;
275  write(clientIdentifierHeader);
277 
278 #if ENABLE_DHCP_HOSTNAME
279  DHCPOptionHeader hostnameOptionHeader(DHCP_OPTIONS_HOSTNAME, strlen_P(DHCP_HOSTNAME));
280  write(hostnameOptionHeader);
281  write((const __FlashStringHelper*)DHCP_HOSTNAME);
282 #endif
283 }
284 
285 
286 
287 __FlashStringHelper* DHCP::getStateString()
288 {
289  const char* s;
290 
291  switch (state)
292  {
293  case DHCP_STATE_BOUND: s = PSTR("BOUND"); break;
294  case DHCP_STATE_INIT: s = PSTR("INIT"); break;
295  case DHCP_STATE_REBINDING: s = PSTR("REBINDING"); break;
296  case DHCP_STATE_RENEWING: s = PSTR("RENEWING"); break;
297  case DHCP_STATE_REQUESTING: s = PSTR("REQUESTING"); break;
298  case DHCP_STATE_SELECTING: s = PSTR("SELECTING"); break;
299 
300  default:
301  s = PSTR("UNKNOWN");
302  }
303 
304  return (__FlashStringHelper*)s;
305 
306 }
307 
308 
static const uint8_t DHCP_OPTIONS_SUBNET
Definition: DHCP.h:53
Stores a MAC address in memory.
Definition: inet.h:252
static const uint8_t DHCP_STATE_REBINDING
The client has failed to renew its lease with the server that originally granted it, and now seeks a lease extension with any server that can hear it.
Definition: inet.h:568
static const char DHCP_HOSTNAME[]
host name sent to DHCP server.
Definition: config.h:203
nint16_t localPort
local TCP or UDP port
Definition: Socket.h:54
uint8_t htype
Hardware Type.
Definition: inet.h:468
MACAddress mac
The hardware (layer two) address of the client, which is used for identification and communication...
Definition: inet.h:480
static const uint8_t DHCP_STATE_BOUND
Client has a valid lease and is in its normal operating state.
Definition: inet.h:566
static const uint8_t DHCP_OPTIONS_HOSTNAME
Definition: DHCP.h:56
DHCPHeader dhcp
Definition: inet.h:519
static const uint8_t DHCP_MAX_ATTEMPTS
how many times to retry if address request is denied.
Definition: config.h:215
IPAddress yiaddr
Your IP Address.
Definition: inet.h:475
static DNSClient & DNS()
Obtains access to the DNS service singleton instance.
uint16_t write(uint16_t len, const byte *data)
In the case of TCP, writes the given data buffer to the socket.
Definition: Socket.cpp:54
static EthBuffer packet
in-memory packet buffer currently being processed.
void setState(uint8_t newState, uint8_t timeout)
Changes to a new state
Definition: Stateful.cpp:28
nint16_t remotePort
remote TCP or UDP port
Definition: Socket.h:53
uint32_t rawValue
provides low level access to the memory containing the network-order integer
Definition: inet.h:111
static const uint8_t DHCP_OPTIONS_RENEWAL_TIME
Definition: DHCP.h:60
DHCPOptions dhcpOptions
Definition: inet.h:520
void tick()
This is a timer function that is called every NETWORK_TIMER_RESOLUTION milliseconds on every service...
Definition: UDPSocket.cpp:99
bool send()
Sends the packet currently in the outgoing buffer
Definition: UDPSocket.cpp:45
uint8_t stateTimer
state timer, in ticks
Definition: Stateful.h:34
static const uint8_t DHCP_OPTIONS_END
Definition: DHCP.h:62
void setBroadcastIP()
Sets the IP to 255.255.255.255
Definition: inet.h:215
static IPAddress netmask
Subnet mask.
IPAddress magicCookie
Definition: inet.h:485
static IPAddress gatewayIP
IP address of the gateway in this network.
nint32_t xid
Transaction Identifier.
Definition: inet.h:471
static const uint8_t DHCP_OPTIONS_SERVER_IDENTIFIER
Definition: DHCP.h:59
#define IPADDR_P(b0, b1, b2, b3)
defines an IP address as stored in PROGMEM
Definition: inet.h:177
bool dhcpSetup()
Attempts to configure the IP settings: local IP, subnet mask, gateway and DNS via DHCP...
Definition: DHCP.cpp:50
DHCP()
Definition: DHCP.cpp:25
uint16_t broadcastFlag
Broadcast flag.
Definition: inet.h:473
uint8_t state
state code
Definition: Stateful.h:35
static const uint8_t DHCP_OFFER
Definition: DHCP.h:44
static IPAddress dnsIP
IP address of the DNS server to use.
static MACAddress localMAC
Ethernet MAC address.
uint8_t hlen
Hardware Address Length.
Definition: inet.h:469
static const uint8_t DHCP_STATE_REQUESTING
The client is waiting to hear back from the server to which it sent its request.
Definition: inet.h:565
Base data structures for Internet communication.
ACROSS_MODULE("DHCP")
static const uint8_t DHCP_ACK
Definition: DHCP.h:47
static const uint8_t DHCP_OPTIONS_ROUTER
Definition: DHCP.h:54
static void loop()
Gives processing time to EtherDune so that it can check for incoming packets or send queued packets...
static IPAddress localIP
IP address of this application.
IPAddress remoteIP
remote IP address to connect to (TCP) or send the next packet to (UDP)
Definition: Socket.h:55
uint8_t op
Operation Code.
Definition: inet.h:467
static const uint8_t DHCP_NACK
Definition: DHCP.h:48
static const uint8_t DHCP_OPTIONS_DNS
Definition: DHCP.h:55
static const uint8_t DHCP_OPTIONS_MESSAGETYPE
Definition: DHCP.h:58
static const uint8_t DHCP_STATE_INIT
This is the initialization state, where a client begins the process of acquiring a lease...
Definition: inet.h:563
static const uint8_t DHCP_STATE_RENEWING
Client is trying to renew its lease.
Definition: inet.h:567
static const uint8_t DHCP_STATE_SELECTING
The client is waiting to receive DHCPOFFER messages from one or more DHCP servers, so it can choose one.
Definition: inet.h:564