EVE 1.0
dhcps.c
1 /* Adapted by Simon Berg from net/dhcpc.c */
2 /*
3  * Copyright (c) 2005, Swedish Institute of Computer Science
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the Institute nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * This file is part of the Contiki operating system.
31  *
32  */
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <net/uip_arp.h>
37 #include "contiki.h"
38 #include "contiki-net.h"
39 #include "dhcps.h"
40 
41 struct dhcp_msg {
42  uint8_t op, htype, hlen, hops;
43  uint8_t xid[4];
44  uint16_t secs, flags;
45  uint8_t ciaddr[4];
46  uint8_t yiaddr[4];
47  uint8_t siaddr[4];
48  uint8_t giaddr[4];
49  uint8_t chaddr[16];
50 #ifndef UIP_CONF_DHCP_LIGHT
51  uint8_t sname[64];
52  uint8_t file[128];
53 #endif
54  uint8_t options[312];
55 } CC_BYTE_ALIGNED;
56 
57 /* Disable debug spam from the module */
58 #ifndef NDEBUG
59  #define NDEBUG
60 #endif
61 
62 /** The macro provides debug print functionality */
63 #ifdef NDEBUG
64  #define PRINTD(FORMAT, args...) do {} while (0)
65 #else
66  #define PRINTD(FORMAT, args...) printf("%s" FORMAT, "[DHCPS] ", ##args)
67 #endif
68 
69 #define BOOTP_BROADCAST 0x8000
70 
71 #define DHCP_REQUEST 1
72 #define DHCP_REPLY 2
73 #define DHCP_HTYPE_ETHERNET 1
74 #define DHCP_HLEN_ETHERNET 6
75 #define DHCP_MSG_LEN 236
76 
77 #define DHCPS_SERVER_PORT 67
78 #define DHCPS_CLIENT_PORT 68
79 
80 #define DHCPDISCOVER 1
81 #define DHCPOFFER 2
82 #define DHCPREQUEST 3
83 #define DHCPDECLINE 4
84 #define DHCPACK 5
85 #define DHCPNAK 6
86 #define DHCPRELEASE 7
87 #define DHCPINFORM 8
88 
89 #define DHCP_OPTION_SUBNET_MASK 1
90 #define DHCP_OPTION_ROUTER 3
91 #define DHCP_OPTION_DNS_SERVER 6
92 #define DHCP_OPTION_REQ_IPADDR 50
93 #define DHCP_OPTION_LEASE_TIME 51
94 #define DHCP_OPTION_MSG_TYPE 53
95 #define DHCP_OPTION_SERVER_ID 54
96 #define DHCP_OPTION_REQ_LIST 55
97 #define DHCP_OPTION_END 255
98 
99 
100 
101 #define LEASE_FLAGS_ALLOCATED 0x01 /* Lease with an allocated address*/
102 #define LEASE_FLAGS_VALID 0x02 /* Contains a valid but
103  possibly outdated lease */
104 
105 
106 static const struct dhcps_config *config;
107 
108 
109 static uint8_t *
110 find_option(uint8_t option)
111 {
112  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
113  uint8_t *optptr = &m->options[4];
114  uint8_t *end = (uint8_t*)uip_appdata + uip_datalen();
115  while(optptr < end && *optptr != DHCP_OPTION_END) {
116  if(*optptr == option) {
117  return optptr;
118  }
119  optptr += optptr[1] + 2;
120  }
121  return NULL;
122 }
123 
124 static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
125 
126 static int
127 check_cookie(void)
128 {
129  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
130  return memcmp(m->options, magic_cookie, 4) == 0;
131 }
132 
133 /* Finds any valid lease for a given MAC address */
134 static struct dhcps_client_lease *
135 lookup_lease_mac(const uint8_t *chaddr, uint8_t hlen)
136 {
137  struct dhcps_client_lease *lease = config->leases;
138  struct dhcps_client_lease *end = config->leases + config->num_leases;
139  while(lease != end) {
140  if ((lease->flags & LEASE_FLAGS_VALID)
141  && memcmp(lease->chaddr, chaddr, hlen) == 0) {
142  return lease;
143  }
144  lease++;
145  }
146  return NULL;
147 }
148 
149 static struct dhcps_client_lease *
150 lookup_lease_ip(const uip_ipaddr_t *ip)
151 {
152  struct dhcps_client_lease *lease = config->leases;
153  struct dhcps_client_lease *end = config->leases + config->num_leases;
154  while(lease != end) {
155  if (uip_ipaddr_cmp(&lease->ipaddr, ip)) {
156  return lease;
157  }
158  lease++;
159  }
160  return NULL;
161 }
162 
163 static struct dhcps_client_lease *
164 find_free_lease(void)
165 {
166  struct dhcps_client_lease *found = NULL;
167  struct dhcps_client_lease *lease = config->leases;
168  struct dhcps_client_lease *end = config->leases + config->num_leases;
169  while(lease != end) {
170  if (!(lease->flags & LEASE_FLAGS_VALID)) return lease;
171  if (!(lease->flags & LEASE_FLAGS_ALLOCATED)) found = lease;
172  lease++;
173  }
174  return found;
175 }
176 
177 struct dhcps_client_lease *
178 init_lease(struct dhcps_client_lease *lease,
179  const uint8_t *chaddr, uint8_t hlen)
180 {
181  if (lease) {
182  memcpy(lease->chaddr, chaddr, hlen);
183  lease->flags = LEASE_FLAGS_VALID;
184  }
185  return lease;
186 }
187 
188 
189 static struct dhcps_client_lease *
190 choose_address()
191 {
192  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
193  struct dhcps_client_lease *lease;
194  lease = lookup_lease_mac(m->chaddr, m->hlen);
195  if (lease) {
196  return lease;
197  }
198  {
199  uint8_t *opt;
200  opt = find_option(DHCP_OPTION_REQ_IPADDR);
201  if (opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
202  && !(lease->flags & LEASE_FLAGS_ALLOCATED)) {
203  return init_lease(lease, m->chaddr,m->hlen);
204  }
205  }
206  lease = find_free_lease();
207  if (lease) {
208  return init_lease(lease, m->chaddr,m->hlen);
209  }
210  return NULL;
211 }
212 
213 static struct dhcps_client_lease *
214 allocate_address()
215 {
216  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
217  struct dhcps_client_lease *lease;
218  lease = lookup_lease_mac(m->chaddr, m->hlen);
219  if (!lease) {
220  uint8_t *opt;
221  opt = find_option(DHCP_OPTION_REQ_IPADDR);
222  if (!(opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
223  && !(lease->flags & LEASE_FLAGS_ALLOCATED))) {
224  return NULL;
225  }
226  }
227  lease->lease_end = clock_seconds()+config->default_lease_time;
228  lease->flags |= LEASE_FLAGS_ALLOCATED;
229  return lease;
230 }
231 
232 static struct dhcps_client_lease *
233 release_address()
234 {
235  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
236  struct dhcps_client_lease *lease;
237  lease = lookup_lease_mac(m->chaddr, m->hlen);
238  if (!lease) {
239  return NULL;
240  }
241  lease->flags &= ~LEASE_FLAGS_ALLOCATED;
242  return lease;
243 }
244 
245 /*---------------------------------------------------------------------------*/
246 static uint8_t *
247 add_msg_type(uint8_t *optptr, uint8_t type)
248 {
249  *optptr++ = DHCP_OPTION_MSG_TYPE;
250  *optptr++ = 1;
251  *optptr++ = type;
252  return optptr;
253 }
254 /*---------------------------------------------------------------------------*/
255 static uint8_t *
256 add_server_id(uint8_t *optptr)
257 {
258  *optptr++ = DHCP_OPTION_SERVER_ID;
259  *optptr++ = 4;
260  memcpy(optptr, &uip_hostaddr, 4);
261  return optptr + 4;
262 }
263 /*---------------------------------------------------------------------------*/
264 static uint8_t *
265 add_lease_time(uint8_t *optptr)
266 {
267  uint32_t lt;
268  *optptr++ = DHCP_OPTION_LEASE_TIME;
269  *optptr++ = 4;
270  lt = UIP_HTONL(config->default_lease_time);
271  memcpy(optptr, &lt, 4);
272  return optptr + 4;
273 }
274 
275 /*---------------------------------------------------------------------------*/
276 static uint8_t *
277 add_end(uint8_t *optptr)
278 {
279  *optptr++ = DHCP_OPTION_END;
280  return optptr;
281 }
282 
283 static uint8_t *
284 add_config(uint8_t *optptr)
285 {
286  if (config->flags & DHCP_CONF_NETMASK) {
287  *optptr++ = DHCP_OPTION_SUBNET_MASK;
288  *optptr++ = 4;
289  memcpy(optptr, &config->netmask, 4);
290  optptr += 4;
291  }
292  if (config->flags & DHCP_CONF_DNSADDR) {
293  *optptr++ = DHCP_OPTION_DNS_SERVER;
294  *optptr++ = 4;
295  memcpy(optptr, &config->dnsaddr, 4);
296  optptr += 4;
297  }
298  if (config->flags & DHCP_CONF_DEFAULT_ROUTER) {
299  *optptr++ = DHCP_OPTION_ROUTER;
300  *optptr++ = 4;
301  memcpy(optptr, &config->default_router, 4);
302  optptr += 4;
303  }
304  return optptr;
305 }
306 
307 static void
308 create_msg(CC_REGISTER_ARG struct dhcp_msg *m)
309 {
310  m->op = DHCP_REPLY;
311  /* m->htype = DHCP_HTYPE_ETHERNET; */
312 /* m->hlen = DHCP_HLEN_ETHERNET; */
313 /* memcpy(m->chaddr, &uip_lladdr,DHCP_HLEN_ETHERNET); */
314  m->hops = 0;
315  m->secs = 0;
316  memcpy(m->siaddr, &uip_hostaddr, 4);
317  m->sname[0] = '\0';
318  m->file[0] = '\0';
319  memcpy(m->options, magic_cookie, sizeof(magic_cookie));
320 }
321 
322 static uip_ipaddr_t any_addr;
323 static uip_ipaddr_t bcast_addr;
324 
325 /*---------------------------------------------------------------------------*/
326 static void
327 send_offer(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
328 {
329  uint8_t *end;
330  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
331 
332  create_msg(m);
333  memcpy(&m->yiaddr, &lease->ipaddr,4);
334 
335  end = add_msg_type(&m->options[4], DHCPOFFER);
336  end = add_server_id(end);
337  end = add_lease_time(end);
338  end = add_config(end);
339  end = add_end(end);
340  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
341  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
342 }
343 
344 static void
345 send_ack(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
346 {
347  uint8_t *end;
348  uip_ipaddr_t ciaddr;
349  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
350 
351  create_msg(m);
352  memcpy(&m->yiaddr, &lease->ipaddr,4);
353 
354  end = add_msg_type(&m->options[4], DHCPACK);
355  end = add_server_id(end);
356  end = add_lease_time(end);
357  end = add_config(end);
358  end = add_end(end);
359  memcpy(&ciaddr, &lease->ipaddr,4);
360  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
361  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
362  PRINTD("ACK\n");
363 }
364 static void
365 send_nack(struct uip_udp_conn *conn)
366 {
367  uint8_t *end;
368  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
369 
370  create_msg(m);
371  memset(&m->yiaddr, 0, 4);
372 
373  end = add_msg_type(&m->options[4], DHCPNAK);
374  end = add_server_id(end);
375  end = add_end(end);
376 
377  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
378  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
379  PRINTD("NACK\n");
380 }
381 
382 /*---------------------------------------------------------------------------*/
383 PROCESS(dhcp_server_process, "DHCP server");
384 /*---------------------------------------------------------------------------*/
385 
386 PROCESS_THREAD(dhcp_server_process, ev , data)
387 {
388  static struct uip_udp_conn *conn;
389  static struct uip_udp_conn *send_conn;
390  static struct dhcps_client_lease *lease;
391  PROCESS_BEGIN();
392  PRINTD("DHCP server starting\n");
393  uip_ipaddr(&any_addr, 0,0,0,0);
394  uip_ipaddr(&bcast_addr, 255,255,255,255);
395  conn = udp_new(&any_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
396  if (!conn) goto exit;
397  send_conn = udp_new(&bcast_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
398  if (!send_conn) goto exit;
399 
400  uip_udp_bind(conn, UIP_HTONS(DHCPS_SERVER_PORT));
401  uip_udp_bind(send_conn, UIP_HTONS(DHCPS_SERVER_PORT));
402  while(1) {
404  if(ev == tcpip_event) {
405  if (uip_newdata()) {
406  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
407  //struct uip_udpip_hdr *header = (struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN];
408 
409  if (m->op == DHCP_REQUEST && check_cookie() && m->hlen <= MAX_HLEN) {
410  uint8_t *opt = find_option(DHCP_OPTION_MSG_TYPE);
411  if (opt) {
412  uint8_t mtype = opt[2];
413  if (opt[2] == DHCPDISCOVER) {
414  PRINTD("Discover\n");
415  lease = choose_address();
416  if (lease) {
417  lease->lease_end = clock_seconds()+config->default_lease_time;
418  tcpip_poll_udp(send_conn);
420  send_offer(conn,lease);
421  }
422  } else {
423  uint8_t *opt = find_option(DHCP_OPTION_SERVER_ID);
424  if (!opt || uip_ipaddr_cmp((uip_ipaddr_t*)&opt[2], &uip_hostaddr)) {
425  if (mtype == DHCPREQUEST) {
426  PRINTD("Request\n");
427  lease = allocate_address();
428  tcpip_poll_udp(send_conn);
430  if (!lease) {
431  send_nack(send_conn);
432  } else {
433  send_ack(send_conn,lease);
434  }
435  } else if (mtype == DHCPRELEASE) {
436  PRINTD("Release\n");
437  release_address();
438  } else if (mtype == DHCPDECLINE) {
439  PRINTD("Decline\n");
440  } else if (mtype == DHCPINFORM) {
441  PRINTD("Inform\n");
442  }
443  }
444  }
445  }
446  }
447  }
448  } else if (uip_poll()) {
449 
450  }
451  }
452  exit:
453  PRINTD("DHCP server exiting\n");
454  PROCESS_END();
455 #ifdef ECLIPSE_STUB_CODE_ANALYSE
456  return PT_ENDED;
457 #endif /* ECLIPSE_STUB_CODE_ANALYSE */
458 }
459 
460 void
461 dhcps_init(const struct dhcps_config *conf)
462 {
463  config = conf;
464  process_start(&dhcp_server_process,NULL);
465 }
466 
467 void
468 dhcps_exit()
469 {
470  process_exit(&dhcp_server_process);
471 }
#define PROCESS_END()
Definition: process.h:140
#define PROCESS(name, strname)
Definition: process.h:316
void process_start(struct process *p, const char *arg)
Definition: process.c:100
#define PRINTD(FORMAT, args...)
Definition: uip.c:100
#define PROCESS_WAIT_EVENT_UNTIL(c)
Definition: process.h:166
void process_exit(struct process *p)
Cause a process to exit.
Definition: process.c:203
#define PROCESS_WAIT_EVENT()
Definition: process.h:150
#define CC_REGISTER_ARG
Definition: cc.h:56
#define uip_ipaddr(addr, addr0, addr1, addr2, addr3)
Definition: uip.h:965
void uip_send(const void *data, int len)
Definition: uip.c:2088
process_event_t tcpip_event
Definition: tcpip.c:81
#define PROCESS_BEGIN()
Definition: process.h:129
#define uip_newdata()
Definition: uip.h:745
#define uip_ipaddr_copy(dest, src)
Definition: uip.h:1036
CCIF struct uip_udp_conn * udp_new(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
#define UIP_HTONS(n)
Definition: uip.h:1248
uint8_t data[USBNET_RX_BUF_SIZE]
Definition: usbnet.h:140
uip_ipaddr_t ripaddr
Definition: uip.h:1413
#define PROCESS_THREAD(name, ev, data)
Definition: process.h:282
#define uip_udp_bind(conn, port)
Definition: uip.h:895
CCIF unsigned long clock_seconds(void)
Definition: clock.c:201
#define uip_poll()
Definition: uip.h:824
CCIF void tcpip_poll_udp(struct uip_udp_conn *conn)
void * uip_appdata
Definition: uip.c:154
#define uip_datalen()
Definition: uip.h:653