Contiki 2.6

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 Contiki operating system.
00030  *
00031  * @(#)$Id: dhcpc.c,v 1.9 2010/10/19 18:29:04 adamdunkels Exp $
00032  */
00033 
00034 #include <stdio.h>
00035 #include <string.h>
00036 
00037 #include "contiki.h"
00038 #include "contiki-net.h"
00039 #include "net/dhcpc.h"
00040 
00041 #define STATE_INITIAL         0
00042 #define STATE_SENDING         1
00043 #define STATE_OFFER_RECEIVED  2
00044 #define STATE_CONFIG_RECEIVED 3
00045 
00046 static struct dhcpc_state s;
00047 
00048 struct dhcp_msg {
00049   uint8_t op, htype, hlen, hops;
00050   uint8_t xid[4];
00051   uint16_t secs, flags;
00052   uint8_t ciaddr[4];
00053   uint8_t yiaddr[4];
00054   uint8_t siaddr[4];
00055   uint8_t giaddr[4];
00056   uint8_t chaddr[16];
00057 #ifndef UIP_CONF_DHCP_LIGHT
00058   uint8_t sname[64];
00059   uint8_t file[128];
00060 #endif
00061   uint8_t options[312];
00062 };
00063 
00064 #define BOOTP_BROADCAST 0x8000
00065 
00066 #define DHCP_REQUEST        1
00067 #define DHCP_REPLY          2
00068 #define DHCP_HTYPE_ETHERNET 1
00069 #define DHCP_HLEN_ETHERNET  6
00070 #define DHCP_MSG_LEN      236
00071 
00072 #define DHCPC_SERVER_PORT  67
00073 #define DHCPC_CLIENT_PORT  68
00074 
00075 #define DHCPDISCOVER  1
00076 #define DHCPOFFER     2
00077 #define DHCPREQUEST   3
00078 #define DHCPDECLINE   4
00079 #define DHCPACK       5
00080 #define DHCPNAK       6
00081 #define DHCPRELEASE   7
00082 
00083 #define DHCP_OPTION_SUBNET_MASK   1
00084 #define DHCP_OPTION_ROUTER        3
00085 #define DHCP_OPTION_DNS_SERVER    6
00086 #define DHCP_OPTION_REQ_IPADDR   50
00087 #define DHCP_OPTION_LEASE_TIME   51
00088 #define DHCP_OPTION_MSG_TYPE     53
00089 #define DHCP_OPTION_SERVER_ID    54
00090 #define DHCP_OPTION_REQ_LIST     55
00091 #define DHCP_OPTION_END         255
00092 
00093 static uint32_t xid;
00094 static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
00095 /*---------------------------------------------------------------------------*/
00096 static uint8_t *
00097 add_msg_type(uint8_t *optptr, uint8_t type)
00098 {
00099   *optptr++ = DHCP_OPTION_MSG_TYPE;
00100   *optptr++ = 1;
00101   *optptr++ = type;
00102   return optptr;
00103 }
00104 /*---------------------------------------------------------------------------*/
00105 static uint8_t *
00106 add_server_id(uint8_t *optptr)
00107 {
00108   *optptr++ = DHCP_OPTION_SERVER_ID;
00109   *optptr++ = 4;
00110   memcpy(optptr, s.serverid, 4);
00111   return optptr + 4;
00112 }
00113 /*---------------------------------------------------------------------------*/
00114 static uint8_t *
00115 add_req_ipaddr(uint8_t *optptr)
00116 {
00117   *optptr++ = DHCP_OPTION_REQ_IPADDR;
00118   *optptr++ = 4;
00119   memcpy(optptr, s.ipaddr.u16, 4);
00120   return optptr + 4;
00121 }
00122 /*---------------------------------------------------------------------------*/
00123 static uint8_t *
00124 add_req_options(uint8_t *optptr)
00125 {
00126   *optptr++ = DHCP_OPTION_REQ_LIST;
00127   *optptr++ = 3;
00128   *optptr++ = DHCP_OPTION_SUBNET_MASK;
00129   *optptr++ = DHCP_OPTION_ROUTER;
00130   *optptr++ = DHCP_OPTION_DNS_SERVER;
00131   return optptr;
00132 }
00133 /*---------------------------------------------------------------------------*/
00134 static uint8_t *
00135 add_end(uint8_t *optptr)
00136 {
00137   *optptr++ = DHCP_OPTION_END;
00138   return optptr;
00139 }
00140 /*---------------------------------------------------------------------------*/
00141 static void
00142 create_msg(CC_REGISTER_ARG struct dhcp_msg *m)
00143 {
00144   m->op = DHCP_REQUEST;
00145   m->htype = DHCP_HTYPE_ETHERNET;
00146   m->hlen = s.mac_len;
00147   m->hops = 0;
00148   memcpy(m->xid, &xid, sizeof(m->xid));
00149   m->secs = 0;
00150   m->flags = UIP_HTONS(BOOTP_BROADCAST); /*  Broadcast bit. */
00151   /*  uip_ipaddr_copy(m->ciaddr, uip_hostaddr);*/
00152   memcpy(m->ciaddr, uip_hostaddr.u16, sizeof(m->ciaddr));
00153   memset(m->yiaddr, 0, sizeof(m->yiaddr));
00154   memset(m->siaddr, 0, sizeof(m->siaddr));
00155   memset(m->giaddr, 0, sizeof(m->giaddr));
00156   memcpy(m->chaddr, s.mac_addr, s.mac_len);
00157   memset(&m->chaddr[s.mac_len], 0, sizeof(m->chaddr) - s.mac_len);
00158 #ifndef UIP_CONF_DHCP_LIGHT
00159   memset(m->sname, 0, sizeof(m->sname));
00160   memset(m->file, 0, sizeof(m->file));
00161 #endif
00162 
00163   memcpy(m->options, magic_cookie, sizeof(magic_cookie));
00164 }
00165 /*---------------------------------------------------------------------------*/
00166 static void
00167 send_discover(void)
00168 {
00169   uint8_t *end;
00170   struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
00171 
00172   create_msg(m);
00173 
00174   end = add_msg_type(&m->options[4], DHCPDISCOVER);
00175   end = add_req_options(end);
00176   end = add_end(end);
00177 
00178   uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
00179 }
00180 /*---------------------------------------------------------------------------*/
00181 static void
00182 send_request(void)
00183 {
00184   uint8_t *end;
00185   struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
00186 
00187   create_msg(m);
00188   
00189   end = add_msg_type(&m->options[4], DHCPREQUEST);
00190   end = add_server_id(end);
00191   end = add_req_ipaddr(end);
00192   end = add_end(end);
00193   
00194   uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
00195 }
00196 /*---------------------------------------------------------------------------*/
00197 static uint8_t
00198 parse_options(uint8_t *optptr, int len)
00199 {
00200   uint8_t *end = optptr + len;
00201   uint8_t type = 0;
00202 
00203   while(optptr < end) {
00204     switch(*optptr) {
00205     case DHCP_OPTION_SUBNET_MASK:
00206       memcpy(s.netmask.u16, optptr + 2, 4);
00207       break;
00208     case DHCP_OPTION_ROUTER:
00209       memcpy(s.default_router.u16, optptr + 2, 4);
00210       break;
00211     case DHCP_OPTION_DNS_SERVER:
00212       memcpy(s.dnsaddr.u16, optptr + 2, 4);
00213       break;
00214     case DHCP_OPTION_MSG_TYPE:
00215       type = *(optptr + 2);
00216       break;
00217     case DHCP_OPTION_SERVER_ID:
00218       memcpy(s.serverid, optptr + 2, 4);
00219       break;
00220     case DHCP_OPTION_LEASE_TIME:
00221       memcpy(s.lease_time, optptr + 2, 4);
00222       break;
00223     case DHCP_OPTION_END:
00224       return type;
00225     }
00226 
00227     optptr += optptr[1] + 2;
00228   }
00229   return type;
00230 }
00231 /*---------------------------------------------------------------------------*/
00232 static uint8_t
00233 parse_msg(void)
00234 {
00235   struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
00236   
00237   if(m->op == DHCP_REPLY &&
00238      memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
00239      memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
00240     memcpy(s.ipaddr.u16, m->yiaddr, 4);
00241     return parse_options(&m->options[4], uip_datalen());
00242   }
00243   return 0;
00244 }
00245 /*---------------------------------------------------------------------------*/
00246 /*
00247  * Is this a "fresh" reply for me? If it is, return the type.
00248  */
00249 static int
00250 msg_for_me(void)
00251 {
00252   struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
00253   uint8_t *optptr = &m->options[4];
00254   uint8_t *end = (uint8_t*)uip_appdata + uip_datalen();
00255   
00256   if(m->op == DHCP_REPLY &&
00257      memcmp(m->xid, &xid, sizeof(xid)) == 0 &&
00258      memcmp(m->chaddr, s.mac_addr, s.mac_len) == 0) {
00259     while(optptr < end) {
00260       if(*optptr == DHCP_OPTION_MSG_TYPE) {
00261         return *(optptr + 2);
00262       } else if (*optptr == DHCP_OPTION_END) {
00263         return -1;
00264       }
00265       optptr += optptr[1] + 2;
00266     }
00267   }
00268   return -1;
00269 }
00270 /*---------------------------------------------------------------------------*/
00271 static
00272 PT_THREAD(handle_dhcp(process_event_t ev, void *data))
00273 {
00274   clock_time_t ticks;
00275 
00276   PT_BEGIN(&s.pt);
00277   
00278  init:
00279   xid++;
00280   s.state = STATE_SENDING;
00281   s.ticks = CLOCK_SECOND;
00282   while (1) {
00283     while(ev != tcpip_event) {
00284       tcpip_poll_udp(s.conn);
00285       PT_YIELD(&s.pt);
00286     }
00287     send_discover();
00288     etimer_set(&s.etimer, s.ticks);
00289     do {
00290       PT_YIELD(&s.pt);
00291       if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPOFFER) {
00292         parse_msg();
00293         s.state = STATE_OFFER_RECEIVED;
00294         goto selecting;
00295       }
00296     } while (!etimer_expired(&s.etimer));
00297 
00298     if(s.ticks < CLOCK_SECOND * 60) {
00299       s.ticks *= 2;
00300     }
00301   }
00302   
00303  selecting:
00304   xid++;
00305   s.ticks = CLOCK_SECOND;
00306   do {
00307     while(ev != tcpip_event) {
00308       tcpip_poll_udp(s.conn);
00309       PT_YIELD(&s.pt);
00310     }
00311     send_request();
00312     etimer_set(&s.etimer, s.ticks);
00313     do {
00314       PT_YIELD(&s.pt);
00315       if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
00316         parse_msg();
00317         s.state = STATE_CONFIG_RECEIVED;
00318         goto bound;
00319       }
00320     } while (!etimer_expired(&s.etimer));
00321 
00322     if(s.ticks <= CLOCK_SECOND * 10) {
00323       s.ticks += CLOCK_SECOND;
00324     } else {
00325       goto init;
00326     }
00327   } while(s.state != STATE_CONFIG_RECEIVED);
00328   
00329  bound:
00330 #if 0
00331   printf("Got IP address %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.ipaddr));
00332   printf("Got netmask %d.%d.%d.%d\n",    uip_ipaddr_to_quad(&s.netmask));
00333   printf("Got DNS server %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.dnsaddr));
00334   printf("Got default router %d.%d.%d.%d\n",
00335          uip_ipaddr_to_quad(&s.default_router));
00336   printf("Lease expires in %ld seconds\n",
00337          uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]));
00338 #endif
00339 
00340   dhcpc_configured(&s);
00341   
00342 #define MAX_TICKS (~((clock_time_t)0) / 2)
00343 #define MAX_TICKS32 (~((uint32_t)0))
00344 #define IMIN(a, b) ((a) < (b) ? (a) : (b))
00345 
00346   if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
00347      <= MAX_TICKS32) {
00348     s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
00349                )*CLOCK_SECOND/2;
00350   } else {
00351     s.ticks = MAX_TICKS32;
00352   }
00353 
00354   while(s.ticks > 0) {
00355     ticks = IMIN(s.ticks, MAX_TICKS);
00356     s.ticks -= ticks;
00357     etimer_set(&s.etimer, ticks);
00358     PT_YIELD_UNTIL(&s.pt, etimer_expired(&s.etimer));
00359   }
00360 
00361   if((uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1]))*CLOCK_SECOND/2
00362      <= MAX_TICKS32) {
00363     s.ticks = (uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])
00364                )*CLOCK_SECOND/2;
00365   } else {
00366     s.ticks = MAX_TICKS32;
00367   }
00368 
00369   /* renewing: */
00370   xid++;
00371   do {
00372     while(ev != tcpip_event) {
00373       tcpip_poll_udp(s.conn);
00374       PT_YIELD(&s.pt);
00375     }
00376     send_request();
00377     ticks = IMIN(s.ticks / 2, MAX_TICKS);
00378     s.ticks -= ticks;
00379     etimer_set(&s.etimer, ticks);
00380     do {
00381       PT_YIELD(&s.pt);
00382       if(ev == tcpip_event && uip_newdata() && msg_for_me() == DHCPACK) {
00383         parse_msg();
00384         goto bound;
00385       }
00386     } while(!etimer_expired(&s.etimer));
00387   } while(s.ticks >= CLOCK_SECOND*3);
00388 
00389   /* rebinding: */
00390 
00391   /* lease_expired: */
00392   dhcpc_unconfigured(&s);
00393   goto init;
00394 
00395   PT_END(&s.pt);
00396 }
00397 /*---------------------------------------------------------------------------*/
00398 void
00399 dhcpc_init(const void *mac_addr, int mac_len)
00400 {
00401   uip_ipaddr_t addr;
00402   
00403   s.mac_addr = mac_addr;
00404   s.mac_len  = mac_len;
00405 
00406   s.state = STATE_INITIAL;
00407   uip_ipaddr(&addr, 255,255,255,255);
00408   s.conn = udp_new(&addr, UIP_HTONS(DHCPC_SERVER_PORT), NULL);
00409   if(s.conn != NULL) {
00410     udp_bind(s.conn, UIP_HTONS(DHCPC_CLIENT_PORT));
00411   }
00412   PT_INIT(&s.pt);
00413 }
00414 /*---------------------------------------------------------------------------*/
00415 void
00416 dhcpc_appcall(process_event_t ev, void *data)
00417 {
00418   if(ev == tcpip_event || ev == PROCESS_EVENT_TIMER) {
00419     handle_dhcp(ev, data);
00420   }
00421 }
00422 /*---------------------------------------------------------------------------*/
00423 void
00424 dhcpc_request(void)
00425 {
00426   uip_ipaddr_t ipaddr;
00427   
00428   if(s.state == STATE_INITIAL) {
00429     uip_ipaddr(&ipaddr, 0,0,0,0);
00430     uip_sethostaddr(&ipaddr);
00431     handle_dhcp(PROCESS_EVENT_NONE, NULL);
00432   }
00433 }
00434 /*---------------------------------------------------------------------------*/