# dhcpc.c

    00001 /*
    00002  * Copyright (c) 2005, Swedish Institute of Computer Science
    00003  * All rights reserved.
    00004  *
    00005  * Redistribution and use in source and binary forms, with or without
    00006  * modification, are permitted provided that the following conditions
    00007  * are met:
    00008  * 1. Redistributions of source code must retain the above copyright
    00009  *    notice, this list of conditions and the following disclaimer.
    00010  * 2. Redistributions in binary form must reproduce the above copyright
    00011  *    notice, this list of conditions and the following disclaimer in the
    00012  *    documentation and/or other materials provided with the distribution.
    00013  * 3. Neither the name of the Institute nor the names of its contributors
    00014  *    may be used to endorse or promote products derived from this software
    00015  *    without specific prior written permission.
    00016  *
    00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
    00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
    00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    00027  * SUCH DAMAGE.
    00028  *
    00029  * This file is part of the uIP TCP/IP stack
    00030  *
    00031  * @(#)$Id: dhcpc.c,v 1.2 2006/06/11 21:46:37 adam Exp $
    00032  */
    00033
    00034 #include <stdio.h>
    00035 #include <string.h>
    00036
    00037 #include "uip.h"
    00038 #include "dhcpc.h"
    00039 #include "timer.h"
    00040 #include "pt.h"
    00041
    00042 #define STATE_INITIAL         0
    00043 #define STATE_SENDING         1
    00044 #define STATE_OFFER_RECEIVED  2
    00045 #define STATE_CONFIG_RECEIVED 3
    00046
    00047 static struct dhcpc_state s;
    00048
    00049 struct dhcp_msg {
    00050   u8_t op, htype, hlen, hops;
    00051   u8_t xid[4];
    00052   u16_t secs, flags;
    00053   u8_t ciaddr[4];
    00054   u8_t yiaddr[4];
    00055   u8_t siaddr[4];
    00056   u8_t giaddr[4];
    00057   u8_t chaddr[16];
    00058 #ifndef UIP_CONF_DHCP_LIGHT
    00059   u8_t sname[64];
    00060   u8_t file[128];
    00061 #endif
    00062   u8_t options[312];
    00063 };
    00064
    00065 #define BOOTP_BROADCAST 0x8000
    00066
    00067 #define DHCP_REQUEST        1
    00068 #define DHCP_REPLY          2
    00069 #define DHCP_HTYPE_ETHERNET 1
    00070 #define DHCP_HLEN_ETHERNET  6
    00071 #define DHCP_MSG_LEN      236
    00072
    00073 #define DHCPC_SERVER_PORT  67
    00074 #define DHCPC_CLIENT_PORT  68
    00075
    00076 #define DHCPDISCOVER  1
    00077 #define DHCPOFFER     2
    00078 #define DHCPREQUEST   3
    00079 #define DHCPDECLINE   4
    00080 #define DHCPACK       5
    00081 #define DHCPNAK       6
    00082 #define DHCPRELEASE   7
    00083
    00084 #define DHCP_OPTION_SUBNET_MASK   1
    00085 #define DHCP_OPTION_ROUTER        3
    00086 #define DHCP_OPTION_DNS_SERVER    6
    00087 #define DHCP_OPTION_REQ_IPADDR   50
    00088 #define DHCP_OPTION_LEASE_TIME   51
    00089 #define DHCP_OPTION_MSG_TYPE     53
    00090 #define DHCP_OPTION_SERVER_ID    54
    00091 #define DHCP_OPTION_REQ_LIST     55
    00092 #define DHCP_OPTION_END         255
    00093
    00094 static const u8_t xid[4] = {0xad, 0xde, 0x12, 0x23};
    00095 static const u8_t magic_cookie[4] = {99, 130, 83, 99};
    00096 /*---------------------------------------------------------------------------*/
    00097 static u8_t *
    00098 add_msg_type(u8_t *optptr, u8_t type)
    00099 {
    00100   *optptr++ = DHCP_OPTION_MSG_TYPE;
    00101   *optptr++ = 1;
    00102   *optptr++ = type;
    00103   return optptr;
    00104 }
    00105 /*---------------------------------------------------------------------------*/
    00106 static u8_t *
    00107 add_server_id(u8_t *optptr)
    00108 {
    00109   *optptr++ = DHCP_OPTION_SERVER_ID;
    00110   *optptr++ = 4;
    00111   memcpy(optptr, s.serverid, 4);
    00112   return optptr + 4;
    00113 }
    00114 /*---------------------------------------------------------------------------*/
    00115 static u8_t *
    00116 add_req_ipaddr(u8_t *optptr)
    00117 {
    00118   *optptr++ = DHCP_OPTION_REQ_IPADDR;
    00119   *optptr++ = 4;
    00120   memcpy(optptr, s.ipaddr, 4);
    00121   return optptr + 4;
    00122 }
    00123 /*---------------------------------------------------------------------------*/
    00124 static u8_t *
    00125 add_req_options(u8_t *optptr)
    00126 {
    00127   *optptr++ = DHCP_OPTION_REQ_LIST;
    00128   *optptr++ = 3;
    00129   *optptr++ = DHCP_OPTION_SUBNET_MASK;
    00130   *optptr++ = DHCP_OPTION_ROUTER;
    00131   *optptr++ = DHCP_OPTION_DNS_SERVER;
    00132   return optptr;
    00133 }
    00134 /*---------------------------------------------------------------------------*/
    00135 static u8_t *
    00136 add_end(u8_t *optptr)
    00137 {
    00138   *optptr++ = DHCP_OPTION_END;
    00139   return optptr;
    00140 }
    00141 /*---------------------------------------------------------------------------*/
    00142 static void
    00143 create_msg(register struct dhcp_msg *m)
    00144 {
    00145   m->op = DHCP_REQUEST;
    00146   m->htype = DHCP_HTYPE_ETHERNET;
    00147   m->hlen = s.mac_len;
    00148   m->hops = 0;
    00149   memcpy(m->xid, xid, sizeof(m->xid));
    00150   m->secs = 0;
    00151   m->flags = HTONS(BOOTP_BROADCAST); /*  Broadcast bit. */
    00152   /*  uip_ipaddr_copy(m->ciaddr, uip_hostaddr);*/
    00153   memcpy(m->ciaddr, uip_hostaddr, sizeof(m->ciaddr));
    00154   memset(m->yiaddr, 0, sizeof(m->yiaddr));
    00155   memset(m->siaddr, 0, sizeof(m->siaddr));
    00156   memset(m->giaddr, 0, sizeof(m->giaddr));
    00157   memcpy(m->chaddr, s.mac_addr, s.mac_len);
    00158   memset(&m->chaddr[s.mac_len], 0, sizeof(m->chaddr) - s.mac_len);
    00159 #ifndef UIP_CONF_DHCP_LIGHT
    00160   memset(m->sname, 0, sizeof(m->sname));
    00161   memset(m->file, 0, sizeof(m->file));
    00162 #endif
    00163
    00164   memcpy(m->options, magic_cookie, sizeof(magic_cookie));
    00165 }
    00166 /*---------------------------------------------------------------------------*/
    00167 static void
    00168 send_discover(void)
    00169 {
    00170   u8_t *end;
    00171   struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
    00172
    00173   create_msg(m);
    00174
    00175   end = add_msg_type(&m->options[4], DHCPDISCOVER);
    00176   end = add_req_options(end);
    00177   end = add_end(end);
    00178
    00179   uip_send(uip_appdata, end - (u8_t *)uip_appdata);
    00180 }
    00181 /*---------------------------------------------------------------------------*/
    00182 static void
    00183 send_request(void)
    00184 {
    00185   u8_t *end;
    00186   struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
    00187
    00188   create_msg(m);
    00189
    00190   end = add_msg_type(&m->options[4], DHCPREQUEST);
    00191   end = add_server_id(end);
    00192   end = add_req_ipaddr(end);
    00193   end = add_end(end);
    00194
    00195   uip_send(uip_appdata, end - (u8_t *)uip_appdata);
    00196 }
    00197 /*---------------------------------------------------------------------------*/
    00198 static u8_t
    00199 parse_options(u8_t *optptr, int len)
    00200 {
    00201   u8_t *end = optptr + len;
    00202   u8_t type = 0;
    00203
    00204   while(optptr < end) {
    00205     switch(*optptr) {
    00206     case DHCP_OPTION_SUBNET_MASK:
    00207       memcpy(s.netmask, optptr + 2, 4);
    00208       break;
    00209     case DHCP_OPTION_ROUTER:
    00210       memcpy(s.default_router, optptr + 2, 4);
    00211       break;
    00212     case DHCP_OPTION_DNS_SERVER:
    00213       memcpy(s.dnsaddr, optptr + 2, 4);
    00214       break;
    00215     case DHCP_OPTION_MSG_TYPE:
    00216       type = *(optptr + 2);
    00217       break;
    00218     case DHCP_OPTION_SERVER_ID:
    00219       memcpy(s.serverid, optptr + 2, 4);
    00220       break;
    00221     case DHCP_OPTION_LEASE_TIME:
    00222       memcpy(s.lease_time, optptr + 2, 4);
    00223       break;
    00224     case DHCP_OPTION_END:
    00225       return type;
    00226     }
    00227
    00228     optptr += optptr[1] + 2;
    00229   }
    00230   return type;
    00231 }
    00232 /*---------------------------------------------------------------------------*/
    00233 static u8_t
    00234 parse_msg(void)
    00235 {
    00236   struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
    00237
    00238   if(m->op == DHCP_REPLY &&
    00239      memcmp(m->xid, xid, sizeof(xid)) == 0 &&
    00240      memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
    00241     memcpy(s.ipaddr, m->yiaddr, 4);
    00242     return parse_options(&m->options[4], uip_datalen());
    00243   }
    00244   return 0;
    00245 }
    00246 /*---------------------------------------------------------------------------*/
    00247 static
    00248 PT_THREAD(handle_dhcp(void))
    00249 {
    00250   PT_BEGIN(&s.pt);
    00251
    00252   /* try_again:*/
    00253   s.state = STATE_SENDING;
    00254   s.ticks = CLOCK_SECOND;
    00255
    00256   do {
    00257     send_discover();
    00258     timer_set(&s.timer, s.ticks);
    00259     PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
    00260
    00261     if(uip_newdata() && parse_msg() == DHCPOFFER) {
    00262       s.state = STATE_OFFER_RECEIVED;
    00263       break;
    00264     }
    00265
    00266     if(s.ticks < CLOCK_SECOND * 60) {
    00267       s.ticks *= 2;
    00268     }
    00269   } while(s.state != STATE_OFFER_RECEIVED);
    00270
    00271   s.ticks = CLOCK_SECOND;
    00272
    00273   do {
    00274     send_request();
    00275     timer_set(&s.timer, s.ticks);
    00276     PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
    00277
    00278     if(uip_newdata() && parse_msg() == DHCPACK) {
    00279       s.state = STATE_CONFIG_RECEIVED;
    00280       break;
    00281     }
    00282
    00283     if(s.ticks <= CLOCK_SECOND * 10) {
    00284       s.ticks += CLOCK_SECOND;
    00285     } else {
    00286       PT_RESTART(&s.pt);
    00287     }
    00288   } while(s.state != STATE_CONFIG_RECEIVED);
    00289
    00290 #if 0
    00291   printf("Got IP address %d.%d.%d.%d\n",
    00292          uip_ipaddr1(s.ipaddr), uip_ipaddr2(s.ipaddr),
    00293          uip_ipaddr3(s.ipaddr), uip_ipaddr4(s.ipaddr));
    00294   printf("Got netmask %d.%d.%d.%d\n",
    00295          uip_ipaddr1(s.netmask), uip_ipaddr2(s.netmask),
    00296          uip_ipaddr3(s.netmask), uip_ipaddr4(s.netmask));
    00297   printf("Got DNS server %d.%d.%d.%d\n",
    00298          uip_ipaddr1(s.dnsaddr), uip_ipaddr2(s.dnsaddr),
    00299          uip_ipaddr3(s.dnsaddr), uip_ipaddr4(s.dnsaddr));
    00300   printf("Got default router %d.%d.%d.%d\n",
    00301          uip_ipaddr1(s.default_router), uip_ipaddr2(s.default_router),
    00302          uip_ipaddr3(s.default_router), uip_ipaddr4(s.default_router));
    00303   printf("Lease expires in %ld seconds\n",
    00304          ntohs(s.lease_time[0])*65536ul + ntohs(s.lease_time[1]));
    00305 #endif
    00306
    00307   dhcpc_configured(&s);
    00308
    00309   /*  timer_stop(&s.timer);*/
    00310
    00311   /*
    00312    * PT_END restarts the thread so we do this instead. Eventually we
    00313    * should reacquire expired leases here.
    00314    */
    00315   while(1) {
    00316     PT_YIELD(&s.pt);
    00317   }
    00318
    00319   PT_END(&s.pt);
    00320 }
    00321 /*---------------------------------------------------------------------------*/
    00322 void
    00323 dhcpc_init(const void *mac_addr, int mac_len)
    00324 {
    00325   uip_ipaddr_t addr;
    00326
    00327   s.mac_addr = mac_addr;
    00328   s.mac_len  = mac_len;
    00329
    00330   s.state = STATE_INITIAL;
    00331   uip_ipaddr(addr, 255,255,255,255);
    00332   s.conn = uip_udp_new(&addr, HTONS(DHCPC_SERVER_PORT));
    00333   if(s.conn != NULL) {
    00334     uip_udp_bind(s.conn, HTONS(DHCPC_CLIENT_PORT));
    00335   }
    00336   PT_INIT(&s.pt);
    00337 }
    00338 /*---------------------------------------------------------------------------*/
    00339 void
    00340 dhcpc_appcall(void)
    00341 {
    00342   handle_dhcp();
    00343 }
    00344 /*---------------------------------------------------------------------------*/
    00345 void
    00346 dhcpc_request(void)
    00347 {
    00348   u16_t ipaddr[2];
    00349
    00350   if(s.state == STATE_INITIAL) {
    00351     uip_ipaddr(ipaddr, 0,0,0,0);
    00352     uip_sethostaddr(ipaddr);
    00353     /*    handle_dhcp(PROCESS_EVENT_NONE, NULL);*/
    00354   }
    00355 }
    00356 /*---------------------------------------------------------------------------*/