Contiki 2.5
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  * @(#)$Id: dhcps.c,v 1.2 2010/10/19 18:29:04 adamdunkels Exp $
33  */
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <uip_arp.h>
38 #include "contiki.h"
39 #include "contiki-net.h"
40 #include "dhcps.h"
41 
42 struct dhcp_msg {
43  uint8_t op, htype, hlen, hops;
44  uint8_t xid[4];
45  uint16_t secs, flags;
46  uint8_t ciaddr[4];
47  uint8_t yiaddr[4];
48  uint8_t siaddr[4];
49  uint8_t giaddr[4];
50  uint8_t chaddr[16];
51 #ifndef UIP_CONF_DHCP_LIGHT
52  uint8_t sname[64];
53  uint8_t file[128];
54 #endif
55  uint8_t options[312];
56 } CC_BYTE_ALIGNED;
57 
58 #define BOOTP_BROADCAST 0x8000
59 
60 #define DHCP_REQUEST 1
61 #define DHCP_REPLY 2
62 #define DHCP_HTYPE_ETHERNET 1
63 #define DHCP_HLEN_ETHERNET 6
64 #define DHCP_MSG_LEN 236
65 
66 #define DHCPS_SERVER_PORT 67
67 #define DHCPS_CLIENT_PORT 68
68 
69 #define DHCPDISCOVER 1
70 #define DHCPOFFER 2
71 #define DHCPREQUEST 3
72 #define DHCPDECLINE 4
73 #define DHCPACK 5
74 #define DHCPNAK 6
75 #define DHCPRELEASE 7
76 #define DHCPINFORM 8
77 
78 #define DHCP_OPTION_SUBNET_MASK 1
79 #define DHCP_OPTION_ROUTER 3
80 #define DHCP_OPTION_DNS_SERVER 6
81 #define DHCP_OPTION_REQ_IPADDR 50
82 #define DHCP_OPTION_LEASE_TIME 51
83 #define DHCP_OPTION_MSG_TYPE 53
84 #define DHCP_OPTION_SERVER_ID 54
85 #define DHCP_OPTION_REQ_LIST 55
86 #define DHCP_OPTION_END 255
87 
88 
89 
90 #define LEASE_FLAGS_ALLOCATED 0x01 /* Lease with an allocated address*/
91 #define LEASE_FLAGS_VALID 0x02 /* Contains a valid but
92  possibly outdated lease */
93 
94 
95 static const struct dhcps_config *config;
96 
97 
98 static uint8_t *
99 find_option(uint8_t option)
100 {
101  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
102  uint8_t *optptr = &m->options[4];
103  uint8_t *end = (uint8_t*)uip_appdata + uip_datalen();
104  while(optptr < end && *optptr != DHCP_OPTION_END) {
105  if(*optptr == option) {
106  return optptr;
107  }
108  optptr += optptr[1] + 2;
109  }
110  return NULL;
111 }
112 
113 static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
114 
115 static int
116 check_cookie(void)
117 {
118  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
119  return memcmp(m->options, magic_cookie, 4) == 0;
120 }
121 
122 /* Finds any valid lease for a given MAC address */
123 static struct dhcps_client_lease *
124 lookup_lease_mac(const uint8_t *chaddr, uint8_t hlen)
125 {
126  struct dhcps_client_lease *lease = config->leases;
127  struct dhcps_client_lease *end = config->leases + config->num_leases;
128  while(lease != end) {
129  if (lease->flags & LEASE_FLAGS_VALID
130  && memcmp(lease->chaddr, chaddr, hlen) == 0) {
131  return lease;
132  }
133  lease++;
134  }
135  return NULL;
136 }
137 
138 static struct dhcps_client_lease *
139 lookup_lease_ip(const uip_ipaddr_t *ip)
140 {
141  struct dhcps_client_lease *lease = config->leases;
142  struct dhcps_client_lease *end = config->leases + config->num_leases;
143  while(lease != end) {
144  if (uip_ipaddr_cmp(&lease->ipaddr, ip)) {
145  return lease;
146  }
147  lease++;
148  }
149  return NULL;
150 }
151 
152 static struct dhcps_client_lease *
153 find_free_lease(void)
154 {
155  struct dhcps_client_lease *found = NULL;
156  struct dhcps_client_lease *lease = config->leases;
157  struct dhcps_client_lease *end = config->leases + config->num_leases;
158  while(lease != end) {
159  if (!(lease->flags & LEASE_FLAGS_VALID)) return lease;
160  if (!(lease->flags & LEASE_FLAGS_ALLOCATED)) found = lease;
161  lease++;
162  }
163  return found;
164 }
165 
166 struct dhcps_client_lease *
167 init_lease(struct dhcps_client_lease *lease,
168  const uint8_t *chaddr, uint8_t hlen)
169 {
170  if (lease) {
171  memcpy(lease->chaddr, chaddr, hlen);
172  lease->flags = LEASE_FLAGS_VALID;
173  }
174  return lease;
175 }
176 
177 
178 static struct dhcps_client_lease *
179 choose_address()
180 {
181  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
182  struct dhcps_client_lease *lease;
183  lease = lookup_lease_mac(m->chaddr, m->hlen);
184  if (lease) {
185  return lease;
186  }
187  {
188  uint8_t *opt;
189  opt = find_option(DHCP_OPTION_REQ_IPADDR);
190  if (opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
191  && !(lease->flags & LEASE_FLAGS_ALLOCATED)) {
192  return init_lease(lease, m->chaddr,m->hlen);
193  }
194  }
195  lease = find_free_lease();
196  if (lease) {
197  return init_lease(lease, m->chaddr,m->hlen);
198  }
199  return NULL;
200 }
201 
202 static struct dhcps_client_lease *
203 allocate_address()
204 {
205  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
206  struct dhcps_client_lease *lease;
207  lease = lookup_lease_mac(m->chaddr, m->hlen);
208  if (!lease) {
209  uint8_t *opt;
210  opt = find_option(DHCP_OPTION_REQ_IPADDR);
211  if (!(opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
212  && !(lease->flags & LEASE_FLAGS_ALLOCATED))) {
213  return NULL;
214  }
215  }
216  lease->lease_end = clock_seconds()+config->default_lease_time;
217  lease->flags |= LEASE_FLAGS_ALLOCATED;
218  return lease;
219 }
220 
221 static struct dhcps_client_lease *
222 release_address()
223 {
224  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
225  struct dhcps_client_lease *lease;
226  lease = lookup_lease_mac(m->chaddr, m->hlen);
227  if (!lease) {
228  return NULL;
229  }
230  lease->flags &= ~LEASE_FLAGS_ALLOCATED;
231  return lease;
232 }
233 
234 /*---------------------------------------------------------------------------*/
235 static uint8_t *
236 add_msg_type(uint8_t *optptr, uint8_t type)
237 {
238  *optptr++ = DHCP_OPTION_MSG_TYPE;
239  *optptr++ = 1;
240  *optptr++ = type;
241  return optptr;
242 }
243 /*---------------------------------------------------------------------------*/
244 static uint8_t *
245 add_server_id(uint8_t *optptr)
246 {
247  *optptr++ = DHCP_OPTION_SERVER_ID;
248  *optptr++ = 4;
249  memcpy(optptr, &uip_hostaddr, 4);
250  return optptr + 4;
251 }
252 /*---------------------------------------------------------------------------*/
253 static uint8_t *
254 add_lease_time(uint8_t *optptr)
255 {
256  uint32_t lt;
257  *optptr++ = DHCP_OPTION_LEASE_TIME;
258  *optptr++ = 4;
259  lt = UIP_HTONL(config->default_lease_time);
260  memcpy(optptr, &lt, 4);
261  return optptr + 4;
262 }
263 
264 /*---------------------------------------------------------------------------*/
265 static uint8_t *
266 add_end(uint8_t *optptr)
267 {
268  *optptr++ = DHCP_OPTION_END;
269  return optptr;
270 }
271 
272 static uint8_t *
273 add_config(uint8_t *optptr)
274 {
275  if (config->flags & DHCP_CONF_NETMASK) {
276  *optptr++ = DHCP_OPTION_SUBNET_MASK;
277  *optptr++ = 4;
278  memcpy(optptr, &config->netmask, 4);
279  optptr += 4;
280  }
281  if (config->flags & DHCP_CONF_DNSADDR) {
282  *optptr++ = DHCP_OPTION_DNS_SERVER;
283  *optptr++ = 4;
284  memcpy(optptr, &config->dnsaddr, 4);
285  optptr += 4;
286  }
287  if (config->flags & DHCP_CONF_DEFAULT_ROUTER) {
288  *optptr++ = DHCP_OPTION_ROUTER;
289  *optptr++ = 4;
290  memcpy(optptr, &config->default_router, 4);
291  optptr += 4;
292  }
293  return optptr;
294 }
295 
296 static void
297 create_msg(CC_REGISTER_ARG struct dhcp_msg *m)
298 {
299  m->op = DHCP_REPLY;
300  /* m->htype = DHCP_HTYPE_ETHERNET; */
301 /* m->hlen = DHCP_HLEN_ETHERNET; */
302 /* memcpy(m->chaddr, &uip_ethaddr,DHCP_HLEN_ETHERNET); */
303  m->hops = 0;
304  m->secs = 0;
305  memcpy(m->siaddr, &uip_hostaddr, 4);
306  m->sname[0] = '\0';
307  m->file[0] = '\0';
308  memcpy(m->options, magic_cookie, sizeof(magic_cookie));
309 }
310 
311 static uip_ipaddr_t any_addr;
312 static uip_ipaddr_t bcast_addr;
313 
314 /*---------------------------------------------------------------------------*/
315 static void
316 send_offer(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
317 {
318  u8_t *end;
319  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
320 
321  create_msg(m);
322  memcpy(&m->yiaddr, &lease->ipaddr,4);
323 
324  end = add_msg_type(&m->options[4], DHCPOFFER);
325  end = add_server_id(end);
326  end = add_lease_time(end);
327  end = add_config(end);
328  end = add_end(end);
329  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
330  uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata));
331 }
332 
333 static void
334 send_ack(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
335 {
336  u8_t *end;
337  uip_ipaddr_t ciaddr;
338  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
339 
340  create_msg(m);
341  memcpy(&m->yiaddr, &lease->ipaddr,4);
342 
343  end = add_msg_type(&m->options[4], DHCPACK);
344  end = add_server_id(end);
345  end = add_lease_time(end);
346  end = add_config(end);
347  end = add_end(end);
348  memcpy(&ciaddr, &lease->ipaddr,4);
349  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
350  uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata));
351  printf("ACK\n");
352 }
353 static void
354 send_nack(struct uip_udp_conn *conn)
355 {
356  u8_t *end;
357  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
358 
359  create_msg(m);
360  memset(&m->yiaddr, 0, 4);
361 
362  end = add_msg_type(&m->options[4], DHCPNAK);
363  end = add_server_id(end);
364  end = add_end(end);
365 
366  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
367  uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata));
368  printf("NACK\n");
369 }
370 
371 /*---------------------------------------------------------------------------*/
372 PROCESS(dhcp_server_process, "DHCP server");
373 /*---------------------------------------------------------------------------*/
374 
375 PROCESS_THREAD(dhcp_server_process, ev , data)
376 {
377  static struct uip_udp_conn *conn;
378  static struct uip_udp_conn *send_conn;
379  static struct dhcps_client_lease *lease;
380  PROCESS_BEGIN();
381  printf("DHCP server starting\n");
382  uip_ipaddr(&any_addr, 0,0,0,0);
383  uip_ipaddr(&bcast_addr, 255,255,255,255);
384  conn = udp_new(&any_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
385  if (!conn) goto exit;
386  send_conn = udp_new(&bcast_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
387  if (!send_conn) goto exit;
388 
389  uip_udp_bind(conn, UIP_HTONS(DHCPS_SERVER_PORT));
390  uip_udp_bind(send_conn, UIP_HTONS(DHCPS_SERVER_PORT));
391  while(1) {
393  if(ev == tcpip_event) {
394  if (uip_newdata()) {
395  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
396  struct uip_udpip_hdr *header = (struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN];
397 
398  if (m->op == DHCP_REQUEST && check_cookie() && m->hlen <= MAX_HLEN) {
399  uint8_t *opt = find_option(DHCP_OPTION_MSG_TYPE);
400  if (opt) {
401  uint8_t mtype = opt[2];
402  if (opt[2] == DHCPDISCOVER) {
403  printf("Discover\n");
404  lease = choose_address();
405  if (lease) {
406  lease->lease_end = clock_seconds()+config->default_lease_time;
407  tcpip_poll_udp(send_conn);
409  send_offer(conn,lease);
410  }
411  } else {
412  uint8_t *opt = find_option(DHCP_OPTION_SERVER_ID);
413  if (!opt || uip_ipaddr_cmp((uip_ipaddr_t*)&opt[2], &uip_hostaddr)) {
414  if (mtype == DHCPREQUEST) {
415  printf("Request\n");
416  lease = allocate_address();
417  tcpip_poll_udp(send_conn);
419  if (!lease) {
420  send_nack(send_conn);
421  } else {
422  send_ack(send_conn,lease);
423  }
424  } else if (mtype == DHCPRELEASE) {
425  printf("Release\n");
426  release_address();
427  } else if (mtype == DHCPDECLINE) {
428  printf("Decline\n");
429  } else if (mtype == DHCPINFORM) {
430  printf("Inform\n");
431  }
432  }
433  }
434  }
435  }
436  }
437  } else if (uip_poll()) {
438 
439  }
440  }
441  exit:
442  printf("DHCP server exiting\n");
443  PROCESS_END();
444 }
445 
446 void
447 dhcps_init(const struct dhcps_config *conf)
448 {
449  config = conf;
450  process_start(&dhcp_server_process,NULL);
451 }