Contiki 3.x
dhcpc.c
1 /*
2  * Copyright (c) 2005, Swedish Institute of Computer Science
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 #include <stdio.h>
34 #include <string.h>
35 
36 #include "contiki.h"
37 #include "contiki-net.h"
38 #include "net/ip/dhcpc.h"
39 
40 #define STATE_INITIAL 0
41 #define STATE_SENDING 1
42 #define STATE_OFFER_RECEIVED 2
43 #define STATE_CONFIG_RECEIVED 3
44 
45 static struct dhcpc_state s;
46 
47 struct dhcp_msg {
48  uint8_t op, htype, hlen, hops;
49  uint8_t xid[4];
50  uint16_t secs, flags;
51  uint8_t ciaddr[4];
52  uint8_t yiaddr[4];
53  uint8_t siaddr[4];
54  uint8_t giaddr[4];
55  uint8_t chaddr[16];
56 #ifndef UIP_CONF_DHCP_LIGHT
57  uint8_t sname[64];
58  uint8_t file[128];
59 #endif
60  uint8_t options[312];
61 };
62 
63 #define BOOTP_BROADCAST 0x8000
64 
65 #define DHCP_REQUEST 1
66 #define DHCP_REPLY 2
67 #define DHCP_HTYPE_ETHERNET 1
68 #define DHCP_HLEN_ETHERNET 6
69 #define DHCP_MSG_LEN 236
70 
71 #define DHCPC_SERVER_PORT 67
72 #define DHCPC_CLIENT_PORT 68
73 
74 #define DHCPDISCOVER 1
75 #define DHCPOFFER 2
76 #define DHCPREQUEST 3
77 #define DHCPDECLINE 4
78 #define DHCPACK 5
79 #define DHCPNAK 6
80 #define DHCPRELEASE 7
81 
82 #define DHCP_OPTION_SUBNET_MASK 1
83 #define DHCP_OPTION_ROUTER 3
84 #define DHCP_OPTION_DNS_SERVER 6
85 #define DHCP_OPTION_REQ_IPADDR 50
86 #define DHCP_OPTION_LEASE_TIME 51
87 #define DHCP_OPTION_MSG_TYPE 53
88 #define DHCP_OPTION_SERVER_ID 54
89 #define DHCP_OPTION_REQ_LIST 55
90 #define DHCP_OPTION_END 255
91 
92 static uint32_t xid;
93 static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
94 /*---------------------------------------------------------------------------*/
95 static uint8_t *
96 add_msg_type(uint8_t *optptr, uint8_t type)
97 {
98  *optptr++ = DHCP_OPTION_MSG_TYPE;
99  *optptr++ = 1;
100  *optptr++ = type;
101  return optptr;
102 }
103 /*---------------------------------------------------------------------------*/
104 static uint8_t *
105 add_server_id(uint8_t *optptr)
106 {
107  *optptr++ = DHCP_OPTION_SERVER_ID;
108  *optptr++ = 4;
109  memcpy(optptr, s.serverid, 4);
110  return optptr + 4;
111 }
112 /*---------------------------------------------------------------------------*/
113 static uint8_t *
114 add_req_ipaddr(uint8_t *optptr)
115 {
116  *optptr++ = DHCP_OPTION_REQ_IPADDR;
117  *optptr++ = 4;
118  memcpy(optptr, s.ipaddr.u16, 4);
119  return optptr + 4;
120 }
121 /*---------------------------------------------------------------------------*/
122 static uint8_t *
123 add_req_options(uint8_t *optptr)
124 {
125  *optptr++ = DHCP_OPTION_REQ_LIST;
126  *optptr++ = 3;
127  *optptr++ = DHCP_OPTION_SUBNET_MASK;
128  *optptr++ = DHCP_OPTION_ROUTER;
129  *optptr++ = DHCP_OPTION_DNS_SERVER;
130  return optptr;
131 }
132 /*---------------------------------------------------------------------------*/
133 static uint8_t *
134 add_end(uint8_t *optptr)
135 {
136  *optptr++ = DHCP_OPTION_END;
137  return optptr;
138 }
139 /*---------------------------------------------------------------------------*/
140 static void
141 create_msg(CC_REGISTER_ARG struct dhcp_msg *m)
142 {
143  m->op = DHCP_REQUEST;
144  m->htype = DHCP_HTYPE_ETHERNET;
145  m->hlen = s.mac_len;
146  m->hops = 0;
147  memcpy(m->xid, &xid, sizeof(m->xid));
148  m->secs = 0;
149  m->flags = UIP_HTONS(BOOTP_BROADCAST); /* Broadcast bit. */
150  /* uip_ipaddr_copy(m->ciaddr, uip_hostaddr);*/
151  memcpy(m->ciaddr, uip_hostaddr.u16, sizeof(m->ciaddr));
152  memset(m->yiaddr, 0, sizeof(m->yiaddr));
153  memset(m->siaddr, 0, sizeof(m->siaddr));
154  memset(m->giaddr, 0, sizeof(m->giaddr));
155  memcpy(m->chaddr, s.mac_addr, s.mac_len);
156  memset(&m->chaddr[s.mac_len], 0, sizeof(m->chaddr) - s.mac_len);
157 #ifndef UIP_CONF_DHCP_LIGHT
158  memset(m->sname, 0, sizeof(m->sname));
159  memset(m->file, 0, sizeof(m->file));
160 #endif
161 
162  memcpy(m->options, magic_cookie, sizeof(magic_cookie));
163 }
164 /*---------------------------------------------------------------------------*/
165 static void
166 send_discover(void)
167 {
168  uint8_t *end;
169  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
170 
171  create_msg(m);
172 
173  end = add_msg_type(&m->options[4], DHCPDISCOVER);
174  end = add_req_options(end);
175  end = add_end(end);
176 
177  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
178 }
179 /*---------------------------------------------------------------------------*/
180 static void
181 send_request(void)
182 {
183  uint8_t *end;
184  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
185 
186  create_msg(m);
187 
188  end = add_msg_type(&m->options[4], DHCPREQUEST);
189  end = add_server_id(end);
190  end = add_req_ipaddr(end);
191  end = add_end(end);
192 
193  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
194 }
195 /*---------------------------------------------------------------------------*/
196 static uint8_t
197 parse_options(uint8_t *optptr, int len)
198 {
199  uint8_t *end = optptr + len;
200  uint8_t type = 0;
201 
202  while(optptr < end) {
203  switch(*optptr) {
204  case DHCP_OPTION_SUBNET_MASK:
205  memcpy(s.netmask.u16, optptr + 2, 4);
206  break;
207  case DHCP_OPTION_ROUTER:
208  memcpy(s.default_router.u16, optptr + 2, 4);
209  break;
210  case DHCP_OPTION_DNS_SERVER:
211  memcpy(s.dnsaddr.u16, optptr + 2, 4);
212  break;
213  case DHCP_OPTION_MSG_TYPE:
214  type = *(optptr + 2);
215  break;
216  case DHCP_OPTION_SERVER_ID:
217  memcpy(s.serverid, optptr + 2, 4);
218  break;
219  case DHCP_OPTION_LEASE_TIME:
220  memcpy(s.lease_time, optptr + 2, 4);
221  break;
222  case DHCP_OPTION_END:
223  return type;
224  }
225 
226  optptr += optptr[1] + 2;
227  }
228  return type;
229 }
230 /*---------------------------------------------------------------------------*/
231 static uint8_t
232 parse_msg(void)
233 {
234  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
235 
236  if(m->op == DHCP_REPLY &&
237  memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
238  memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
239  memcpy(s.ipaddr.u16, m->yiaddr, 4);
240  return parse_options(&m->options[4], uip_datalen());
241  }
242  return 0;
243 }
244 /*---------------------------------------------------------------------------*/
245 /*
246  * Is this a "fresh" reply for me? If it is, return the type.
247  */
248 static int
249 msg_for_me(void)
250 {
251  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
252  uint8_t *optptr = &m->options[4];
253  uint8_t *end = (uint8_t*)uip_appdata + uip_datalen();
254 
255  if(m->op == DHCP_REPLY &&
256  memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
257  memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
258  while(optptr < end) {
259  if(*optptr == DHCP_OPTION_MSG_TYPE) {
260  return *(optptr + 2);
261  } else if (*optptr == DHCP_OPTION_END) {
262  return -1;
263  }
264  optptr += optptr[1] + 2;
265  }
266  }
267  return -1;
268 }
269 /*---------------------------------------------------------------------------*/
270 static
271 PT_THREAD(handle_dhcp(process_event_t ev, void *data))
272 {
273  clock_time_t ticks;
274 
275  PT_BEGIN(&s.pt);
276 
277  init:
278  xid++;
279  s.state = STATE_SENDING;
280  s.ticks = CLOCK_SECOND;
281  while (1) {
282  while(ev != tcpip_event) {
283  tcpip_poll_udp(s.conn);
284  PT_YIELD(&s.pt);
285  }
286  send_discover();
287  etimer_set(&s.etimer, s.ticks);
288  do {
289  PT_YIELD(&s.pt);
290  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPOFFER) {
291  parse_msg();
292  s.state = STATE_OFFER_RECEIVED;
293  goto selecting;
294  }
295  } while (!etimer_expired(&s.etimer));
296 
297  if(s.ticks < CLOCK_SECOND * 60) {
298  s.ticks *= 2;
299  }
300  }
301 
302  selecting:
303  xid++;
304  s.ticks = CLOCK_SECOND;
305  do {
306  while(ev != tcpip_event) {
307  tcpip_poll_udp(s.conn);
308  PT_YIELD(&s.pt);
309  }
310  send_request();
311  etimer_set(&s.etimer, s.ticks);
312  do {
313  PT_YIELD(&s.pt);
314  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
315  parse_msg();
316  s.state = STATE_CONFIG_RECEIVED;
317  goto bound;
318  }
319  } while (!etimer_expired(&s.etimer));
320 
321  if(s.ticks <= CLOCK_SECOND * 10) {
322  s.ticks += CLOCK_SECOND;
323  } else {
324  goto init;
325  }
326  } while(s.state != STATE_CONFIG_RECEIVED);
327 
328  bound:
329 #if 0
330  printf("Got IP address %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.ipaddr));
331  printf("Got netmask %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.netmask));
332  printf("Got DNS server %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.dnsaddr));
333  printf("Got default router %d.%d.%d.%d\n",
334  uip_ipaddr_to_quad(&s.default_router));
335  printf("Lease expires in %ld seconds\n",
336  uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]));
337 #endif
338 
339  dhcpc_configured(&s);
340 
341 #define MAX_TICKS (~((clock_time_t)0) / 2)
342 #define MAX_TICKS32 (~((uint32_t)0))
343 #define IMIN(a, b) ((a) < (b) ? (a) : (b))
344 
345  if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
346  <= MAX_TICKS32) {
347  s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
348  )*CLOCK_SECOND/2;
349  } else {
350  s.ticks = MAX_TICKS32;
351  }
352 
353  while(s.ticks > 0) {
354  ticks = IMIN(s.ticks, MAX_TICKS);
355  s.ticks -= ticks;
356  etimer_set(&s.etimer, ticks);
357  PT_YIELD_UNTIL(&s.pt, etimer_expired(&s.etimer));
358  }
359 
360  if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
361  <= MAX_TICKS32) {
362  s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
363  )*CLOCK_SECOND/2;
364  } else {
365  s.ticks = MAX_TICKS32;
366  }
367 
368  /* renewing: */
369  xid++;
370  do {
371  while(ev != tcpip_event) {
372  tcpip_poll_udp(s.conn);
373  PT_YIELD(&s.pt);
374  }
375  send_request();
376  ticks = IMIN(s.ticks / 2, MAX_TICKS);
377  s.ticks -= ticks;
378  etimer_set(&s.etimer, ticks);
379  do {
380  PT_YIELD(&s.pt);
381  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
382  parse_msg();
383  goto bound;
384  }
385  } while(!etimer_expired(&s.etimer));
386  } while(s.ticks >= CLOCK_SECOND*3);
387 
388  /* rebinding: */
389 
390  /* lease_expired: */
391  dhcpc_unconfigured(&s);
392  goto init;
393 
394  PT_END(&s.pt);
395 }
396 /*---------------------------------------------------------------------------*/
397 void
398 dhcpc_init(const void *mac_addr, int mac_len)
399 {
400  uip_ipaddr_t addr;
401 
402  s.mac_addr = mac_addr;
403  s.mac_len = mac_len;
404 
405  s.state = STATE_INITIAL;
406  uip_ipaddr(&addr, 255,255,255,255);
407  s.conn = udp_new(&addr, UIP_HTONS(DHCPC_SERVER_PORT), NULL);
408  if(s.conn != NULL) {
409  udp_bind(s.conn, UIP_HTONS(DHCPC_CLIENT_PORT));
410  }
411  PT_INIT(&s.pt);
412 }
413 /*---------------------------------------------------------------------------*/
414 void
415 dhcpc_appcall(process_event_t ev, void *data)
416 {
417  if(ev == tcpip_event || ev == PROCESS_EVENT_TIMER) {
418  handle_dhcp(ev, data);
419  }
420 }
421 /*---------------------------------------------------------------------------*/
422 void
423 dhcpc_request(void)
424 {
425  uip_ipaddr_t ipaddr;
426 
427  if(s.state == STATE_INITIAL) {
428  uip_ipaddr(&ipaddr, 0,0,0,0);
429  uip_sethostaddr(&ipaddr);
430  handle_dhcp(PROCESS_EVENT_NONE, NULL);
431  }
432 }
433 /*---------------------------------------------------------------------------*/
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition: etimer.c:205
#define uip_sethostaddr(addr)
Set the IP address of this host.
Definition: uip.h:195
#define PT_YIELD(pt)
Yield from the current protothread.
Definition: pt.h:289
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 NULL
The null pointer.
#define PT_INIT(pt)
Initialize a protothread.
Definition: pt.h:79
#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.
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
#define uip_ipaddr_to_quad(a)
Convert an IP address to four bytes separated by commas.
Definition: uip.h:927
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
#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
#define PT_YIELD_UNTIL(pt, cond)
Yield from the protothread until a condition occurs.
Definition: pt.h:309
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:75
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition: etimer.c:177
#define udp_bind(conn, port)
Bind a UDP connection to a local port.
Definition: tcpip.h:262
#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
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82