Contiki-NG
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/ip64-dhcpc.h"
40 
41 #include "ipv6/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, 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  memset(m->file, 0, sizeof(m->file));
167 
168 
169  memcpy(m->options, magic_cookie, sizeof(magic_cookie));
170 }
171 /*---------------------------------------------------------------------------*/
172 static void
173 send_discover(void)
174 {
175  uint8_t *end;
176  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
177 
178  create_msg(m);
179 
180  end = add_msg_type(&m->options[4], DHCPDISCOVER);
181  end = add_req_options(end);
182  end = add_end(end);
183 
184  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
185 }
186 /*---------------------------------------------------------------------------*/
187 static void
188 send_request(void)
189 {
190  uint8_t *end;
191  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
192 
193  create_msg(m);
194 
195  end = add_msg_type(&m->options[4], DHCPREQUEST);
196  end = add_server_id(end);
197  end = add_req_ipaddr(end);
198  end = add_end(end);
199 
200  uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
201 }
202 /*---------------------------------------------------------------------------*/
203 static uint8_t
204 parse_options(uint8_t *optptr, int len)
205 {
206  uint8_t *end = optptr + len;
207  uint8_t type = 0;
208 
209  while(optptr < end) {
210  switch(*optptr) {
211  case DHCP_OPTION_SUBNET_MASK:
212  memcpy(s.netmask.u16, optptr + 2, 4);
213  break;
214  case DHCP_OPTION_ROUTER:
215  memcpy(s.default_router.u16, optptr + 2, 4);
216  break;
217  case DHCP_OPTION_DNS_SERVER:
218  memcpy(s.dnsaddr.u16, optptr + 2, 4);
219  break;
220  case DHCP_OPTION_MSG_TYPE:
221  type = *(optptr + 2);
222  break;
223  case DHCP_OPTION_SERVER_ID:
224  memcpy(s.serverid, optptr + 2, 4);
225  break;
226  case DHCP_OPTION_LEASE_TIME:
227  memcpy(s.lease_time, optptr + 2, 4);
228  break;
229  case DHCP_OPTION_END:
230  return type;
231  }
232 
233  optptr += optptr[1] + 2;
234  }
235  return type;
236 }
237 /*---------------------------------------------------------------------------*/
238 static uint8_t
239 parse_msg(void)
240 {
241  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
242 
243  if(m->op == DHCP_REPLY &&
244  memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
245  memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
246  memcpy(s.ipaddr.u16, m->yiaddr, 4);
247  return parse_options(&m->options[4], uip_datalen());
248  }
249  return 0;
250 }
251 /*---------------------------------------------------------------------------*/
252 /*
253  * Is this a "fresh" reply for me? If it is, return the type.
254  */
255 static int
256 msg_for_me(void)
257 {
258  struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
259  uint8_t *optptr = &m->options[4];
260  uint8_t *end = (uint8_t*)uip_appdata + uip_datalen();
261 
262  if(m->op == DHCP_REPLY &&
263  memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
264  memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
265  while(optptr < end) {
266  if(*optptr == DHCP_OPTION_MSG_TYPE) {
267  return *(optptr + 2);
268  } else if (*optptr == DHCP_OPTION_END) {
269  return -1;
270  }
271  optptr += optptr[1] + 2;
272  }
273  }
274  return -1;
275 }
276 /*---------------------------------------------------------------------------*/
277 static
278 PT_THREAD(handle_dhcp(process_event_t ev, void *data))
279 {
280  clock_time_t ticks;
281 
282  PT_BEGIN(&s.pt);
283 
284  init:
285  xid++;
286  s.state = STATE_SENDING;
287  s.ticks = CLOCK_SECOND * 4;
288  while(1) {
289  while(ev != tcpip_event) {
290  tcpip_poll_udp(s.conn);
291  PT_YIELD(&s.pt);
292  }
293  send_discover();
294  etimer_set(&s.etimer, s.ticks);
295  do {
296  PT_YIELD(&s.pt);
297  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPOFFER) {
298  parse_msg();
299  s.state = STATE_OFFER_RECEIVED;
300  goto selecting;
301  }
302  } while(!etimer_expired(&s.etimer));
303 
304  if(s.ticks < CLOCK_SECOND * 60) {
305  s.ticks *= 2;
306  }
307  }
308 
309  selecting:
310  s.ticks = CLOCK_SECOND;
311  do {
312  while(ev != tcpip_event) {
313  tcpip_poll_udp(s.conn);
314  PT_YIELD(&s.pt);
315  }
316  send_request();
317  etimer_set(&s.etimer, s.ticks);
318  do {
319  PT_YIELD(&s.pt);
320  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
321  parse_msg();
322  s.state = STATE_CONFIG_RECEIVED;
323  goto bound;
324  }
325  } while (!etimer_expired(&s.etimer));
326 
327  if(s.ticks <= CLOCK_SECOND * 10) {
328  s.ticks += CLOCK_SECOND;
329  } else {
330  goto init;
331  }
332  } while(s.state != STATE_CONFIG_RECEIVED);
333 
334  bound:
335 #if 0
336  printf("Got IP address %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.ipaddr));
337  printf("Got netmask %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.netmask));
338  printf("Got DNS server %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.dnsaddr));
339  printf("Got default router %d.%d.%d.%d\n",
340  uip_ipaddr_to_quad(&s.default_router));
341  printf("Lease expires in %ld seconds\n",
342  uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]));
343 #endif
344 
345  ip64_dhcpc_configured(&s);
346 
347 #define MAX_TICKS (~((clock_time_t)0) / 2)
348 #define MAX_TICKS32 (~((uint32_t)0))
349 #define IMIN(a, b) ((a) < (b) ? (a) : (b))
350 
351  if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
352  <= MAX_TICKS32) {
353  s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
354  )*CLOCK_SECOND/2;
355  } else {
356  s.ticks = MAX_TICKS32;
357  }
358 
359  while(s.ticks > 0) {
360  ticks = IMIN(s.ticks, MAX_TICKS);
361  s.ticks -= ticks;
362  etimer_set(&s.etimer, ticks);
363  PT_YIELD_UNTIL(&s.pt, etimer_expired(&s.etimer));
364  }
365 
366  if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
367  <= MAX_TICKS32) {
368  s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
369  )*CLOCK_SECOND/2;
370  } else {
371  s.ticks = MAX_TICKS32;
372  }
373 
374  /* renewing: */
375  do {
376  while(ev != tcpip_event) {
377  tcpip_poll_udp(s.conn);
378  PT_YIELD(&s.pt);
379  }
380  send_request();
381  ticks = IMIN(s.ticks / 2, MAX_TICKS);
382  s.ticks -= ticks;
383  etimer_set(&s.etimer, ticks);
384  do {
385  PT_YIELD(&s.pt);
386  if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
387  parse_msg();
388  goto bound;
389  }
390  } while(!etimer_expired(&s.etimer));
391  } while(s.ticks >= CLOCK_SECOND*3);
392 
393  /* rebinding: */
394 
395  /* lease_expired: */
396  ip64_dhcpc_unconfigured(&s);
397  goto init;
398 
399  PT_END(&s.pt);
400 }
401 /*---------------------------------------------------------------------------*/
402 void
403 ip64_dhcpc_init(const void *mac_addr, int mac_len)
404 {
405  /* Although this is DHCPv4, we explicitly bind the socket to an IPv6
406  address so that it can operate over the ip64 bridge. */
407  uip_ip6addr_t v6addr;
408  uip_ip4addr_t v4addr;
409  struct uip_udp_conn *conn2;
410 
411  s.mac_addr = mac_addr;
412  s.mac_len = mac_len;
413 
414  s.state = STATE_INITIAL;
415  uip_ipaddr(&v4addr, 255,255,255,255);
416  ip64_addr_4to6(&v4addr, &v6addr);
417  s.conn = udp_new(&v6addr, UIP_HTONS(IP64_DHCPC_SERVER_PORT), NULL);
418  conn2 = udp_new(NULL, UIP_HTONS(IP64_DHCPC_SERVER_PORT), NULL);
419  if(s.conn != NULL) {
420  udp_bind(s.conn, UIP_HTONS(IP64_DHCPC_CLIENT_PORT));
421  }
422  if(conn2 != NULL) {
423  udp_bind(conn2, UIP_HTONS(IP64_DHCPC_CLIENT_PORT));
424  }
425  PT_INIT(&s.pt);
426 }
427 /*---------------------------------------------------------------------------*/
428 void
429 ip64_dhcpc_appcall(process_event_t ev, void *data)
430 {
431  if(ev == tcpip_event || ev == PROCESS_EVENT_TIMER) {
432  handle_dhcp(ev, data);
433  }
434 }
435 /*---------------------------------------------------------------------------*/
436 void
437 ip64_dhcpc_request(void)
438 {
439  uip_ipaddr_t ipaddr;
440 
441  if(s.state == STATE_INITIAL) {
442  uip_ipaddr(&ipaddr, 0,0,0,0);
443  uip_sethostaddr(&ipaddr);
444  handle_dhcp(PROCESS_EVENT_NONE, NULL);
445  }
446 }
447 /*---------------------------------------------------------------------------*/
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition: uip-nd6.c:116
void tcpip_poll_udp(struct uip_udp_conn *conn)
Cause a specified UDP connection to be polled.
Definition: tcpip.c:750
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:62
#define uip_ipaddr_to_quad(a)
Convert an IP address to four bytes separated by commas.
Definition: uip.h:916
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
Representation of an IP address.
Definition: uip.h:95
#define CC_REGISTER_ARG
Configure if the C compiler supports the "register" keyword for function arguments.
Definition: cc.h:58
#define uip_ipaddr(addr, addr0, addr1, addr2, addr3)
Construct an IP address from four bytes.
Definition: uip.h:944
#define PT_YIELD_UNTIL(pt, cond)
Yield from the protothread until a condition occurs.
Definition: pt.h:309
#define PT_INIT(pt)
Initialize a protothread.
Definition: pt.h:79
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:726
struct uip_udp_conn * udp_new(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Create a new UDP connection.
Definition: tcpip.c:261
#define PT_YIELD(pt)
Yield from the current protothread.
Definition: pt.h:289
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition: etimer.c:213
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1223
void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip6.c:2350
#define uip_sethostaddr(addr)
Set the IP address of this host.
Definition: uip.h:197
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:261
void * uip_appdata
Pointer to the application data in the packet buffer.
Definition: uip6.c:148
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:639
Representation of a uIP UDP connection.
Definition: uip.h:1375