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 #define STATE_INITIAL 0
43 #define STATE_SENDING 1
44 #define STATE_OFFER_RECEIVED 2
45 #define STATE_CONFIG_RECEIVED 3
46
47 static struct dhcpc_state s;
48
49 struct dhcp_msg {
50 u8_t op, htype, hlen, hops;
51 u8_t xid[4];
52 u16_t secs, flags;
53 u8_t ciaddr[4];
54 u8_t yiaddr[4];
55 u8_t siaddr[4];
56 u8_t giaddr[4];
57 u8_t chaddr[16];
58 #ifndef UIP_CONF_DHCP_LIGHT
59 u8_t sname[64];
60 u8_t file[128];
61 #endif
62 u8_t options[312];
63 };
64
65 #define BOOTP_BROADCAST 0x8000
66
67 #define DHCP_REQUEST 1
68 #define DHCP_REPLY 2
69 #define DHCP_HTYPE_ETHERNET 1
70 #define DHCP_HLEN_ETHERNET 6
71 #define DHCP_MSG_LEN 236
72
73 #define DHCPC_SERVER_PORT 67
74 #define DHCPC_CLIENT_PORT 68
75
76 #define DHCPDISCOVER 1
77 #define DHCPOFFER 2
78 #define DHCPREQUEST 3
79 #define DHCPDECLINE 4
80 #define DHCPACK 5
81 #define DHCPNAK 6
82 #define DHCPRELEASE 7
83
84 #define DHCP_OPTION_SUBNET_MASK 1
85 #define DHCP_OPTION_ROUTER 3
86 #define DHCP_OPTION_DNS_SERVER 6
87 #define DHCP_OPTION_REQ_IPADDR 50
88 #define DHCP_OPTION_LEASE_TIME 51
89 #define DHCP_OPTION_MSG_TYPE 53
90 #define DHCP_OPTION_SERVER_ID 54
91 #define DHCP_OPTION_REQ_LIST 55
92 #define DHCP_OPTION_END 255
93
94 static const u8_t xid[4] = {0xad, 0xde, 0x12, 0x23};
95 static const u8_t magic_cookie[4] = {99, 130, 83, 99};
96 /*---------------------------------------------------------------------------*/
97 static u8_t *
add_msg_type(u8_t * optptr,u8_t type)98 add_msg_type(u8_t *optptr, u8_t type)
99 {
100 *optptr++ = DHCP_OPTION_MSG_TYPE;
101 *optptr++ = 1;
102 *optptr++ = type;
103 return optptr;
104 }
105 /*---------------------------------------------------------------------------*/
106 static u8_t *
add_server_id(u8_t * optptr)107 add_server_id(u8_t *optptr)
108 {
109 *optptr++ = DHCP_OPTION_SERVER_ID;
110 *optptr++ = 4;
111 memcpy(optptr, s.serverid, 4);
112 return optptr + 4;
113 }
114 /*---------------------------------------------------------------------------*/
115 static u8_t *
add_req_ipaddr(u8_t * optptr)116 add_req_ipaddr(u8_t *optptr)
117 {
118 *optptr++ = DHCP_OPTION_REQ_IPADDR;
119 *optptr++ = 4;
120 memcpy(optptr, s.ipaddr, 4);
121 return optptr + 4;
122 }
123 /*---------------------------------------------------------------------------*/
124 static u8_t *
add_req_options(u8_t * optptr)125 add_req_options(u8_t *optptr)
126 {
127 *optptr++ = DHCP_OPTION_REQ_LIST;
128 *optptr++ = 3;
129 *optptr++ = DHCP_OPTION_SUBNET_MASK;
130 *optptr++ = DHCP_OPTION_ROUTER;
131 *optptr++ = DHCP_OPTION_DNS_SERVER;
132 return optptr;
133 }
134 /*---------------------------------------------------------------------------*/
135 static u8_t *
add_end(u8_t * optptr)136 add_end(u8_t *optptr)
137 {
138 *optptr++ = DHCP_OPTION_END;
139 return optptr;
140 }
141 /*---------------------------------------------------------------------------*/
142 static void
create_msg(register struct dhcp_msg * m)143 create_msg(register struct dhcp_msg *m)
144 {
145 m->op = DHCP_REQUEST;
146 m->htype = DHCP_HTYPE_ETHERNET;
147 m->hlen = s.mac_len;
148 m->hops = 0;
149 memcpy(m->xid, xid, sizeof(m->xid));
150 m->secs = 0;
151 m->flags = HTONS(BOOTP_BROADCAST); /* Broadcast bit. */
152 /* uip_ipaddr_copy(m->ciaddr, uip_hostaddr);*/
153 memcpy(m->ciaddr, uip_hostaddr, sizeof(m->ciaddr));
154 memset(m->yiaddr, 0, sizeof(m->yiaddr));
155 memset(m->siaddr, 0, sizeof(m->siaddr));
156 memset(m->giaddr, 0, sizeof(m->giaddr));
157 memcpy(m->chaddr, s.mac_addr, s.mac_len);
158 memset(&m->chaddr[s.mac_len], 0, sizeof(m->chaddr) - s.mac_len);
159 #ifndef UIP_CONF_DHCP_LIGHT
160 memset(m->sname, 0, sizeof(m->sname));
161 memset(m->file, 0, sizeof(m->file));
162 #endif
163
164 memcpy(m->options, magic_cookie, sizeof(magic_cookie));
165 }
166 /*---------------------------------------------------------------------------*/
167 static void
send_discover(void)168 send_discover(void)
169 {
170 u8_t *end;
171 struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
172
173 create_msg(m);
174
175 end = add_msg_type(&m->options[4], DHCPDISCOVER);
176 end = add_req_options(end);
177 end = add_end(end);
178
179 uip_send(uip_appdata, end - (u8_t *)uip_appdata);
180 }
181 /*---------------------------------------------------------------------------*/
182 static void
send_request(void)183 send_request(void)
184 {
185 u8_t *end;
186 struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
187
188 create_msg(m);
189
190 end = add_msg_type(&m->options[4], DHCPREQUEST);
191 end = add_server_id(end);
192 end = add_req_ipaddr(end);
193 end = add_end(end);
194
195 uip_send(uip_appdata, end - (u8_t *)uip_appdata);
196 }
197 /*---------------------------------------------------------------------------*/
198 static u8_t
parse_options(u8_t * optptr,int len)199 parse_options(u8_t *optptr, int len)
200 {
201 u8_t *end = optptr + len;
202 u8_t type = 0;
203
204 while(optptr < end) {
205 switch(*optptr) {
206 case DHCP_OPTION_SUBNET_MASK:
207 memcpy(s.netmask, optptr + 2, 4);
208 break;
209 case DHCP_OPTION_ROUTER:
210 memcpy(s.default_router, optptr + 2, 4);
211 break;
212 case DHCP_OPTION_DNS_SERVER:
213 memcpy(s.dnsaddr, optptr + 2, 4);
214 break;
215 case DHCP_OPTION_MSG_TYPE:
216 type = *(optptr + 2);
217 break;
218 case DHCP_OPTION_SERVER_ID:
219 memcpy(s.serverid, optptr + 2, 4);
220 break;
221 case DHCP_OPTION_LEASE_TIME:
222 memcpy(s.lease_time, optptr + 2, 4);
223 break;
224 case DHCP_OPTION_END:
225 return type;
226 }
227
228 optptr += optptr[1] + 2;
229 }
230 return type;
231 }
232 /*---------------------------------------------------------------------------*/
233 static u8_t
parse_msg(void)234 parse_msg(void)
235 {
236 struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
237
238 if(m->op == DHCP_REPLY &&
239 memcmp(m->xid, xid, sizeof(xid)) == 0 &&
240 memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
241 memcpy(s.ipaddr, m->yiaddr, 4);
242 return parse_options(&m->options[4], uip_datalen());
243 }
244 return 0;
245 }
246 /*---------------------------------------------------------------------------*/
247 static
PT_THREAD(handle_dhcp (void))248 PT_THREAD(handle_dhcp(void))
249 {
250 PT_BEGIN(&s.pt);
251
252 /* try_again:*/
253 s.state = STATE_SENDING;
254 s.ticks = CLOCK_SECOND;
255
256 do {
257 send_discover();
258 timer_set(&s.timer, s.ticks);
259 PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
260
261 if(uip_newdata() && parse_msg() == DHCPOFFER) {
262 s.state = STATE_OFFER_RECEIVED;
263 break;
264 }
265
266 if(s.ticks < CLOCK_SECOND * 60) {
267 s.ticks *= 2;
268 }
269 } while(s.state != STATE_OFFER_RECEIVED);
270
271 s.ticks = CLOCK_SECOND;
272
273 do {
274 send_request();
275 timer_set(&s.timer, s.ticks);
276 PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
277
278 if(uip_newdata() && parse_msg() == DHCPACK) {
279 s.state = STATE_CONFIG_RECEIVED;
280 break;
281 }
282
283 if(s.ticks <= CLOCK_SECOND * 10) {
284 s.ticks += CLOCK_SECOND;
285 } else {
286 PT_RESTART(&s.pt);
287 }
288 } while(s.state != STATE_CONFIG_RECEIVED);
289
290 #if 0
291 printf("Got IP address %d.%d.%d.%d\n",
292 uip_ipaddr1(s.ipaddr), uip_ipaddr2(s.ipaddr),
293 uip_ipaddr3(s.ipaddr), uip_ipaddr4(s.ipaddr));
294 printf("Got netmask %d.%d.%d.%d\n",
295 uip_ipaddr1(s.netmask), uip_ipaddr2(s.netmask),
296 uip_ipaddr3(s.netmask), uip_ipaddr4(s.netmask));
297 printf("Got DNS server %d.%d.%d.%d\n",
298 uip_ipaddr1(s.dnsaddr), uip_ipaddr2(s.dnsaddr),
299 uip_ipaddr3(s.dnsaddr), uip_ipaddr4(s.dnsaddr));
300 printf("Got default router %d.%d.%d.%d\n",
301 uip_ipaddr1(s.default_router), uip_ipaddr2(s.default_router),
302 uip_ipaddr3(s.default_router), uip_ipaddr4(s.default_router));
303 printf("Lease expires in %ld seconds\n",
304 ntohs(s.lease_time[0])*65536ul + ntohs(s.lease_time[1]));
305 #endif
306
307 dhcpc_configured(&s);
308
309 /* timer_stop(&s.timer);*/
310
311 /*
312 * PT_END restarts the thread so we do this instead. Eventually we
313 * should reacquire expired leases here.
314 */
315 while(1) {
316 PT_YIELD(&s.pt);
317 }
318
319 PT_END(&s.pt);
320 }
321 /*---------------------------------------------------------------------------*/
322 void
dhcpc_init(const void * mac_addr,int mac_len)323 dhcpc_init(const void *mac_addr, int mac_len)
324 {
325 uip_ipaddr_t addr;
326
327 s.mac_addr = mac_addr;
328 s.mac_len = mac_len;
329
330 s.state = STATE_INITIAL;
331 uip_ipaddr(addr, 255,255,255,255);
332 s.conn = uip_udp_new(&addr, HTONS(DHCPC_SERVER_PORT));
333 if(s.conn != NULL) {
334 uip_udp_bind(s.conn, HTONS(DHCPC_CLIENT_PORT));
335 }
336 PT_INIT(&s.pt);
337 }
338 /*---------------------------------------------------------------------------*/
339 void
dhcpc_appcall(void)340 dhcpc_appcall(void)
341 {
342 handle_dhcp();
343 }
344 /*---------------------------------------------------------------------------*/
345 void
dhcpc_request(void)346 dhcpc_request(void)
347 {
348 u16_t ipaddr[2];
349
350 if(s.state == STATE_INITIAL) {
351 uip_ipaddr(ipaddr, 0,0,0,0);
352 uip_sethostaddr(ipaddr);
353 /* handle_dhcp(PROCESS_EVENT_NONE, NULL);*/
354 }
355 }
356 /*---------------------------------------------------------------------------*/
357