agileRTOS (zrtos)  Version 0.8.0 (ghostbuster)
dhcp.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024 ykat UG (haftungsbeschraenkt) - All Rights Reserved
3  *
4  * Permission for non-commercial use is hereby granted,
5  * free of charge, without warranty of any kind.
6  */
7 #ifndef ZRTOS_VFS_MODULE_DHCP_H
8 #define ZRTOS_VFS_MODULE_DHCP_H
9 #ifdef __cplusplus
10 extern "C" {
11 #endif
12 
13 #include <zrtos/types.h>
14 
15 #define ZRTOS_VFS_MODULE_DHCP__FLAGSBROADCAST 0x8000
16 
17 /* UDP port numbers for DHCP */
18 #define ZRTOS_VFS_MODULE_DHCP__SERVER_PORT 67 /* from server to client */
19 #define ZRTOS_VFS_MODULE_DHCP__CLIENT_PORT 68 /* from client to server */
20 
21 #define ZRTOS_VFS_MODULE_DHCP__HTYPE10MB 1
22 #define ZRTOS_VFS_MODULE_DHCP__HTYPE100MB 2
23 
24 #define ZRTOS_VFS_MODULE_DHCP__HLENETHERNET 6
25 #define ZRTOS_VFS_MODULE_DHCP__HOPS 0
26 #define ZRTOS_VFS_MODULE_DHCP__SECS 0
27 
28 #define ZRTOS_VFS_MODULE_DHCP__MAGIC_COOKIE 0x63825363
29 #define ZRTOS_VFS_MODULE_DHCP__MAX_DHCP_OPT 16
30 
31 #define ZRTOS_VFS_MODULE_DHCP__HOST_NAME "WIZnet"
32 #define ZRTOS_VFS_MODULE_DHCP__DEFAULT_LEASE (900) //default lease time in seconds
33 
34 //DHCP state machine
35 typedef enum{
43 
44 /* DHCP message OP code */
45 typedef enum{
49 
50 /* DHCP message type */
51 typedef enum{
61 
62 typedef enum{
69 /*
70 #define DHCP_CHECK_NONE (0)
71 #define DHCP_CHECK_RENEW_FAIL (1)
72 #define DHCP_CHECK_RENEW_OK (2)
73 #define DHCP_CHECK_REBIND_FAIL (3)
74 #define DHCP_CHECK_REBIND_OK (4)
75 */
76 typedef enum{
82  ,ZRTOS_VFS_MODULE_DHCP_OPTION__nameServer = 5,*/
84  ,ZRTOS_VFS_MODULE_DHCP_OPTION__/*logServer = 7
85  ,ZRTOS_VFS_MODULE_DHCP_OPTION__cookieServer = 8
86  ,ZRTOS_VFS_MODULE_DHCP_OPTION__lprServer = 9
87  ,ZRTOS_VFS_MODULE_DHCP_OPTION__impressServer = 10
88  ,ZRTOS_VFS_MODULE_DHCP_OPTION__resourceLocationServer = 11,*/
90  ,ZRTOS_VFS_MODULE_DHCP_OPTION__/*bootFileSize = 13
91  ,ZRTOS_VFS_MODULE_DHCP_OPTION__meritDumpFile = 14,*/
93  ,ZRTOS_VFS_MODULE_DHCP_OPTION__/*swapServer = 16
94  ,ZRTOS_VFS_MODULE_DHCP_OPTION__rootPath = 17
95  ,ZRTOS_VFS_MODULE_DHCP_OPTION__extentionsPath = 18
96  ,ZRTOS_VFS_MODULE_DHCP_OPTION__IPforwarding = 19
97  ,ZRTOS_VFS_MODULE_DHCP_OPTION__nonLocalSourceRouting = 20
98  ,ZRTOS_VFS_MODULE_DHCP_OPTION__policyFilter = 21
99  ,ZRTOS_VFS_MODULE_DHCP_OPTION__maxDgramReasmSize = 22
100  ,ZRTOS_VFS_MODULE_DHCP_OPTION__defaultIPTTL = 23
101  ,ZRTOS_VFS_MODULE_DHCP_OPTION__pathMTUagingTimeout = 24
102  ,ZRTOS_VFS_MODULE_DHCP_OPTION__pathMTUplateauTable = 25
103  ,ZRTOS_VFS_MODULE_DHCP_OPTION__ifMTU = 26
104  ,ZRTOS_VFS_MODULE_DHCP_OPTION__allSubnetsLocal = 27
105  ,ZRTOS_VFS_MODULE_DHCP_OPTION__broadcastAddr = 28
106  ,ZRTOS_VFS_MODULE_DHCP_OPTION__performMaskDiscovery = 29
107  ,ZRTOS_VFS_MODULE_DHCP_OPTION__maskSupplier = 30
108  ,ZRTOS_VFS_MODULE_DHCP_OPTION__performRouterDiscovery = 31
109  ,ZRTOS_VFS_MODULE_DHCP_OPTION__routerSolicitationAddr = 32
110  ,ZRTOS_VFS_MODULE_DHCP_OPTION__staticRoute = 33
111  ,ZRTOS_VFS_MODULE_DHCP_OPTION__trailerEncapsulation = 34
112  ,ZRTOS_VFS_MODULE_DHCP_OPTION__arpCacheTimeout = 35
113  ,ZRTOS_VFS_MODULE_DHCP_OPTION__ethernetEncapsulation = 36
114  ,ZRTOS_VFS_MODULE_DHCP_OPTION__tcpDefaultTTL = 37
115  ,ZRTOS_VFS_MODULE_DHCP_OPTION__tcpKeepaliveInterval = 38
116  ,ZRTOS_VFS_MODULE_DHCP_OPTION__tcpKeepaliveGarbage = 39
117  ,ZRTOS_VFS_MODULE_DHCP_OPTION__nisDomainName = 40
118  ,ZRTOS_VFS_MODULE_DHCP_OPTION__nisServers = 41
119  ,ZRTOS_VFS_MODULE_DHCP_OPTION__ntpServers = 42
120  ,ZRTOS_VFS_MODULE_DHCP_OPTION__vendorSpecificInfo = 43
121  ,ZRTOS_VFS_MODULE_DHCP_OPTION__netBIOSnameServer = 44
122  ,ZRTOS_VFS_MODULE_DHCP_OPTION__netBIOSdgramDistServer = 45
123  ,ZRTOS_VFS_MODULE_DHCP_OPTION__netBIOSnodeType = 46
124  ,ZRTOS_VFS_MODULE_DHCP_OPTION__netBIOSscope = 47
125  ,ZRTOS_VFS_MODULE_DHCP_OPTION__xFontServer = 48
126  ,ZRTOS_VFS_MODULE_DHCP_OPTION__xDisplayManager = 49,*/
129  ,ZRTOS_VFS_MODULE_DHCP_OPTION__/*dhcpOptionOverload = 52,*/
133  ,ZRTOS_VFS_MODULE_DHCP_OPTION__/*dhcpMsg = 56
134  ,ZRTOS_VFS_MODULE_DHCP_OPTION__dhcpMaxMsgSize = 57,*/
137  ,ZRTOS_VFS_MODULE_DHCP_OPTION__/*dhcpClassIdentifier = 60,*/
141 
142 typedef struct /*_RIP_MSG_FIXED*/{
147  uint32_t xid;
148  uint16_t secs;
149  uint16_t flags;
150  uint8_t ciaddr[4];
151  uint8_t yiaddr[4];
152  uint8_t siaddr[4];
153  uint8_t giaddr[4];
154  uint8_t chaddr[6];
156 
157 typedef struct{
158  zrtos_vfs_module_network_ip_t ip;
160  zrtos_vfs_module_network_ip_t gateway;
161  zrtos_vfs_module_network_ip_t dns_server;
164 
165 int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout)
166 {
167  _dhcpLeaseTime=0;
168  _dhcpT1=0;
169  _dhcpT2=0;
170  _lastCheck=0;
171  _timeout = timeout;
172  _responseTimeout = responseTimeout;
173 
174  // zero out _dhcpMacAddr
175  memset(_dhcpMacAddr, 0, 6);
176  reset_DHCP_lease();
177 
178  memcpy((void*)_dhcpMacAddr, (void*)mac, 6);
179  _dhcp_state = STATE_DHCP_START;
180  return request_DHCP_lease();
181 }
182 
183 void DhcpClass::reset_DHCP_lease(){
184  // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp
185  memset(_dhcpLocalIp, 0, 20);
186 }
187 
188 //return:0 on error, 1 if request is sent and response is received
189 int DhcpClass::request_DHCP_lease(){
190  uint8_t messageType = 0;
191 
192  // Pick an initial transaction ID
193  _dhcpTransactionId = random(1UL, 2000UL);
194  _dhcpInitialTransactionId = _dhcpTransactionId;
195 
196  _dhcpUdpSocket.stop();
197  if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0)
198  {
199  // Couldn't get a socket
200  return 0;
201  }
202 
203  presend_DHCP();
204 
205  int result = 0;
206 
207  unsigned long startTime = millis();
208 
209  while(_dhcp_state != STATE_DHCP_LEASED)
210  {
211  if(_dhcp_state == STATE_DHCP_START)
212  {
213  _dhcpTransactionId++;
214 
215  send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000));
216  _dhcp_state = STATE_DHCP_DISCOVER;
217  }
218  else if(_dhcp_state == STATE_DHCP_REREQUEST){
219  _dhcpTransactionId++;
220  send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000));
221  _dhcp_state = STATE_DHCP_REQUEST;
222  }
223  else if(_dhcp_state == STATE_DHCP_DISCOVER)
224  {
225  uint32_t respId;
226  messageType = parseDHCPResponse(_responseTimeout, respId);
227  if(messageType == DHCP_OFFER)
228  {
229  // We'll use the transaction ID that the offer came with,
230  // rather than the one we were up to
231  _dhcpTransactionId = respId;
232  send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000));
233  _dhcp_state = STATE_DHCP_REQUEST;
234  }
235  }
236  else if(_dhcp_state == STATE_DHCP_REQUEST)
237  {
238  uint32_t respId;
239  messageType = parseDHCPResponse(_responseTimeout, respId);
240  if(messageType == DHCP_ACK)
241  {
242  _dhcp_state = STATE_DHCP_LEASED;
243  result = 1;
244  //use default lease time if we didn't get it
245  if(_dhcpLeaseTime == 0){
246  _dhcpLeaseTime = DEFAULT_LEASE;
247  }
248  //calculate T1 & T2 if we didn't get it
249  if(_dhcpT1 == 0){
250  //T1 should be 50% of _dhcpLeaseTime
251  _dhcpT1 = _dhcpLeaseTime >> 1;
252  }
253  if(_dhcpT2 == 0){
254  //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime
255  _dhcpT2 = _dhcpT1 << 1;
256  }
257  _renewInSec = _dhcpT1;
258  _rebindInSec = _dhcpT2;
259  }
260  else if(messageType == DHCP_NAK)
261  _dhcp_state = STATE_DHCP_START;
262  }
263 
264  if(messageType == 255)
265  {
266  messageType = 0;
267  _dhcp_state = STATE_DHCP_START;
268  }
269 
270  if(result != 1 && ((millis() - startTime) > _timeout))
271  break;
272  }
273 
274  // We're done with the socket now
275  _dhcpUdpSocket.stop();
276  _dhcpTransactionId++;
277 
278  return result;
279 }
280 
281 void DhcpClass::presend_DHCP()
282 {
283 }
284 
285 void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed)
286 {
287  uint8_t buffer[32];
288  memset(buffer, 0, 32);
289  IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address
290 
291  if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT))
292  {
293  // FIXME Need to return errors
294  return;
295  }
296 
297  buffer[0] = DHCP_BOOTREQUEST; // op
298  buffer[1] = DHCP_HTYPE10MB; // htype
299  buffer[2] = DHCP_HLENETHERNET; // hlen
300  buffer[3] = DHCP_HOPS; // hops
301 
302  // xid
303  unsigned long xid = htonl(_dhcpTransactionId);
304  memcpy(buffer + 4, &(xid), 4);
305 
306  // 8, 9 - seconds elapsed
307  buffer[8] = ((secondsElapsed & 0xff00) >> 8);
308  buffer[9] = (secondsElapsed & 0x00ff);
309 
310  // flags
311  unsigned short flags = htons(DHCP_FLAGSBROADCAST);
312  memcpy(buffer + 10, &(flags), 2);
313 
314  // ciaddr: already zeroed
315  // yiaddr: already zeroed
316  // siaddr: already zeroed
317  // giaddr: already zeroed
318 
319  //put data in W5100 transmit buffer
320  _dhcpUdpSocket.write(buffer, 28);
321 
322  memset(buffer, 0, 32); // clear local buffer
323 
324  memcpy(buffer, _dhcpMacAddr, 6); // chaddr
325 
326  //put data in W5100 transmit buffer
327  _dhcpUdpSocket.write(buffer, 16);
328 
329  memset(buffer, 0, 32); // clear local buffer
330 
331  // leave zeroed out for sname && file
332  // put in W5100 transmit buffer x 6 (192 bytes)
333 
334  for(int i = 0; i < 6; i++) {
335  _dhcpUdpSocket.write(buffer, 32);
336  }
337 
338  // OPT - Magic Cookie
339  buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF);
340  buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF);
341  buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF);
342  buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF);
343 
344  // OPT - message type
345  buffer[4] = dhcpMessageType;
346  buffer[5] = 0x01;
347  buffer[6] = messageType; //DHCP_REQUEST;
348 
349  // OPT - client identifier
350  buffer[7] = dhcpClientIdentifier;
351  buffer[8] = 0x07;
352  buffer[9] = 0x01;
353  memcpy(buffer + 10, _dhcpMacAddr, 6);
354 
355  // OPT - host name
356  buffer[16] = hostName;
357  buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address
358  strcpy((char*)&(buffer[18]), HOST_NAME);
359 
360  printByte((char*)&(buffer[24]), _dhcpMacAddr[3]);
361  printByte((char*)&(buffer[26]), _dhcpMacAddr[4]);
362  printByte((char*)&(buffer[28]), _dhcpMacAddr[5]);
363 
364  //put data in W5100 transmit buffer
365  _dhcpUdpSocket.write(buffer, 30);
366 
367  if(messageType == DHCP_REQUEST)
368  {
369  buffer[0] = dhcpRequestedIPaddr;
370  buffer[1] = 0x04;
371  buffer[2] = _dhcpLocalIp[0];
372  buffer[3] = _dhcpLocalIp[1];
373  buffer[4] = _dhcpLocalIp[2];
374  buffer[5] = _dhcpLocalIp[3];
375 
376  buffer[6] = dhcpServerIdentifier;
377  buffer[7] = 0x04;
378  buffer[8] = _dhcpDhcpServerIp[0];
379  buffer[9] = _dhcpDhcpServerIp[1];
380  buffer[10] = _dhcpDhcpServerIp[2];
381  buffer[11] = _dhcpDhcpServerIp[3];
382 
383  //put data in W5100 transmit buffer
384  _dhcpUdpSocket.write(buffer, 12);
385  }
386 
387  buffer[0] = dhcpParamRequest;
388  buffer[1] = 0x06;
389  buffer[2] = subnetMask;
390  buffer[3] = routersOnSubnet;
391  buffer[4] = dns;
392  buffer[5] = domainName;
393  buffer[6] = dhcpT1value;
394  buffer[7] = dhcpT2value;
395  buffer[8] = endOption;
396 
397  //put data in W5100 transmit buffer
398  _dhcpUdpSocket.write(buffer, 9);
399 
400  _dhcpUdpSocket.endPacket();
401 }
402 
403 uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId)
404 {
405  uint8_t type = 0;
406  uint8_t opt_len = 0;
407 
408  unsigned long startTime = millis();
409 
410  while(_dhcpUdpSocket.parsePacket() <= 0)
411  {
412  if((millis() - startTime) > responseTimeout)
413  {
414  return 255;
415  }
416  delay(50);
417  }
418  // start reading in the packet
419  RIP_MSG_FIXED fixedMsg;
420  _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED));
421 
422  if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT)
423  {
424  transactionId = ntohl(fixedMsg.xid);
425  if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId))
426  {
427  // Need to read the rest of the packet here regardless
428  _dhcpUdpSocket.flush();
429  return 0;
430  }
431 
432  memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4);
433 
434  // Skip to the option part
435  // Doing this a byte at a time so we don't have to put a big buffer
436  // on the stack (as we don't have lots of memory lying around)
437  for (int i =0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++)
438  {
439  _dhcpUdpSocket.read(); // we don't care about the returned byte
440  }
441 
442  while (_dhcpUdpSocket.available() > 0)
443  {
444  switch (_dhcpUdpSocket.read())
445  {
446  case endOption :
447  break;
448 
449  case padOption :
450  break;
451 
452  case dhcpMessageType :
453  opt_len = _dhcpUdpSocket.read();
454  type = _dhcpUdpSocket.read();
455  break;
456 
457  case subnetMask :
458  opt_len = _dhcpUdpSocket.read();
459  _dhcpUdpSocket.read(_dhcpSubnetMask, 4);
460  break;
461 
462  case routersOnSubnet :
463  opt_len = _dhcpUdpSocket.read();
464  _dhcpUdpSocket.read(_dhcpGatewayIp, 4);
465  for (int i = 0; i < opt_len-4; i++)
466  {
467  _dhcpUdpSocket.read();
468  }
469  break;
470 
471  case dns :
472  opt_len = _dhcpUdpSocket.read();
473  _dhcpUdpSocket.read(_dhcpDnsServerIp, 4);
474  for (int i = 0; i < opt_len-4; i++)
475  {
476  _dhcpUdpSocket.read();
477  }
478  break;
479 
480  case dhcpServerIdentifier :
481  opt_len = _dhcpUdpSocket.read();
482  if( *((uint32_t*)_dhcpDhcpServerIp) == 0 ||
483  IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() )
484  {
485  _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp));
486  }
487  else
488  {
489  // Skip over the rest of this option
490  while (opt_len--)
491  {
492  _dhcpUdpSocket.read();
493  }
494  }
495  break;
496 
497  case dhcpT1value :
498  opt_len = _dhcpUdpSocket.read();
499  _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1));
500  _dhcpT1 = ntohl(_dhcpT1);
501  break;
502 
503  case dhcpT2value :
504  opt_len = _dhcpUdpSocket.read();
505  _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2));
506  _dhcpT2 = ntohl(_dhcpT2);
507  break;
508 
509  case dhcpIPaddrLeaseTime :
510  opt_len = _dhcpUdpSocket.read();
511  _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime));
512  _dhcpLeaseTime = ntohl(_dhcpLeaseTime);
513  _renewInSec = _dhcpLeaseTime;
514  break;
515 
516  default :
517  opt_len = _dhcpUdpSocket.read();
518  // Skip over the rest of this option
519  while (opt_len--)
520  {
521  _dhcpUdpSocket.read();
522  }
523  break;
524  }
525  }
526  }
527 
528  // Need to skip to end of the packet regardless here
529  _dhcpUdpSocket.flush();
530 
531  return type;
532 }
533 
534 
535 /*
536  returns:
537  0/DHCP_CHECK_NONE: nothing happened
538  1/DHCP_CHECK_RENEW_FAIL: renew failed
539  2/DHCP_CHECK_RENEW_OK: renew success
540  3/DHCP_CHECK_REBIND_FAIL: rebind fail
541  4/DHCP_CHECK_REBIND_OK: rebind success
542 */
543 int DhcpClass::checkLease(){
544  //this uses a signed / unsigned trick to deal with millis overflow
545  unsigned long now = millis();
546  signed long snow = (long)now;
547  int rc=DHCP_CHECK_NONE;
548  if (_lastCheck != 0){
549  signed long factor;
550  //calc how many ms past the timeout we are
551  factor = snow - (long)_secTimeout;
552  //if on or passed the timeout, reduce the counters
553  if ( factor >= 0 ){
554  //next timeout should be now plus 1000 ms minus parts of second in factor
555  _secTimeout = snow + 1000 - factor % 1000;
556  //how many seconds late are we, minimum 1
557  factor = factor / 1000 +1;
558 
559  //reduce the counters by that mouch
560  //if we can assume that the cycle time (factor) is fairly constant
561  //and if the remainder is less than cycle time * 2
562  //do it early instead of late
563  if(_renewInSec < factor*2 )
564  _renewInSec = 0;
565  else
566  _renewInSec -= factor;
567 
568  if(_rebindInSec < factor*2 )
569  _rebindInSec = 0;
570  else
571  _rebindInSec -= factor;
572  }
573 
574  //if we have a lease but should renew, do it
575  if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){
576  _dhcp_state = STATE_DHCP_REREQUEST;
577  rc = 1 + request_DHCP_lease();
578  }
579 
580  //if we have a lease or is renewing but should bind, do it
581  if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){
582  //this should basically restart completely
583  _dhcp_state = STATE_DHCP_START;
584  reset_DHCP_lease();
585  rc = 3 + request_DHCP_lease();
586  }
587  }
588  else{
589  _secTimeout = snow + 1000;
590  }
591 
592  _lastCheck = now;
593  return rc;
594 }
595 
596 #ifdef __cplusplus
597 }
598 #endif
599 #endif
zrtos_vfs_module_dhcp_state_t state
Definition: dhcp.h:162
zrtos_vfs_module_dhcp_state_t
Definition: dhcp.h:35
zrtos_vfs_module_network_subnet_mask_t subnet_mask
Definition: dhcp.h:159
zrtos_vfs_module_dhcp_message_type_t
Definition: dhcp.h:51
zrtos_vfs_module_dhcp_option_t
Definition: dhcp.h:76
zrtos_vfs_module_network_ip_t gateway
Definition: dhcp.h:160
zrtos_vfs_module_dhcp_error_t
Definition: dhcp.h:62
zrtos_vfs_module_network_ip_t ip
Definition: dhcp.h:158
zrtos_vfs_module_dhcp_message_op_t
Definition: dhcp.h:45
static uint8_t
Definition: mcp2515.h:159
zrtos_vfs_module_network_ip_t dns_server
Definition: dhcp.h:161