COSA
An Object-Oriented Platform for Arduino Programming
DHCP.cpp
Go to the documentation of this file.
1 
21 #include "DHCP.hh"
22 #include "Cosa/Watchdog.hh"
23 #include "Cosa/Errno.h"
24 
25 DHCP::DHCP(const char* hostname, const uint8_t* mac) :
26  m_hostname(hostname),
27  m_mac(mac),
28  m_sock(NULL),
29  m_lease_obtained(0L),
30  m_lease_expires(0L)
31 {
32 }
33 
34 int
35 DHCP::send(uint8_t type)
36 {
37  if (UNLIKELY(m_sock == NULL)) return (ENOTSOCK);
38 
39  // Start the construction of the message
40  uint8_t BROADCAST[4] = { 0xff, 0xff, 0xff, 0xff };
41  int res = m_sock->datagram(BROADCAST, SERVER_PORT);
42  if (UNLIKELY(res < 0)) return (res);
43 
44  // Construct DHCP message header
45  header_t header;
46  memset(&header, 0, sizeof(header));
47  header.OP = REQUEST;
48  header.HTYPE = HTYPE_10MB;
49  header.HLEN = HLEN_ETHERNET;
50  header.XID = Watchdog::millis();
51  header.SECS = 1;
52  header.FLAGS = hton((int16_t) FLAGS_BROADCAST);
53  memcpy_P(header.CHADDRB, m_mac, INET::MAC_MAX);
54  res = m_sock->write(&header, sizeof(header));
55  if (UNLIKELY(res < 0)) return (res);
56 
57  // Write BOOTP legacy (192 bytes zero)
58  uint8_t buf[32];
59  memset(buf, 0, sizeof(buf));
60  for (uint8_t i = 0; i < 6; i++) m_sock->write(buf, sizeof(buf));
61 
62  // Write DHCP Magic Cookie
63  uint32_t magic = hton((int32_t) MAGIC_COOKIE);
64  res = m_sock->write(&magic, sizeof(magic));
65  if (res < 0) return (res);
66 
67  // Write DHCP options; message type, client hardware address and hostname
68  uint8_t len = strlen_P(m_hostname) + 1;
69  buf[0] = MESSAGE_TYPE;
70  buf[1] = 1;
71  buf[2] = type;
72  buf[3] = CLIENT_IDENTIFIER;
73  buf[4] = 7;
74  buf[5] = 1;
75  memcpy(&buf[6], m_mac, INET::MAC_MAX);
76  buf[12] = HOSTNAME;
77  buf[13] = len;
78  memcpy_P(&buf[14], m_hostname, len);
79  res = m_sock->write(buf, 14 + len);
80  if (UNLIKELY(res < 0)) return (res);
81 
82  // On request add client and server address options
83  if (type == DHCP_REQUEST) {
84  buf[0] = REQUESTED_IP_ADDR;
85  buf[1] = INET::IP_MAX;
86  memcpy(&buf[2], m_ip, INET::IP_MAX);
87  buf[6] = SERVER_IDENTIFIER;
88  buf[7] = INET::IP_MAX;
89  memcpy(&buf[8], m_dhcp, INET::IP_MAX);
90  res = m_sock->write(buf, 8 + INET::IP_MAX);
91  if (UNLIKELY(res < 0)) return (res);
92  }
93 
94  // Parameter request and end option list
95  static const uint8_t param[] __PROGMEM = {
96  PARAM_REQUEST,
97  5,
98  SUBNET_MASK,
99  ROUTERS_ON_SUBNET,
100  DNS_SERVER,
101  DOMAIN_NAME,
102  IP_ADDR_LEASE_TIME,
103  END_OPTION
104  };
105  res = m_sock->write_P(param, sizeof(param));
106  if (UNLIKELY(res < 0)) return (res);
107  return (m_sock->flush());
108 }
109 
110 int
111 DHCP::recv(uint8_t type, uint16_t ms)
112 {
113  if (UNLIKELY(m_sock == NULL)) return (ENOTSOCK);
114 
115  // Wait for a reply
116  int res = 0;
117  for (uint16_t i = 0; i < ms; i += 32) {
118  if ((res = m_sock->available()) != 0) break;
119  delay(32);
120  }
121  if (UNLIKELY(res == 0)) return (ETIME);
122 
123  // Read response message
124  header_t header;
125  uint16_t port;
126  res = m_sock->recv(&header, sizeof(header), m_dhcp, port);
127  if (UNLIKELY(res <= 0)) return (EIO);
128  // Fix: Should also check that the hardware address (broadcast)
129  if (UNLIKELY(port != SERVER_PORT)) return (ENXIO);
130  if (UNLIKELY(header.OP != REPLY)) return (EBADR);
131  memcpy(m_ip, header.YIADDR, sizeof(m_ip));
132 
133  // Skip legacy BOOTP parameters
134  uint8_t buf[32];
135  for (uint8_t i = 0; i < 6; i++) m_sock->read(buf, sizeof(buf));
136 
137  // Check Magic Cookie
138  uint32_t magic;
139  res = m_sock->read(&magic, sizeof(magic));
140  if (UNLIKELY(res < 0)) return (EIO);
141  magic = ntoh((int32_t) magic);
142  if (UNLIKELY(magic != MAGIC_COOKIE)) return (EBADRQC);
143 
144  // Parse options and collect; subnet mask, server addresses and lease time
145  uint8_t op;
146  uint8_t len;
147  res = 0;
148  while (m_sock->read(&op, sizeof(op)) == sizeof(op)) {
149  if (op == END_OPTION) break;
150  if (op == PAD_OPTION) continue;
151  m_sock->read(&len, sizeof(len));
152  m_sock->read(buf, len);
153  switch (op) {
154  case MESSAGE_TYPE:
155  if (buf[0] != type) res = -6;
156  break;
157  case SUBNET_MASK:
158  memcpy(m_subnet, buf, sizeof(m_subnet));
159  break;
160  case DNS_SERVER:
161  memcpy(m_dns, buf, sizeof(m_dns));
162  break;
163  case ROUTERS_ON_SUBNET:
164  memcpy(m_gateway, buf, sizeof(m_gateway));
165  break;
166  case IP_ADDR_LEASE_TIME:
167  m_lease_obtained = Watchdog::millis() / 1000;
168  int32_t* expire_p = (int32_t*) buf;
169  m_lease_expires = ntoh(*expire_p) + m_lease_obtained;
170  break;
171  };
172  };
173 
174  // Flush any remains of the reply
175  while (m_sock->available() > 0) m_sock->read(buf, sizeof(buf));
176  return (res);
177 }
178 
179 bool
181 {
182  if (UNLIKELY(m_sock != NULL)) return (false);
183  m_sock = sock;
184  return (true);
185 }
186 
187 bool
189 {
190  if (UNLIKELY(m_sock == NULL)) return (false);
191  m_sock->close();
192  m_sock = NULL;
193  return (true);
194 }
195 
196 int
198 {
199  if (UNLIKELY(m_sock == NULL)) return (ENOTSOCK);
200  int res = send(DHCP_DISCOVER);
201  if (UNLIKELY(res < 0)) return (res);
202  return (recv(DHCP_OFFER));
203 }
204 
205 int
206 DHCP::request(uint8_t ip[4], uint8_t subnet[4], uint8_t gateway[4])
207 {
208  if (UNLIKELY(m_sock == NULL)) return (ENOTSOCK);
209  int res = send(DHCP_REQUEST);
210  if (UNLIKELY(res < 0)) return (res);
211  res = recv(DHCP_ACK);
212  if (UNLIKELY(res < 0)) return (res);
213  memcpy(ip, m_ip, sizeof(m_ip));
214  memcpy(subnet, m_subnet, sizeof(m_subnet));
215  memcpy(gateway, m_gateway, sizeof(m_gateway));
216  return (0);
217 }
218 
219 int
221 {
222  if (UNLIKELY(m_sock != NULL)) return (ENOTSOCK);
223  if (UNLIKELY(m_lease_expires == 0L)) return (EACCES);
224  m_sock = sock;
225  int res = send(DHCP_REQUEST);
226  if (UNLIKELY(res < 0)) return (res);
227  res = recv(DHCP_ACK);
228  if (UNLIKELY(res < 0)) return (res);
229  m_sock->close();
230  m_sock = NULL;
231  return (0);
232 }
233 
234 int
236 {
237  if (UNLIKELY(m_sock != NULL)) return (EPERM);
238  m_sock = sock;
239  int res = send(DHCP_RELEASE);
240  if (UNLIKELY(res < 0)) return (res);
241  res = recv(DHCP_ACK);
242  if (UNLIKELY(res < 0)) return (res);
243  m_sock->close();
244  m_sock = NULL;
245  memset(m_ip, 0, sizeof(m_ip));
246  m_lease_obtained = 0L;
247  m_lease_expires = 0L;
248  return (0);
249 }
250 
#define EIO
Definition: Errno.h:32
Definition: Socket.hh:31
static uint32_t millis()
Definition: Watchdog.hh:51
virtual int recv(void *buf, size_t len)=0
int request(uint8_t ip[4], uint8_t subnet[4], uint8_t gateway[4])
Definition: DHCP.cpp:206
#define __PROGMEM
Definition: Types.h:183
virtual int write(const void *buf, size_t size)
Definition: Socket.hh:93
#define NULL
Definition: Types.h:101
#define EBADRQC
Definition: Errno.h:83
#define EACCES
Definition: Errno.h:40
int discover()
Definition: DHCP.cpp:197
#define ENOTSOCK
Definition: Errno.h:115
int renew(Socket *sock)
Definition: DHCP.cpp:220
#define ETIME
Definition: Errno.h:89
virtual int datagram(uint8_t addr[4], uint16_t port)=0
#define ENXIO
Definition: Errno.h:33
void(* delay)(uint32_t ms)
bool end()
Definition: DHCP.cpp:188
virtual int write_P(const void *buf, size_t size)
Definition: Socket.hh:105
#define EBADR
Definition: Errno.h:80
virtual int close()=0
virtual int flush()
static const uint8_t IP_MAX
Definition: INET.hh:69
int release(Socket *sock)
Definition: DHCP.cpp:235
#define EPERM
Definition: Errno.h:28
virtual int available()
static const uint8_t MAC_MAX
Definition: INET.hh:68
#define hton
Definition: Types.h:584
DHCP(const char *hostname, const uint8_t *mac)
Definition: DHCP.cpp:25
#define UNLIKELY(x)
Definition: Types.h:153
#define ntoh
Definition: Types.h:583
size_t strlen_P(str_P s)
Definition: Types.h:254
bool begin(Socket *sock)
Definition: DHCP.cpp:180
virtual int read(void *buf, size_t size)
Definition: Socket.hh:132