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