Merge remote-tracking branch 'upstream/master' into edge
[clinton/Smoothieware.git] / src / libs / Network / uip / dhcpc / 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 uIP TCP/IP stack
30 *
31 * @(#)$Id: dhcpc.c,v 1.2 2006/06/11 21:46:37 adam Exp $
32 */
33
34 #include <stdio.h>
35 #include <string.h>
36
37 #include "uip.h"
38 #include "dhcpc.h"
39 #include "timer.h"
40 #include "pt.h"
41
42 #if UIP_CONF_UDP
43
44 #define STATE_INITIAL 0
45 #define STATE_SENDING 1
46 #define STATE_OFFER_RECEIVED 2
47 #define STATE_CONFIG_RECEIVED 3
48
49 #define ntohl(a) ((((a) >> 24) & 0x000000FF) | (((a) >> 8) & 0x0000FF00) | (((a) << 8) & 0x00FF0000) | (((a) << 24) & 0xFF000000))
50 static struct dhcpc_state s __attribute__ ((section ("AHBSRAM1")));
51 //#define UIP_CONF_DHCP_LIGHT
52
53 struct dhcp_msg {
54 u8_t op, htype, hlen, hops;
55 u8_t xid[4];
56 u16_t secs, flags;
57 u8_t ciaddr[4];
58 u8_t yiaddr[4];
59 u8_t siaddr[4];
60 u8_t giaddr[4];
61 u8_t chaddr[16];
62 #ifndef UIP_CONF_DHCP_LIGHT
63 u8_t sname[64];
64 u8_t file[128];
65 #endif
66 u8_t options[312];
67 };
68
69 #define BOOTP_BROADCAST 0x8000
70
71 #define DHCP_REQUEST 1
72 #define DHCP_REPLY 2
73 #define DHCP_HTYPE_ETHERNET 1
74 #define DHCP_HLEN_ETHERNET 6
75 #define DHCP_MSG_LEN 236
76
77 #define DHCPC_SERVER_PORT 67
78 #define DHCPC_CLIENT_PORT 68
79
80 #define DHCPDISCOVER 1
81 #define DHCPOFFER 2
82 #define DHCPREQUEST 3
83 #define DHCPDECLINE 4
84 #define DHCPACK 5
85 #define DHCPNAK 6
86 #define DHCPRELEASE 7
87
88 #define DHCP_OPTION_SUBNET_MASK 1
89 #define DHCP_OPTION_ROUTER 3
90 #define DHCP_OPTION_DNS_SERVER 6
91 #define DHCP_OPTION_HOSTNAME 12
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= 0x00112233;
100
101 static const u8_t magic_cookie[4] = {99, 130, 83, 99};
102 /*---------------------------------------------------------------------------*/
103 static u8_t *
104 add_msg_type(u8_t *optptr, u8_t type)
105 {
106 *optptr++ = DHCP_OPTION_MSG_TYPE;
107 *optptr++ = 1;
108 *optptr++ = type;
109 return optptr;
110 }
111 /*---------------------------------------------------------------------------*/
112 static u8_t *
113 add_server_id(u8_t *optptr)
114 {
115 *optptr++ = DHCP_OPTION_SERVER_ID;
116 *optptr++ = 4;
117 memcpy(optptr, &s.serverid, 4);
118 return optptr + 4;
119 }
120 /*---------------------------------------------------------------------------*/
121 static u8_t *
122 add_req_ipaddr(u8_t *optptr)
123 {
124 *optptr++ = DHCP_OPTION_REQ_IPADDR;
125 *optptr++ = 4;
126 memcpy(optptr, &s.ipaddr, 4);
127 return optptr + 4;
128 }
129 /*---------------------------------------------------------------------------*/
130 static u8_t *
131 add_hostname(u8_t *optptr)
132 {
133 if (s.hostname == NULL) {
134 return optptr;
135 }
136 const u8_t l = strlen(s.hostname);
137 *optptr++ = DHCP_OPTION_HOSTNAME;
138 *optptr++ = l;
139 memcpy(optptr, s.hostname, l);
140 return optptr + l;
141 }
142 /*---------------------------------------------------------------------------*/
143 static u8_t *
144 add_req_options(u8_t *optptr)
145 {
146 *optptr++ = DHCP_OPTION_REQ_LIST;
147 *optptr++ = s.hostname == NULL ? 3 : 4;
148 *optptr++ = DHCP_OPTION_SUBNET_MASK;
149 *optptr++ = DHCP_OPTION_ROUTER;
150 *optptr++ = DHCP_OPTION_DNS_SERVER;
151 if (s.hostname != NULL) {
152 *optptr++ = DHCP_OPTION_HOSTNAME;
153 }
154 return optptr;
155 }
156 /*---------------------------------------------------------------------------*/
157 static u8_t *
158 add_end(u8_t *optptr)
159 {
160 *optptr++ = DHCP_OPTION_END;
161 return optptr;
162 }
163 /*---------------------------------------------------------------------------*/
164 static void
165 create_msg(register struct dhcp_msg *m, int rea)
166 {
167 m->op = DHCP_REQUEST;
168 m->htype = DHCP_HTYPE_ETHERNET;
169 m->hlen = s.mac_len;
170 m->hops = 0;
171 memcpy(m->xid, &xid, sizeof(m->xid));
172 m->secs = 0;
173 m->flags = HTONS(BOOTP_BROADCAST); /* Broadcast bit. */
174 /* uip_ipaddr_copy(m->ciaddr, uip_hostaddr);*/
175 if(rea == 0 ) memcpy(m->ciaddr, uip_hostaddr, sizeof(m->ciaddr));
176 else memset(m->ciaddr, 0, sizeof(m->ciaddr));
177 memset(m->yiaddr, 0, sizeof(m->yiaddr));
178 memset(m->siaddr, 0, sizeof(m->siaddr));
179 memset(m->giaddr, 0, sizeof(m->giaddr));
180 memcpy(m->chaddr, s.mac_addr, s.mac_len);
181 memset(&m->chaddr[s.mac_len], 0, sizeof(m->chaddr) - s.mac_len);
182 #ifndef UIP_CONF_DHCP_LIGHT
183 memset(m->sname, 0, sizeof(m->sname));
184 memset(m->file, 0, sizeof(m->file));
185 #endif
186
187 memcpy(m->options, magic_cookie, sizeof(magic_cookie));
188 }
189 /*---------------------------------------------------------------------------*/
190 static void
191 send_discover(void)
192 {
193 u8_t *end;
194 struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
195
196 create_msg(m, 0);
197
198 end = add_msg_type(&m->options[4], DHCPDISCOVER);
199 end = add_req_options(end);
200 end = add_end(end);
201
202 uip_send(uip_appdata, end - (u8_t *)uip_appdata);
203 }
204 /*---------------------------------------------------------------------------*/
205 static void
206 send_request(int rea)
207 {
208 u8_t *end;
209 struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
210
211 create_msg(m, rea);
212
213 end = add_msg_type(&m->options[4], DHCPREQUEST);
214 end = add_server_id(end);
215 end = add_req_ipaddr(end);
216 end = add_hostname(end);
217 end = add_end(end);
218
219 uip_send(uip_appdata, end - (u8_t *)uip_appdata);
220 }
221 /*---------------------------------------------------------------------------*/
222 static u8_t
223 parse_options(u8_t *optptr, int len)
224 {
225 u8_t *end = optptr + len;
226 u8_t type = 0;
227
228 while (optptr < end) {
229 switch (*optptr) {
230 case DHCP_OPTION_SUBNET_MASK:
231 memcpy(&s.netmask, optptr + 2, 4);
232 break;
233 case DHCP_OPTION_ROUTER:
234 memcpy(&s.default_router, optptr + 2, 4);
235 break;
236 case DHCP_OPTION_DNS_SERVER:
237 memcpy(&s.dnsaddr, optptr + 2, 4);
238 break;
239 case DHCP_OPTION_MSG_TYPE:
240 type = *(optptr + 2);
241 break;
242 case DHCP_OPTION_SERVER_ID:
243 memcpy(s.serverid, optptr + 2, 4);
244 break;
245 case DHCP_OPTION_LEASE_TIME:
246 memcpy(&s.lease_time, optptr + 2, 4);
247 break;
248 case DHCP_OPTION_END:
249 return type;
250 }
251
252 optptr += optptr[1] + 2;
253 }
254 return type;
255 }
256 /*---------------------------------------------------------------------------*/
257 u8_t
258 parse_msg(void)
259 {
260 struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
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 memcpy(&s.ipaddr, m->yiaddr, 4);
266 return parse_options(&m->options[4], uip_datalen());
267 }
268 return 0;
269 }
270 /*---------------------------------------------------------------------------*/
271 static
272 PT_THREAD(handle_dhcp(void))
273 {
274 PT_BEGIN(&s.pt);
275
276 /* try_again:*/
277 s.state = STATE_SENDING;
278 s.ticks = CLOCK_SECOND;
279 xid++;
280
281 send_discover();
282 do {
283 timer_set(&s.timer, s.ticks);
284 PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
285 // if we timed out then increase time out and send discover again
286 if (timer_expired(&s.timer)) {
287 if (s.ticks < CLOCK_SECOND * 60) {
288 s.ticks *= 2;
289 }
290 send_discover();
291 }else{
292 // we may have gotten some other UDP packet in which case just wait some more for the right packet
293 if (uip_newdata() && parse_msg() == DHCPOFFER) {
294 s.state = STATE_OFFER_RECEIVED;
295 break;
296 }
297 }
298 PT_YIELD(&s.pt);
299
300 } while (s.state != STATE_OFFER_RECEIVED);
301
302 s.ticks = CLOCK_SECOND;
303 xid++;
304
305 send_request(0);
306 do {
307 timer_set(&s.timer, s.ticks);
308 PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
309
310 if (timer_expired(&s.timer)) {
311 if (s.ticks <= CLOCK_SECOND * 10) {
312 s.ticks += CLOCK_SECOND;
313 send_request(0); // resend only on timeout
314 } else {
315 PT_RESTART(&s.pt);
316 }
317 }else{
318 if (uip_newdata() && parse_msg() == DHCPACK) {
319 s.state = STATE_CONFIG_RECEIVED;
320 break;
321 }
322 }
323 PT_YIELD(&s.pt);
324
325 } while (s.state != STATE_CONFIG_RECEIVED);
326
327 dhcpc_configured(&s);
328
329 // now we wait for close to expiration and renew the lease
330 do {
331 // we should reacquire expired leases here.
332 timer_set(&s.timer, (ntohl(s.lease_time) * 0.5)*CLOCK_SECOND); // half of lease expire time
333 PT_WAIT_UNTIL(&s.pt, timer_expired(&s.timer));
334
335 uip_log("reaquire dhcp lease");
336
337 // spec says send request direct to server that gave it to us, but seems to be unecessary
338 //uip_ipaddr_copy(&s.conn->ripaddr, s.serverid);
339
340 s.ticks = CLOCK_SECOND;
341 xid++;
342 send_request(0);
343 do {
344 timer_set(&s.timer, s.ticks);
345 PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
346
347 if (timer_expired(&s.timer)) {
348 if (s.ticks <= CLOCK_SECOND * 10) {
349 s.ticks += CLOCK_SECOND;
350 send_request(0); // resend only on timeout
351 } else {
352 // give up
353 // TODO probably need to deal with upstream apps and stop them then reinit them
354 PT_RESTART(&s.pt);
355 }
356 }else{
357 if (parse_msg() == DHCPACK) {
358 uip_log("dhcp lease renewed");
359 break;
360 }
361 }
362 PT_YIELD(&s.pt);
363 }while(1);
364
365 }while(1);
366
367 PT_END(&s.pt);
368 }
369 /*---------------------------------------------------------------------------*/
370 void
371 dhcpc_init(const void *mac_addr, int mac_len, char *hostname)
372 {
373 uip_ipaddr_t addr;
374
375 s.mac_addr = mac_addr;
376 s.mac_len = mac_len;
377 s.hostname = hostname;
378
379 s.state = STATE_INITIAL;
380 uip_ipaddr(addr, 255, 255, 255, 255);
381 s.conn = uip_udp_new(&addr, HTONS(DHCPC_SERVER_PORT));
382 if (s.conn != NULL) {
383 uip_udp_bind(s.conn, HTONS(DHCPC_CLIENT_PORT));
384 }
385 PT_INIT(&s.pt);
386 }
387 /*---------------------------------------------------------------------------*/
388 void
389 dhcpc_appcall(void)
390 {
391 handle_dhcp();
392 }
393 /*---------------------------------------------------------------------------*/
394 void
395 dhcpc_request(void)
396 {
397 u16_t ipaddr[2];
398
399 if (s.state == STATE_INITIAL) {
400 uip_ipaddr(ipaddr, 0, 0, 0, 0);
401 uip_sethostaddr(ipaddr);
402 /* handle_dhcp(PROCESS_EVENT_NONE, NULL);*/
403 }
404 }
405 /*---------------------------------------------------------------------------*/
406
407 #endif