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 * 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 * 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 * 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 * 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 * 136 add_end(u8_t *optptr) 137 { 138 *optptr++ = DHCP_OPTION_END; 139 return optptr; 140 } 141 /*---------------------------------------------------------------------------*/ 142 static void 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 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 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 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 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 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 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 340 dhcpc_appcall(void) 341 { 342 handle_dhcp(); 343 } 344 /*---------------------------------------------------------------------------*/ 345 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