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
48static struct ip64_dhcpc_state s;
49
50struct 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
99static uint32_t xid;
100static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
101/*---------------------------------------------------------------------------*/
102static uint8_t *
103add_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/*---------------------------------------------------------------------------*/
111static uint8_t *
112add_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/*---------------------------------------------------------------------------*/
120static uint8_t *
121add_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/*---------------------------------------------------------------------------*/
129static uint8_t *
130add_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/*---------------------------------------------------------------------------*/
140static uint8_t *
141add_end(uint8_t *optptr)
142{
143 *optptr++ = DHCP_OPTION_END;
144 return optptr;
145}
146/*---------------------------------------------------------------------------*/
147static void
148create_msg(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/*---------------------------------------------------------------------------*/
172static void
173send_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/*---------------------------------------------------------------------------*/
187static void
188send_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/*---------------------------------------------------------------------------*/
203static uint8_t
204parse_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/*---------------------------------------------------------------------------*/
238static uint8_t
239parse_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 */
255static int
256msg_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/*---------------------------------------------------------------------------*/
277static
278PT_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/*---------------------------------------------------------------------------*/
402void
403ip64_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/*---------------------------------------------------------------------------*/
428void
429ip64_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/*---------------------------------------------------------------------------*/
436void
437ip64_dhcpc_request(void)
438{
439 uip_ipaddr_t ipaddr;
440
441 if(s.state == STATE_INITIAL) {
442 uip_ipaddr(&ipaddr, 0,0,0,0);
444 handle_dhcp(PROCESS_EVENT_NONE, NULL);
445 }
446}
447/*---------------------------------------------------------------------------*/
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition: etimer.c:213
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition: etimer.c:177
#define PT_YIELD(pt)
Yield from the current protothread.
Definition: pt.h:455
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:265
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:280
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:292
#define PT_YIELD_UNTIL(pt, cond)
Yield from the protothread until a condition occurs.
Definition: pt.h:475
#define PT_INIT(pt)
Initialize a protothread.
Definition: pt.h:245
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:62
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 udp_bind(conn, port)
Bind a UDP connection to a local port.
Definition: tcpip.h:261
void tcpip_poll_udp(struct uip_udp_conn *conn)
Cause a specified UDP connection to be polled.
Definition: tcpip.c:750
void * uip_appdata
Pointer to the application data in the packet buffer.
Definition: uip6.c:148
void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip6.c:2359
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer.
Definition: uip.h:593
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:680
#define uip_sethostaddr(addr)
Set the IP address of this host.
Definition: uip.h:197
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1157
#define uip_ipaddr(addr, addr0, addr1, addr2, addr3)
Construct an IP address from four bytes.
Definition: uip.h:898
#define uip_ipaddr_to_quad(a)
Convert an IP address to four bytes separated by commas.
Definition: uip.h:870
Representation of a uIP UDP connection.
Definition: uip.h:1309
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition: uip-nd6.c:116
Representation of an IP address.
Definition: uip.h:95