Contiki 3.x
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 <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 #define BOOTP_BROADCAST 0x8000
58 
59 #define DHCP_REQUEST 1
60 #define DHCP_REPLY 2
61 #define DHCP_HTYPE_ETHERNET 1
62 #define DHCP_HLEN_ETHERNET 6
63 #define DHCP_MSG_LEN 236
64 
65 #define DHCPS_SERVER_PORT 67
66 #define DHCPS_CLIENT_PORT 68
67 
68 #define DHCPDISCOVER 1
69 #define DHCPOFFER 2
70 #define DHCPREQUEST 3
71 #define DHCPDECLINE 4
72 #define DHCPACK 5
73 #define DHCPNAK 6
74 #define DHCPRELEASE 7
75 #define DHCPINFORM 8
76 
77 #define DHCP_OPTION_SUBNET_MASK 1
78 #define DHCP_OPTION_ROUTER 3
79 #define DHCP_OPTION_DNS_SERVER 6
80 #define DHCP_OPTION_REQ_IPADDR 50
81 #define DHCP_OPTION_LEASE_TIME 51
82 #define DHCP_OPTION_MSG_TYPE 53
83 #define DHCP_OPTION_SERVER_ID 54
84 #define DHCP_OPTION_REQ_LIST 55
85 #define DHCP_OPTION_END 255
86 
87 
88 
89 #define LEASE_FLAGS_ALLOCATED 0x01 /* Lease with an allocated address*/
90 #define LEASE_FLAGS_VALID 0x02 /* Contains a valid but
91  possibly outdated lease */
92 
93 
94 static const struct dhcps_config *config;
95 
96 
97 static uint8_t *
98 find_option(uint8_t option)
99 {
100  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
101  uint8_t *optptr = &m->options[4];
102  uint8_t *end = (uint8_t*)uip_appdata + uip_datalen();
103  while(optptr < end && *optptr != DHCP_OPTION_END) {
104  if(*optptr == option) {
105  return optptr;
106  }
107  optptr += optptr[1] + 2;
108  }
109  return NULL;
110 }
111 
112 static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
113 
114 static int
115 check_cookie(void)
116 {
117  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
118  return memcmp(m->options, magic_cookie, 4) == 0;
119 }
120 
121 /* Finds any valid lease for a given MAC address */
122 static struct dhcps_client_lease *
123 lookup_lease_mac(const uint8_t *chaddr, uint8_t hlen)
124 {
125  struct dhcps_client_lease *lease = config->leases;
126  struct dhcps_client_lease *end = config->leases + config->num_leases;
127  while(lease != end) {
128  if (lease->flags & LEASE_FLAGS_VALID
129  && memcmp(lease->chaddr, chaddr, hlen) == 0) {
130  return lease;
131  }
132  lease++;
133  }
134  return NULL;
135 }
136 
137 static struct dhcps_client_lease *
138 lookup_lease_ip(const uip_ipaddr_t *ip)
139 {
140  struct dhcps_client_lease *lease = config->leases;
141  struct dhcps_client_lease *end = config->leases + config->num_leases;
142  while(lease != end) {
143  if (uip_ipaddr_cmp(&lease->ipaddr, ip)) {
144  return lease;
145  }
146  lease++;
147  }
148  return NULL;
149 }
150 
151 static struct dhcps_client_lease *
152 find_free_lease(void)
153 {
154  struct dhcps_client_lease *found = NULL;
155  struct dhcps_client_lease *lease = config->leases;
156  struct dhcps_client_lease *end = config->leases + config->num_leases;
157  while(lease != end) {
158  if (!(lease->flags & LEASE_FLAGS_VALID)) return lease;
159  if (!(lease->flags & LEASE_FLAGS_ALLOCATED)) found = lease;
160  lease++;
161  }
162  return found;
163 }
164 
165 struct dhcps_client_lease *
166 init_lease(struct dhcps_client_lease *lease,
167  const uint8_t *chaddr, uint8_t hlen)
168 {
169  if (lease) {
170  memcpy(lease->chaddr, chaddr, hlen);
171  lease->flags = LEASE_FLAGS_VALID;
172  }
173  return lease;
174 }
175 
176 
177 static struct dhcps_client_lease *
178 choose_address()
179 {
180  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
181  struct dhcps_client_lease *lease;
182  lease = lookup_lease_mac(m->chaddr, m->hlen);
183  if (lease) {
184  return lease;
185  }
186  {
187  uint8_t *opt;
188  opt = find_option(DHCP_OPTION_REQ_IPADDR);
189  if (opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
190  && !(lease->flags & LEASE_FLAGS_ALLOCATED)) {
191  return init_lease(lease, m->chaddr,m->hlen);
192  }
193  }
194  lease = find_free_lease();
195  if (lease) {
196  return init_lease(lease, m->chaddr,m->hlen);
197  }
198  return NULL;
199 }
200 
201 static struct dhcps_client_lease *
202 allocate_address()
203 {
204  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
205  struct dhcps_client_lease *lease;
206  lease = lookup_lease_mac(m->chaddr, m->hlen);
207  if (!lease) {
208  uint8_t *opt;
209  opt = find_option(DHCP_OPTION_REQ_IPADDR);
210  if (!(opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
211  && !(lease->flags & LEASE_FLAGS_ALLOCATED))) {
212  return NULL;
213  }
214  }
215  lease->lease_end = clock_seconds()+config->default_lease_time;
216  lease->flags |= LEASE_FLAGS_ALLOCATED;
217  return lease;
218 }
219 
220 static struct dhcps_client_lease *
221 release_address()
222 {
223  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
224  struct dhcps_client_lease *lease;
225  lease = lookup_lease_mac(m->chaddr, m->hlen);
226  if (!lease) {
227  return NULL;
228  }
229  lease->flags &= ~LEASE_FLAGS_ALLOCATED;
230  return lease;
231 }
232 
233 /*---------------------------------------------------------------------------*/
234 static uint8_t *
235 add_msg_type(uint8_t *optptr, uint8_t type)
236 {
237  *optptr++ = DHCP_OPTION_MSG_TYPE;
238  *optptr++ = 1;
239  *optptr++ = type;
240  return optptr;
241 }
242 /*---------------------------------------------------------------------------*/
243 static uint8_t *
244 add_server_id(uint8_t *optptr)
245 {
246  *optptr++ = DHCP_OPTION_SERVER_ID;
247  *optptr++ = 4;
248  memcpy(optptr, &uip_hostaddr, 4);
249  return optptr + 4;
250 }
251 /*---------------------------------------------------------------------------*/
252 static uint8_t *
253 add_lease_time(uint8_t *optptr)
254 {
255  uint32_t lt;
256  *optptr++ = DHCP_OPTION_LEASE_TIME;
257  *optptr++ = 4;
258  lt = UIP_HTONL(config->default_lease_time);
259  memcpy(optptr, &lt, 4);
260  return optptr + 4;
261 }
262 
263 /*---------------------------------------------------------------------------*/
264 static uint8_t *
265 add_end(uint8_t *optptr)
266 {
267  *optptr++ = DHCP_OPTION_END;
268  return optptr;
269 }
270 
271 static uint8_t *
272 add_config(uint8_t *optptr)
273 {
274  if (config->flags & DHCP_CONF_NETMASK) {
275  *optptr++ = DHCP_OPTION_SUBNET_MASK;
276  *optptr++ = 4;
277  memcpy(optptr, &config->netmask, 4);
278  optptr += 4;
279  }
280  if (config->flags & DHCP_CONF_DNSADDR) {
281  *optptr++ = DHCP_OPTION_DNS_SERVER;
282  *optptr++ = 4;
283  memcpy(optptr, &config->dnsaddr, 4);
284  optptr += 4;
285  }
286  if (config->flags & DHCP_CONF_DEFAULT_ROUTER) {
287  *optptr++ = DHCP_OPTION_ROUTER;
288  *optptr++ = 4;
289  memcpy(optptr, &config->default_router, 4);
290  optptr += 4;
291  }
292  return optptr;
293 }
294 
295 static void
296 create_msg(CC_REGISTER_ARG struct dhcp_msg *m)
297 {
298  m->op = DHCP_REPLY;
299  /* m->htype = DHCP_HTYPE_ETHERNET; */
300 /* m->hlen = DHCP_HLEN_ETHERNET; */
301 /* memcpy(m->chaddr, &uip_lladdr,DHCP_HLEN_ETHERNET); */
302  m->hops = 0;
303  m->secs = 0;
304  memcpy(m->siaddr, &uip_hostaddr, 4);
305  m->sname[0] = '\0';
306  m->file[0] = '\0';
307  memcpy(m->options, magic_cookie, sizeof(magic_cookie));
308 }
309 
310 static uip_ipaddr_t any_addr;
311 static uip_ipaddr_t bcast_addr;
312 
313 /*---------------------------------------------------------------------------*/
314 static void
315 send_offer(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
316 {
317  uint8_t *end;
318  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
319 
320  create_msg(m);
321  memcpy(&m->yiaddr, &lease->ipaddr,4);
322 
323  end = add_msg_type(&m->options[4], DHCPOFFER);
324  end = add_server_id(end);
325  end = add_lease_time(end);
326  end = add_config(end);
327  end = add_end(end);
328  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
329  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
330 }
331 
332 static void
333 send_ack(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
334 {
335  uint8_t *end;
336  uip_ipaddr_t ciaddr;
337  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
338 
339  create_msg(m);
340  memcpy(&m->yiaddr, &lease->ipaddr,4);
341 
342  end = add_msg_type(&m->options[4], DHCPACK);
343  end = add_server_id(end);
344  end = add_lease_time(end);
345  end = add_config(end);
346  end = add_end(end);
347  memcpy(&ciaddr, &lease->ipaddr,4);
348  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
349  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
350  printf("ACK\n");
351 }
352 static void
353 send_nack(struct uip_udp_conn *conn)
354 {
355  uint8_t *end;
356  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
357 
358  create_msg(m);
359  memset(&m->yiaddr, 0, 4);
360 
361  end = add_msg_type(&m->options[4], DHCPNAK);
362  end = add_server_id(end);
363  end = add_end(end);
364 
365  uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
366  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
367  printf("NACK\n");
368 }
369 
370 /*---------------------------------------------------------------------------*/
371 PROCESS(dhcp_server_process, "DHCP server");
372 /*---------------------------------------------------------------------------*/
373 
374 PROCESS_THREAD(dhcp_server_process, ev , data)
375 {
376  static struct uip_udp_conn *conn;
377  static struct uip_udp_conn *send_conn;
378  static struct dhcps_client_lease *lease;
379  PROCESS_BEGIN();
380  printf("DHCP server starting\n");
381  uip_ipaddr(&any_addr, 0,0,0,0);
382  uip_ipaddr(&bcast_addr, 255,255,255,255);
383  conn = udp_new(&any_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
384  if (!conn) goto exit;
385  send_conn = udp_new(&bcast_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
386  if (!send_conn) goto exit;
387 
388  uip_udp_bind(conn, UIP_HTONS(DHCPS_SERVER_PORT));
389  uip_udp_bind(send_conn, UIP_HTONS(DHCPS_SERVER_PORT));
390  while(1) {
392  if(ev == tcpip_event) {
393  if (uip_newdata()) {
394  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
395  struct uip_udpip_hdr *header = (struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN];
396 
397  if (m->op == DHCP_REQUEST && check_cookie() && m->hlen <= MAX_HLEN) {
398  uint8_t *opt = find_option(DHCP_OPTION_MSG_TYPE);
399  if (opt) {
400  uint8_t mtype = opt[2];
401  if (opt[2] == DHCPDISCOVER) {
402  printf("Discover\n");
403  lease = choose_address();
404  if (lease) {
405  lease->lease_end = clock_seconds()+config->default_lease_time;
406  tcpip_poll_udp(send_conn);
408  send_offer(conn,lease);
409  }
410  } else {
411  uint8_t *opt = find_option(DHCP_OPTION_SERVER_ID);
412  if (!opt || uip_ipaddr_cmp((uip_ipaddr_t*)&opt[2], &uip_hostaddr)) {
413  if (mtype == DHCPREQUEST) {
414  printf("Request\n");
415  lease = allocate_address();
416  tcpip_poll_udp(send_conn);
418  if (!lease) {
419  send_nack(send_conn);
420  } else {
421  send_ack(send_conn,lease);
422  }
423  } else if (mtype == DHCPRELEASE) {
424  printf("Release\n");
425  release_address();
426  } else if (mtype == DHCPDECLINE) {
427  printf("Decline\n");
428  } else if (mtype == DHCPINFORM) {
429  printf("Inform\n");
430  }
431  }
432  }
433  }
434  }
435  }
436  } else if (uip_poll()) {
437 
438  }
439  }
440  exit:
441  printf("DHCP server exiting\n");
442  PROCESS_END();
443 }
444 
445 void
446 dhcps_init(const struct dhcps_config *conf)
447 {
448  config = conf;
449  process_start(&dhcp_server_process,NULL);
450 }
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
CCIF struct uip_udp_conn * udp_new(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Create a new UDP connection.
CCIF void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip6.c:2310
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:738
#define uip_udp_bind(conn, port)
Bind a UDP connection to a local port.
Definition: uip.h:888
#define NULL
The null pointer.
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:817
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1238
CCIF void tcpip_poll_udp(struct uip_udp_conn *conn)
Cause a specified UDP connection to be polled.
uip_ipaddr_t ripaddr
The IP address of the remote peer.
Definition: uip.h:1395
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define UIP_LLH_LEN
The link level header length.
Definition: uipopt.h:160
Macros and definitions for the ARP module.
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition: uip.h:1026
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
Definition: process.h:157
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
CCIF unsigned long clock_seconds(void)
Get the current value of the platform seconds.
Definition: clock.c:57
#define CC_REGISTER_ARG
Configure if the C compiler supports the &quot;register&quot; keyword for function arguments.
Definition: cc.h:57
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:651
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:75
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
Representation of a uIP UDP connection.
Definition: uip.h:1394
#define uip_ipaddr(addr, addr0, addr1, addr2, addr3)
Construct an IP address from four bytes.
Definition: uip.h:955
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74