CDC/ECM driver for mbed, based on USBDevice by mbed-official. Uses PicoTCP to access Ethernet USB device. License: GPLv2

Dependents:   USBEthernet_TEST

Fork of USB_Ethernet by Daniele Lacamera

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pico_dhcp_client.c Source File

pico_dhcp_client.c

00001 /*********************************************************************
00002 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
00003 See LICENSE and COPYING for usage.
00004 
00005 Authors: Frederik Van Slycken, Kristof Roelants
00006 *********************************************************************/
00007 
00008 #include "pico_dhcp_client.h"
00009 #include "pico_stack.h"
00010 #include "pico_config.h"
00011 #include "pico_device.h"
00012 #include "pico_ipv4.h"
00013 #include "pico_socket.h"
00014 
00015 #ifdef PICO_SUPPORT_DHCPC
00016 
00017 /***********
00018  * structs *
00019  ***********/
00020 
00021 static uint8_t dhcp_client_mutex = 1; /* to serialize client negotations if multiple devices */
00022 
00023 struct dhcp_timer_param{
00024   uint16_t type;
00025   struct pico_dhcp_client_cookie* cli;
00026   int valid;
00027 };
00028 
00029 struct pico_dhcp_client_cookie
00030 {
00031   uint32_t xid;
00032   uint32_t *xid_user;
00033   struct pico_ip4 address;
00034   struct pico_ip4 netmask;
00035   struct pico_ip4 gateway;
00036   struct pico_ip4 nameserver;
00037   struct pico_ip4 server_id;
00038   uint32_t lease_time;
00039   uint32_t T1;
00040   uint32_t T2;
00041   struct pico_socket* socket;
00042   int connected;
00043   struct pico_device* device;
00044   unsigned long start_time;
00045   int attempt;
00046   enum dhcp_negotiation_state state;
00047   void (*cb)(void* cli, int code);
00048   struct dhcp_timer_param* timer_param_1;
00049   struct dhcp_timer_param* timer_param_2;
00050   struct dhcp_timer_param* timer_param_lease;
00051   struct dhcp_timer_param* timer_param_retransmit;
00052   int link_added;
00053 };
00054 
00055 static int dhcp_cookies_cmp(void *ka, void *kb)
00056 {
00057   struct pico_dhcp_client_cookie *a = ka, *b = kb;
00058   if (a->xid < b->xid)
00059     return -1; 
00060   else if (a->xid > b->xid)
00061     return 1;
00062   else
00063     return 0;
00064 } 
00065 PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp);
00066 
00067 /*************************
00068  * function declarations *
00069  *************************/
00070 static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len);
00071 static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg);
00072 
00073 //cb
00074 static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s);
00075 static void dhcp_timer_cb(unsigned long tick, void* param);
00076 
00077 //util
00078 static void pico_dhcp_retry(struct pico_dhcp_client_cookie *client);
00079 static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type);
00080 static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli);
00081 static int init_cookie(struct pico_dhcp_client_cookie* cli);
00082 static struct pico_dhcp_client_cookie* get_cookie_by_xid(uint32_t xid);
00083 static uint32_t get_xid(uint8_t* data);
00084 
00085 //fsm functions
00086 static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00087 static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00088 static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00089 static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00090 static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00091 
00092 //fsm implementation
00093 static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len);
00094 
00095 /***************
00096  * entry point *
00097  ***************/
00098 
00099 static uint32_t pico_dhcp_execute_init(struct pico_dhcp_client_cookie *cli)
00100 {
00101   if (!dhcp_client_mutex) {
00102     pico_timer_add(3000, pico_dhcp_reinitiate_negotiation, cli);
00103     return 0;
00104   }
00105   dhcp_client_mutex--;
00106 
00107   if (init_cookie(cli) < 0)
00108     return -1;
00109 
00110   dbg("DHCPC: cookie with xid %u\n", cli->xid);
00111   
00112   if (pico_tree_insert(&DHCPCookies, cli)) {
00113     pico_err = PICO_ERR_EAGAIN;
00114     if(cli->cb != NULL) {
00115       cli->cb(cli, PICO_DHCP_ERROR);
00116     }
00117     pico_free(cli);
00118     return -1; /* Element key already exists */
00119   }
00120 
00121   if (dhclient_send(cli, PICO_DHCP_MSG_DISCOVER) < 0)
00122     return -1;
00123 
00124   return 0;
00125 }
00126 
00127 /* returns a pointer to the client cookie. The user should pass this pointer every time he calls a dhcp-function. This is so that we can (one day) support dhcp on multiple interfaces */
00128 int pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void *cli, int code), uint32_t *xid)
00129 {
00130   struct pico_dhcp_client_cookie *cli;
00131   
00132   if(!device || !callback || !xid){
00133     pico_err = PICO_ERR_EINVAL;
00134     return -1;
00135   }
00136   cli = pico_zalloc(sizeof(struct pico_dhcp_client_cookie));
00137   if(!cli){
00138     pico_err = PICO_ERR_ENOMEM;
00139     return -1;
00140   }
00141 
00142   cli->device = device;
00143   cli->cb = callback;
00144   cli->xid_user = xid;
00145   *(cli->xid_user) = 0;
00146 
00147   return pico_dhcp_execute_init(cli);
00148 }
00149 
00150 static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg)
00151 {
00152   struct pico_dhcp_client_cookie *cli = (struct pico_dhcp_client_cookie *) arg;
00153 
00154   pico_dhcp_execute_init(cli);
00155 
00156   return;
00157 }
00158 
00159 /********************
00160  * access functions *
00161  ********************/
00162 
00163 struct pico_ip4 pico_dhcp_get_address(void* cli)
00164 {
00165   return ((struct pico_dhcp_client_cookie*)cli)->address;
00166 }
00167 
00168 struct pico_ip4 pico_dhcp_get_gateway(void* cli)
00169 {
00170   return ((struct pico_dhcp_client_cookie*)cli)->gateway;
00171 }
00172 
00173 struct pico_ip4 pico_dhcp_get_nameserver(void* cli)
00174 {
00175   return ((struct pico_dhcp_client_cookie*)cli)->nameserver;
00176 }
00177 
00178 /*************
00179  * callbacks *
00180  *************/
00181 
00182 static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s)
00183 {
00184   uint8_t buf[DHCPC_DATAGRAM_SIZE];
00185   int r=0;
00186   uint32_t peer;
00187   uint16_t port;
00188   int type;
00189 
00190   struct pico_dhcp_client_cookie *cli;
00191   dbg("DHCPC: called dhcp_wakeup\n");
00192   if (ev == PICO_SOCK_EV_RD) {
00193     do {
00194       r = pico_socket_recvfrom(s, buf, DHCPC_DATAGRAM_SIZE, &peer, &port);
00195       cli = get_cookie_by_xid(get_xid(buf));
00196       if(cli == NULL)
00197         return;
00198       if (r > 0 && port == PICO_DHCPD_PORT) {
00199         type = pico_dhcp_verify_and_identify_type(buf, r, cli);
00200         pico_dhcp_state_machine(type, cli, buf, r);
00201       }
00202     } while(r>0);
00203   }
00204 }
00205 
00206 static void dhcp_timer_cb(unsigned long tick, void* param)
00207 {
00208   struct dhcp_timer_param* param2 = (struct dhcp_timer_param*) param;
00209   if(param2->valid == 1){
00210     //dbg("called timer cb on active timer type %d\n",param2->type);
00211     pico_dhcp_state_machine(param2->type, param2->cli, NULL, 0);
00212   }
00213   if(param2->cli->timer_param_1 == param){
00214     param2->cli->timer_param_1 = NULL;
00215   }
00216   if(param2->cli->timer_param_2 == param){
00217     param2->cli->timer_param_2 = NULL;
00218   }
00219   if(param2->cli->timer_param_lease == param){
00220     param2->cli->timer_param_lease = NULL;
00221   }
00222   if(param2->cli->timer_param_retransmit == param){
00223     param2->cli->timer_param_retransmit = NULL;
00224   }
00225 
00226   pico_free(param);
00227 
00228 }
00229 /*****************
00230  * fsm functions *
00231  *****************/
00232 
00233 static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
00234 {
00235   struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
00236   uint8_t *nextopt, opt_data[20], opt_type;
00237   int opt_len = 20;
00238   uint8_t msg_type = 0xFF;
00239   int T1_set = 0;
00240   int T2_set = 0;
00241 
00242   cli->address.addr = dhdr->yiaddr;
00243 
00244   opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
00245   while (opt_type != PICO_DHCPOPT_END) {
00246     if (opt_type == PICO_DHCPOPT_MSGTYPE)
00247       msg_type = opt_data[0];
00248     if ((opt_type == PICO_DHCPOPT_LEASETIME) && (opt_len == 4)){
00249       memcpy(&cli->lease_time, opt_data, 4);
00250       cli->lease_time = long_be(cli->lease_time);
00251     }
00252     if ((opt_type == PICO_DHCPOPT_RENEWALTIME) && (opt_len == 4)){
00253       memcpy(&cli->T1, opt_data, 4);
00254       cli->T1 = long_be(cli->T1);
00255       T1_set =1;
00256     }
00257     if ((opt_type == PICO_DHCPOPT_REBINDINGTIME) && (opt_len == 4)){
00258       memcpy(&cli->T2, opt_data, 4);
00259       cli->T2 = long_be(cli->T2);
00260       T2_set =1;
00261     }
00262     if ((opt_type == PICO_DHCPOPT_ROUTER) && (opt_len == 4)) //XXX assuming only one router will be advertised...
00263       memcpy(&cli->gateway.addr, opt_data, 4);
00264     if ((opt_type == PICO_DHCPOPT_DNS) && (opt_len == 4))
00265       memcpy(&cli->nameserver.addr, opt_data, 4);
00266     if ((opt_type == PICO_DHCPOPT_NETMASK) && (opt_len == 4))
00267       memcpy(&cli->netmask.addr, opt_data, 4);
00268     if ((opt_type == PICO_DHCPOPT_SERVERID) && (opt_len == 4))
00269       memcpy(&cli->server_id.addr, opt_data, 4);
00270     if (opt_type == PICO_DHCPOPT_OPTIONOVERLOAD)
00271       dbg("DHCPC: WARNING: option overload present (not processed)");
00272 
00273     opt_len = 20;
00274     opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
00275   }
00276 
00277   /* default values for T1 and T2 if necessary */
00278   if(T1_set != 1)
00279     cli->T1 = cli->lease_time >> 1;
00280   if(T2_set != 1)
00281     cli->T2 = (cli->lease_time * 875) / 1000;
00282 
00283 
00284 
00285   if ((msg_type != PICO_DHCP_MSG_OFFER) || !cli->lease_time || !cli->netmask.addr || !cli->server_id.addr )
00286     return 0;
00287 
00288 
00289   dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
00290   cli->state = DHCPSTATE_REQUEST;
00291   return 1;
00292 }
00293 
00294 static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
00295 {
00296   struct pico_ip4 address = {0};
00297 
00298   if(cli->link_added == 0){
00299     /* close the socket bound on address 0.0.0.0 */
00300     pico_socket_close(cli->socket);
00301     cli->socket = NULL;
00302     pico_ipv4_link_del(cli->device, address);
00303     pico_ipv4_link_add(cli->device, cli->address, cli->netmask);
00304     cli->link_added = 1;
00305   }
00306   cli->state = DHCPSTATE_BOUND;
00307 
00308   dbg("DHCPC: T1: %d\n",cli->T1);
00309   dbg("DHCPC: T2: %d\n",cli->T2);
00310   dbg("DHCPC: lease time: %d\n",cli->lease_time);
00311 
00312   if(cli->timer_param_1)
00313     cli->timer_param_1->valid = 0;
00314   if(cli->timer_param_2)
00315     cli->timer_param_2->valid = 0;
00316   if(cli->timer_param_lease)
00317     cli->timer_param_lease->valid = 0;
00318   if(cli->timer_param_retransmit)
00319     cli->timer_param_retransmit->valid = 0;
00320 
00321 
00322   cli->timer_param_1 = pico_zalloc(sizeof(struct dhcp_timer_param));
00323   if(!cli->timer_param_1){
00324     if(cli->cb != NULL){
00325       pico_err = PICO_ERR_ENOMEM;
00326       cli->cb(cli, PICO_DHCP_ERROR);
00327     }
00328     return 0;
00329   }
00330   cli->timer_param_2 = pico_zalloc(sizeof(struct dhcp_timer_param));
00331   if(!cli->timer_param_2){
00332     if(cli->cb != NULL){
00333       pico_err = PICO_ERR_ENOMEM;
00334       cli->cb(cli, PICO_DHCP_ERROR);
00335     }
00336     return 0;
00337   }
00338   cli->timer_param_lease = pico_zalloc(sizeof(struct dhcp_timer_param));
00339   if(!cli->timer_param_lease){
00340     if(cli->cb != NULL){
00341       pico_err = PICO_ERR_ENOMEM;
00342       cli->cb(cli, PICO_DHCP_ERROR);
00343     }
00344     return 0;
00345   }
00346   cli->timer_param_1->valid = 1;
00347   cli->timer_param_2->valid = 1;
00348   cli->timer_param_lease->valid = 1;
00349 
00350   cli->timer_param_1->cli = cli;
00351   cli->timer_param_2->cli = cli;
00352   cli->timer_param_lease->cli = cli;
00353 
00354   cli->timer_param_1->type = PICO_DHCP_EVENT_T1;
00355   cli->timer_param_2->type = PICO_DHCP_EVENT_T2;
00356   cli->timer_param_lease->type = PICO_DHCP_EVENT_LEASE;
00357   //add timer
00358   pico_timer_add(cli->T1*1000, dhcp_timer_cb, cli->timer_param_1);
00359   pico_timer_add(cli->T2*1000, dhcp_timer_cb, cli->timer_param_2);
00360   pico_timer_add(cli->lease_time*1000, dhcp_timer_cb, cli->timer_param_lease);
00361 
00362   *(cli->xid_user) = cli->xid;
00363   if(cli->cb != NULL)
00364     cli->cb(cli, PICO_DHCP_SUCCESS);
00365   else
00366     dbg("DHCPC: no callback\n");
00367 
00368   dhcp_client_mutex++;
00369   cli->state = DHCPSTATE_BOUND;
00370   return 0;
00371 }
00372 
00373 static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
00374 {
00375   uint16_t port = PICO_DHCP_CLIENT_PORT;
00376 
00377   /* open and bind to currently acquired address */
00378   if (!cli->socket){
00379     cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup);
00380     if (!cli->socket) {
00381       dbg("DHCPC: error opening socket on renew: %s\n", strerror(pico_err));
00382       if(cli->cb != NULL)
00383         cli->cb(cli, PICO_DHCP_ERROR);
00384       return -1;
00385     }
00386     if (pico_socket_bind(cli->socket, &cli->address, &port) != 0){
00387       dbg("DHCPC: error binding socket on renew: %s\n", strerror(pico_err));
00388       pico_socket_close(cli->socket);
00389       if(cli->cb != NULL)
00390         cli->cb(cli, PICO_DHCP_ERROR);
00391       return -1;
00392     }
00393   }
00394   cli->state = DHCPSTATE_RENEWING;
00395   dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
00396 
00397   return 0;
00398 }
00399 
00400 static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
00401 {
00402   if(cli->cb != NULL)
00403     cli->cb(cli, PICO_DHCP_RESET);
00404   //reset pretty much everything
00405 
00406   if(cli->timer_param_1)
00407     cli->timer_param_1->valid = 0;
00408   if(cli->timer_param_2)
00409     cli->timer_param_2->valid = 0;
00410   if(cli->timer_param_lease)
00411     cli->timer_param_lease->valid = 0;
00412   if(cli->timer_param_retransmit)
00413     cli->timer_param_retransmit->valid = 0;
00414 
00415   pico_socket_close(cli->socket);
00416   pico_ipv4_link_del(cli->device, cli->address);
00417 
00418   //initiate negotiations again
00419   init_cookie(cli);
00420   pico_dhcp_retry(cli);
00421   dhclient_send(cli, PICO_DHCP_MSG_DISCOVER);
00422 
00423   return 0;
00424 
00425 }
00426 
00427 static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
00428 {
00429   pico_dhcp_retry(cli);
00430 
00431   if(cli->state == DHCPSTATE_DISCOVER)
00432     dhclient_send(cli, PICO_DHCP_MSG_DISCOVER);
00433   else if(cli->state == DHCPSTATE_RENEWING)
00434     dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
00435   else
00436     dbg("DHCPC: WARNING: should not get here in state %d!\n", cli->state);
00437 
00438   return 0;
00439 
00440 }
00441 
00442 /**********************
00443  * fsm implementation *
00444  **********************/
00445 
00446 struct dhcp_action_entry {
00447   uint16_t tcpstate;
00448   int (*offer)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00449   int (*ack)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00450   int (*nak)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00451   int (*timer1)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00452   int (*timer_lease)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00453   int (*timer_retransmit)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
00454 };
00455 
00456 static struct dhcp_action_entry dhcp_fsm[] = {
00457     /* State             offer       ack       nak     timer1  timer_lease timer_retransmit*/
00458   { DHCPSTATE_DISCOVER,  recv_offer, NULL,     NULL,   NULL,   reset,      retransmit},
00459   { DHCPSTATE_OFFER,     NULL,       NULL,     NULL,   NULL,   reset,      NULL},
00460   { DHCPSTATE_REQUEST,   NULL,       recv_ack, reset,  NULL,   reset,      retransmit},
00461   { DHCPSTATE_BOUND,     NULL,       NULL,     reset,  renew,  reset,      NULL},
00462   { DHCPSTATE_RENEWING,  NULL,       recv_ack, reset,  NULL,   reset,      retransmit},
00463 };
00464 
00465 
00466 static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len)
00467 {
00468   dbg("DHCPC: received incoming event of type %d\n", type);
00469   switch(type){
00470     case PICO_DHCP_MSG_OFFER:
00471       if(dhcp_fsm[cli->state].offer != NULL)
00472         dhcp_fsm[cli->state].offer(cli, data, len);
00473       break;
00474     case PICO_DHCP_MSG_ACK:
00475       if(dhcp_fsm[cli->state].ack != NULL){
00476         dhcp_fsm[cli->state].ack(cli, data, len);
00477       }
00478       break;
00479     case PICO_DHCP_MSG_NAK:
00480       if(dhcp_fsm[cli->state].nak!= NULL){
00481         dhcp_fsm[cli->state].nak(cli, data, len);
00482       }
00483       break;
00484     case PICO_DHCP_EVENT_T1:
00485       if(dhcp_fsm[cli->state].timer1!= NULL){
00486         dhcp_fsm[cli->state].timer1(cli, NULL, 0);
00487       }
00488       break;
00489     case PICO_DHCP_EVENT_LEASE:
00490       if(dhcp_fsm[cli->state].timer_lease!= NULL){
00491         dhcp_fsm[cli->state].timer_lease(cli, NULL, 0);
00492       }
00493       break;
00494     case PICO_DHCP_EVENT_RETRANSMIT:
00495       if(dhcp_fsm[cli->state].timer_retransmit!= NULL){
00496         dhcp_fsm[cli->state].timer_retransmit(cli, NULL, 0);
00497       }
00498       break;
00499     default:
00500       dbg("DHCPC: event not supported yet!!\n");
00501       break;
00502   }
00503 }
00504 
00505 
00506 /*********************
00507  * utility functions *
00508  *********************/
00509 
00510 static void pico_dhcp_retry(struct pico_dhcp_client_cookie *cli)
00511 {
00512   const int MAX_RETRY = 3;
00513   uint32_t new_xid;
00514   if (++cli->attempt > MAX_RETRY) {
00515     cli->start_time = pico_tick;
00516     cli->attempt = 0;
00517      new_xid = pico_rand();
00518     while(get_cookie_by_xid(new_xid) != NULL){
00519       new_xid = pico_rand();
00520     }
00521     cli->xid = new_xid;
00522     cli->state = DHCPSTATE_DISCOVER;
00523   }
00524 }
00525 
00526 static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type)
00527 {
00528   uint8_t buf_out[DHCPC_DATAGRAM_SIZE] = {0};
00529   struct pico_dhcphdr *dh_out = (struct pico_dhcphdr *) buf_out;
00530   int sent = 0;
00531   int i = 0;
00532   struct pico_ip4 destination;
00533   uint16_t port = PICO_DHCPD_PORT;
00534   if(cli->state == DHCPSTATE_BOUND || cli->state == DHCPSTATE_RENEWING){
00535     destination.addr = cli->server_id.addr;
00536   }else{
00537     destination.addr = long_be(0xFFFFFFFF);
00538   }
00539 
00540   if(cli->device->eth == NULL){
00541     pico_err = PICO_ERR_EOPNOTSUPP;
00542     if(cli->cb != NULL){
00543       cli->cb(cli, PICO_DHCP_ERROR);
00544     }
00545     return -1;
00546   }
00547   memcpy(dh_out->hwaddr, &cli->device->eth->mac, PICO_HLEN_ETHER);
00548   dh_out->op = PICO_DHCP_OP_REQUEST;
00549   dh_out->htype = PICO_HTYPE_ETHER;
00550   dh_out->hlen = PICO_HLEN_ETHER;
00551   dh_out->xid = cli->xid;
00552   dh_out->secs = (msg_type == PICO_DHCP_MSG_REQUEST)?0:short_be((pico_tick - cli->start_time)/1000);
00553   dh_out->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
00554   if (cli->state == DHCPSTATE_RENEWING)
00555     dh_out->ciaddr = cli->address.addr;
00556 
00557   /* Option: msg type, len 1 */
00558   dh_out->options[i++] = PICO_DHCPOPT_MSGTYPE;
00559   dh_out->options[i++] = 1;
00560   dh_out->options[i++] = msg_type;
00561 
00562   if (( msg_type == PICO_DHCP_MSG_REQUEST) && ( cli->state != DHCPSTATE_RENEWING )) {
00563     dh_out->options[i++] = PICO_DHCPOPT_REQIP;
00564     dh_out->options[i++] = 4;
00565     dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF000000) >> 24;
00566     dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF0000) >> 16;
00567     dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF00) >> 8;
00568     dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF);
00569     dh_out->options[i++] = PICO_DHCPOPT_SERVERID;
00570     dh_out->options[i++] = 4;
00571     dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF000000) >> 24;
00572     dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF0000) >> 16;
00573     dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF00) >> 8;
00574     dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF);
00575   }
00576 
00577   /* Option: req list, len 4 */
00578   dh_out->options[i++] = PICO_DHCPOPT_PARMLIST;
00579   dh_out->options[i++] = 7;
00580   dh_out->options[i++] = PICO_DHCPOPT_NETMASK;
00581   dh_out->options[i++] = PICO_DHCPOPT_BCAST;
00582   dh_out->options[i++] = PICO_DHCPOPT_TIME;
00583   dh_out->options[i++] = PICO_DHCPOPT_ROUTER;
00584   dh_out->options[i++] = PICO_DHCPOPT_HOSTNAME;
00585   dh_out->options[i++] = PICO_DHCPOPT_RENEWALTIME;
00586   dh_out->options[i++] = PICO_DHCPOPT_REBINDINGTIME;
00587 
00588   /* Option : max message size */
00589   if( msg_type == PICO_DHCP_MSG_REQUEST || msg_type == PICO_DHCP_MSG_DISCOVER){
00590     uint16_t dds = DHCPC_DATAGRAM_SIZE;
00591     dh_out->options[i++] = PICO_DHCPOPT_MAXMSGSIZE;
00592     dh_out->options[i++] = 2;
00593     dh_out->options[i++] = (dds & 0xFF00) >> 8;
00594     dh_out->options[i++] = (dds & 0xFF);
00595   }
00596 
00597 
00598 
00599   dh_out->options[i] = PICO_DHCPOPT_END;
00600   sent = pico_socket_sendto(cli->socket, buf_out, DHCPC_DATAGRAM_SIZE, &destination, port);
00601   if (sent < 0) {
00602     dbg("DHCPC: sendto failed: %s\n", strerror(pico_err));
00603     if(cli->cb != NULL)
00604       cli->cb(cli, PICO_DHCP_ERROR);
00605   }
00606 
00607 
00608   //resend-timer :
00609   if(cli->timer_param_retransmit != NULL)
00610     cli->timer_param_retransmit->valid=0;
00611 
00612   cli->timer_param_retransmit = pico_zalloc(sizeof(struct dhcp_timer_param));
00613   if(!cli->timer_param_retransmit){
00614     if(cli->cb != NULL)
00615       pico_err = PICO_ERR_ENOMEM;
00616       cli->cb(cli, PICO_DHCP_ERROR);
00617     return -1;
00618   }
00619   cli->timer_param_retransmit->valid = 1;
00620   cli->timer_param_retransmit->cli = cli;
00621   cli->timer_param_retransmit->type = PICO_DHCP_EVENT_RETRANSMIT;
00622   pico_timer_add(4000, dhcp_timer_cb, cli->timer_param_retransmit);
00623 
00624   return 0;
00625 }
00626 
00627 //identifies type & does some preprocessing : checking if everything is valid
00628 static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli)
00629 {
00630   struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
00631   uint8_t *nextopt, opt_data[20], opt_type;
00632   int opt_len = 20;
00633 
00634   if (dhdr->xid != cli->xid)
00635     return 0;
00636 
00637   if (!is_options_valid(dhdr->options, len - sizeof(struct pico_dhcphdr)))
00638     return 0;
00639 
00640   if( dhdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE)
00641     return 0;
00642 
00643   opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
00644   while (opt_type != PICO_DHCPOPT_END) {
00645     /* parse interesting options here */
00646     if (opt_type == PICO_DHCPOPT_MSGTYPE) {
00647       return *opt_data;
00648     }
00649     opt_len = 20;
00650     opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
00651   }
00652   return 0;
00653 
00654 }
00655 
00656 static int init_cookie(struct pico_dhcp_client_cookie* cli)
00657 {
00658   uint8_t n = 3;
00659   uint16_t port = PICO_DHCP_CLIENT_PORT;
00660   struct pico_ip4 address, netmask;
00661 
00662   address.addr = long_be(0x00000000);
00663   netmask.addr = long_be(0x00000000);
00664 
00665   cli->state = DHCPSTATE_DISCOVER;
00666   cli->start_time = pico_tick;
00667   cli->attempt = 0;
00668 
00669   cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup);
00670   if (!cli->socket) {
00671     dbg("DHCPC: error opening socket: %s\n", strerror(pico_err));
00672     if(cli->cb != NULL)
00673       cli->cb(cli, PICO_DHCP_ERROR);
00674     return -1;
00675   }
00676   if (pico_socket_bind(cli->socket, &address, &port) != 0){
00677     dbg("DHCPC: error binding socket: %s\n", strerror(pico_err));
00678     pico_socket_close(cli->socket);
00679     if(cli->cb != NULL)
00680       cli->cb(cli, PICO_DHCP_ERROR);
00681     return -1;
00682   }
00683   cli->socket->dev = cli->device;
00684 
00685   if(pico_ipv4_link_add(cli->device, address, netmask) != 0){
00686     dbg("DHCPC: error adding link: %s\n", strerror(pico_err));
00687     if(cli->cb != NULL)
00688       cli->cb(cli, PICO_DHCP_ERROR);
00689     return -1;
00690   }
00691 
00692   /* attempt to generate a correct xid 3 times, then fail */
00693   do {
00694     cli->xid = pico_rand();
00695   } while (!cli->xid && --n);
00696   if (!cli->xid) {
00697     if(cli->cb != NULL)
00698       cli->cb(cli, PICO_DHCP_ERROR);
00699     return -1;
00700   }
00701 
00702   return 0;
00703 }
00704 
00705 static struct pico_dhcp_client_cookie *get_cookie_by_xid(uint32_t xid)
00706 {
00707   struct pico_dhcp_client_cookie test = { }, *cookie = NULL;
00708 
00709   if (!xid)
00710     return NULL;
00711 
00712   test.xid = xid;
00713   cookie = pico_tree_findKey(&DHCPCookies, &test);
00714   if (!cookie)
00715     return NULL;
00716   else
00717     return cookie;
00718 }
00719 
00720 void *pico_dhcp_get_identifier(uint32_t xid)
00721 {
00722   return (void *) get_cookie_by_xid(xid);
00723 }
00724 
00725 static uint32_t get_xid(uint8_t* data)
00726 {
00727   struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
00728   return dhdr->xid;
00729 }
00730 
00731 #endif