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