Contiki 2.5
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  * @(#)$Id: dhcpc.c,v 1.9 2010/10/19 18:29:04 adamdunkels Exp $
32  */
33 
34 #include <stdio.h>
35 #include <string.h>
36 
37 #include "contiki.h"
38 #include "contiki-net.h"
39 #include "net/dhcpc.h"
40 
41 #define STATE_INITIAL 0
42 #define STATE_SENDING 1
43 #define STATE_OFFER_RECEIVED 2
44 #define STATE_CONFIG_RECEIVED 3
45 
46 static struct dhcpc_state s;
47 
48 struct dhcp_msg {
49  u8_t op, htype, hlen, hops;
50  u8_t xid[4];
51  u16_t secs, flags;
52  u8_t ciaddr[4];
53  u8_t yiaddr[4];
54  u8_t siaddr[4];
55  u8_t giaddr[4];
56  u8_t chaddr[16];
57 #ifndef UIP_CONF_DHCP_LIGHT
58  u8_t sname[64];
59  u8_t file[128];
60 #endif
61  u8_t options[312];
62 };
63 
64 #define BOOTP_BROADCAST 0x8000
65 
66 #define DHCP_REQUEST 1
67 #define DHCP_REPLY 2
68 #define DHCP_HTYPE_ETHERNET 1
69 #define DHCP_HLEN_ETHERNET 6
70 #define DHCP_MSG_LEN 236
71 
72 #define DHCPC_SERVER_PORT 67
73 #define DHCPC_CLIENT_PORT 68
74 
75 #define DHCPDISCOVER 1
76 #define DHCPOFFER 2
77 #define DHCPREQUEST 3
78 #define DHCPDECLINE 4
79 #define DHCPACK 5
80 #define DHCPNAK 6
81 #define DHCPRELEASE 7
82 
83 #define DHCP_OPTION_SUBNET_MASK 1
84 #define DHCP_OPTION_ROUTER 3
85 #define DHCP_OPTION_DNS_SERVER 6
86 #define DHCP_OPTION_REQ_IPADDR 50
87 #define DHCP_OPTION_LEASE_TIME 51
88 #define DHCP_OPTION_MSG_TYPE 53
89 #define DHCP_OPTION_SERVER_ID 54
90 #define DHCP_OPTION_REQ_LIST 55
91 #define DHCP_OPTION_END 255
92 
93 static u32_t xid;
94 static const u8_t magic_cookie[4] = {99, 130, 83, 99};
95 /*---------------------------------------------------------------------------*/
96 static u8_t *
97 add_msg_type(u8_t *optptr, u8_t type)
98 {
99  *optptr++ = DHCP_OPTION_MSG_TYPE;
100  *optptr++ = 1;
101  *optptr++ = type;
102  return optptr;
103 }
104 /*---------------------------------------------------------------------------*/
105 static u8_t *
106 add_server_id(u8_t *optptr)
107 {
108  *optptr++ = DHCP_OPTION_SERVER_ID;
109  *optptr++ = 4;
110  memcpy(optptr, s.serverid, 4);
111  return optptr + 4;
112 }
113 /*---------------------------------------------------------------------------*/
114 static u8_t *
115 add_req_ipaddr(u8_t *optptr)
116 {
117  *optptr++ = DHCP_OPTION_REQ_IPADDR;
118  *optptr++ = 4;
119  memcpy(optptr, s.ipaddr.u16, 4);
120  return optptr + 4;
121 }
122 /*---------------------------------------------------------------------------*/
123 static u8_t *
124 add_req_options(u8_t *optptr)
125 {
126  *optptr++ = DHCP_OPTION_REQ_LIST;
127  *optptr++ = 3;
128  *optptr++ = DHCP_OPTION_SUBNET_MASK;
129  *optptr++ = DHCP_OPTION_ROUTER;
130  *optptr++ = DHCP_OPTION_DNS_SERVER;
131  return optptr;
132 }
133 /*---------------------------------------------------------------------------*/
134 static u8_t *
135 add_end(u8_t *optptr)
136 {
137  *optptr++ = DHCP_OPTION_END;
138  return optptr;
139 }
140 /*---------------------------------------------------------------------------*/
141 static void
142 create_msg(CC_REGISTER_ARG struct dhcp_msg *m)
143 {
144  m->op = DHCP_REQUEST;
145  m->htype = DHCP_HTYPE_ETHERNET;
146  m->hlen = s.mac_len;
147  m->hops = 0;
148  memcpy(m->xid, &xid, sizeof(m->xid));
149  m->secs = 0;
150  m->flags = UIP_HTONS(BOOTP_BROADCAST); /* Broadcast bit. */
151  /* uip_ipaddr_copy(m->ciaddr, uip_hostaddr);*/
152  memcpy(m->ciaddr, uip_hostaddr.u16, sizeof(m->ciaddr));
153  memset(m->yiaddr, 0, sizeof(m->yiaddr));
154  memset(m->siaddr, 0, sizeof(m->siaddr));
155  memset(m->giaddr, 0, sizeof(m->giaddr));
156  memcpy(m->chaddr, s.mac_addr, s.mac_len);
157  memset(&m->chaddr[s.mac_len], 0, sizeof(m->chaddr) - s.mac_len);
158 #ifndef UIP_CONF_DHCP_LIGHT
159  memset(m->sname, 0, sizeof(m->sname));
160  memset(m->file, 0, sizeof(m->file));
161 #endif
162 
163  memcpy(m->options, magic_cookie, sizeof(magic_cookie));
164 }
165 /*---------------------------------------------------------------------------*/
166 static void
167 send_discover(void)
168 {
169  u8_t *end;
170  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
171 
172  create_msg(m);
173 
174  end = add_msg_type(&m->options[4], DHCPDISCOVER);
175  end = add_req_options(end);
176  end = add_end(end);
177 
178  uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata));
179 }
180 /*---------------------------------------------------------------------------*/
181 static void
182 send_request(void)
183 {
184  u8_t *end;
185  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
186 
187  create_msg(m);
188 
189  end = add_msg_type(&m->options[4], DHCPREQUEST);
190  end = add_server_id(end);
191  end = add_req_ipaddr(end);
192  end = add_end(end);
193 
194  uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata));
195 }
196 /*---------------------------------------------------------------------------*/
197 static u8_t
198 parse_options(u8_t *optptr, int len)
199 {
200  u8_t *end = optptr + len;
201  u8_t type = 0;
202 
203  while(optptr < end) {
204  switch(*optptr) {
205  case DHCP_OPTION_SUBNET_MASK:
206  memcpy(s.netmask.u16, optptr + 2, 4);
207  break;
208  case DHCP_OPTION_ROUTER:
209  memcpy(s.default_router.u16, optptr + 2, 4);
210  break;
211  case DHCP_OPTION_DNS_SERVER:
212  memcpy(s.dnsaddr.u16, optptr + 2, 4);
213  break;
214  case DHCP_OPTION_MSG_TYPE:
215  type = *(optptr + 2);
216  break;
217  case DHCP_OPTION_SERVER_ID:
218  memcpy(s.serverid, optptr + 2, 4);
219  break;
220  case DHCP_OPTION_LEASE_TIME:
221  memcpy(s.lease_time, optptr + 2, 4);
222  break;
223  case DHCP_OPTION_END:
224  return type;
225  }
226 
227  optptr += optptr[1] + 2;
228  }
229  return type;
230 }
231 /*---------------------------------------------------------------------------*/
232 static u8_t
233 parse_msg(void)
234 {
235  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
236 
237  if(m->op == DHCP_REPLY &&
238  memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
239  memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
240  memcpy(s.ipaddr.u16, m->yiaddr, 4);
241  return parse_options(&m->options[4], uip_datalen());
242  }
243  return 0;
244 }
245 /*---------------------------------------------------------------------------*/
246 /*
247  * Is this a "fresh" reply for me? If it is, return the type.
248  */
249 static int
250 msg_for_me(void)
251 {
252  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
253  u8_t *optptr = &m->options[4];
254  u8_t *end = (u8_t*)uip_appdata + uip_datalen();
255 
256  if(m->op == DHCP_REPLY &&
257  memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
258  memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
259  while(optptr < end) {
260  if(*optptr == DHCP_OPTION_MSG_TYPE) {
261  return *(optptr + 2);
262  } else if (*optptr == DHCP_OPTION_END) {
263  return -1;
264  }
265  optptr += optptr[1] + 2;
266  }
267  }
268  return -1;
269 }
270 /*---------------------------------------------------------------------------*/
271 static
272 PT_THREAD(handle_dhcp(process_event_t ev, void *data))
273 {
274  clock_time_t ticks;
275 
276  PT_BEGIN(&s.pt);
277 
278  init:
279  xid++;
280  s.state = STATE_SENDING;
281  s.ticks = CLOCK_SECOND;
282  while (1) {
283  while(ev != tcpip_event) {
284  tcpip_poll_udp(s.conn);
285  PT_YIELD(&s.pt);
286  }
287  send_discover();
288  etimer_set(&s.etimer, s.ticks);
289  do {
290  PT_YIELD(&s.pt);
291  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPOFFER) {
292  parse_msg();
293  s.state = STATE_OFFER_RECEIVED;
294  goto selecting;
295  }
296  } while (!etimer_expired(&s.etimer));
297 
298  if(s.ticks < CLOCK_SECOND * 60) {
299  s.ticks *= 2;
300  }
301  }
302 
303  selecting:
304  xid++;
305  s.ticks = CLOCK_SECOND;
306  do {
307  while(ev != tcpip_event) {
308  tcpip_poll_udp(s.conn);
309  PT_YIELD(&s.pt);
310  }
311  send_request();
312  etimer_set(&s.etimer, s.ticks);
313  do {
314  PT_YIELD(&s.pt);
315  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
316  parse_msg();
317  s.state = STATE_CONFIG_RECEIVED;
318  goto bound;
319  }
320  } while (!etimer_expired(&s.etimer));
321 
322  if(s.ticks <= CLOCK_SECOND * 10) {
323  s.ticks += CLOCK_SECOND;
324  } else {
325  goto init;
326  }
327  } while(s.state != STATE_CONFIG_RECEIVED);
328 
329  bound:
330 #if 0
331  printf("Got IP address %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.ipaddr));
332  printf("Got netmask %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.netmask));
333  printf("Got DNS server %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.dnsaddr));
334  printf("Got default router %d.%d.%d.%d\n",
335  uip_ipaddr_to_quad(&s.default_router));
336  printf("Lease expires in %ld seconds\n",
337  uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]));
338 #endif
339 
340  dhcpc_configured(&s);
341 
342 #define MAX_TICKS (~((clock_time_t)0) / 2)
343 #define MAX_TICKS32 (~((u32_t)0))
344 #define IMIN(a, b) ((a) < (b) ? (a) : (b))
345 
346  if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
347  <= MAX_TICKS32) {
348  s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
349  )*CLOCK_SECOND/2;
350  } else {
351  s.ticks = MAX_TICKS32;
352  }
353 
354  while(s.ticks > 0) {
355  ticks = IMIN(s.ticks, MAX_TICKS);
356  s.ticks -= ticks;
357  etimer_set(&s.etimer, ticks);
358  PT_YIELD_UNTIL(&s.pt, etimer_expired(&s.etimer));
359  }
360 
361  if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
362  <= MAX_TICKS32) {
363  s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
364  )*CLOCK_SECOND/2;
365  } else {
366  s.ticks = MAX_TICKS32;
367  }
368 
369  /* renewing: */
370  xid++;
371  do {
372  while(ev != tcpip_event) {
373  tcpip_poll_udp(s.conn);
374  PT_YIELD(&s.pt);
375  }
376  send_request();
377  ticks = IMIN(s.ticks / 2, MAX_TICKS);
378  s.ticks -= ticks;
379  etimer_set(&s.etimer, ticks);
380  do {
381  PT_YIELD(&s.pt);
382  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
383  parse_msg();
384  goto bound;
385  }
386  } while(!etimer_expired(&s.etimer));
387  } while(s.ticks >= CLOCK_SECOND*3);
388 
389  /* rebinding: */
390 
391  /* lease_expired: */
392  dhcpc_unconfigured(&s);
393  goto init;
394 
395  PT_END(&s.pt);
396 }
397 /*---------------------------------------------------------------------------*/
398 void
399 dhcpc_init(const void *mac_addr, int mac_len)
400 {
401  uip_ipaddr_t addr;
402 
403  s.mac_addr = mac_addr;
404  s.mac_len = mac_len;
405 
406  s.state = STATE_INITIAL;
407  uip_ipaddr(&addr, 255,255,255,255);
408  s.conn = udp_new(&addr, UIP_HTONS(DHCPC_SERVER_PORT), NULL);
409  if(s.conn != NULL) {
410  udp_bind(s.conn, UIP_HTONS(DHCPC_CLIENT_PORT));
411  }
412  PT_INIT(&s.pt);
413 }
414 /*---------------------------------------------------------------------------*/
415 void
416 dhcpc_appcall(process_event_t ev, void *data)
417 {
418  if(ev == tcpip_event || ev == PROCESS_EVENT_TIMER) {
419  handle_dhcp(ev, data);
420  }
421 }
422 /*---------------------------------------------------------------------------*/
423 void
424 dhcpc_request(void)
425 {
426  uip_ipaddr_t ipaddr;
427 
428  if(s.state == STATE_INITIAL) {
429  uip_ipaddr(&ipaddr, 0,0,0,0);
430  uip_sethostaddr(&ipaddr);
431  handle_dhcp(PROCESS_EVENT_NONE, NULL);
432  }
433 }
434 /*---------------------------------------------------------------------------*/