Free (GPLv2) TCP/IP stack developed by TASS Belgium
Fork of PicoTCP by
modules/pico_dhcp_client.c
- Committer:
- daniele
- Date:
- 2013-06-16
- Revision:
- 29:1a47b7151851
File content as of revision 29:1a47b7151851:
/********************************************************************* PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. See LICENSE and COPYING for usage. Authors: Frederik Van Slycken, Kristof Roelants *********************************************************************/ #include "pico_dhcp_client.h" #include "pico_stack.h" #include "pico_config.h" #include "pico_device.h" #include "pico_ipv4.h" #include "pico_socket.h" #ifdef PICO_SUPPORT_DHCPC /*********** * structs * ***********/ static uint8_t dhcp_client_mutex = 1; /* to serialize client negotations if multiple devices */ struct dhcp_timer_param{ uint16_t type; struct pico_dhcp_client_cookie* cli; int valid; }; struct pico_dhcp_client_cookie { uint32_t xid; uint32_t *xid_user; struct pico_ip4 address; struct pico_ip4 netmask; struct pico_ip4 gateway; struct pico_ip4 nameserver; struct pico_ip4 server_id; uint32_t lease_time; uint32_t T1; uint32_t T2; struct pico_socket* socket; int connected; struct pico_device* device; unsigned long start_time; int attempt; enum dhcp_negotiation_state state; void (*cb)(void* cli, int code); struct dhcp_timer_param* timer_param_1; struct dhcp_timer_param* timer_param_2; struct dhcp_timer_param* timer_param_lease; struct dhcp_timer_param* timer_param_retransmit; int link_added; }; static int dhcp_cookies_cmp(void *ka, void *kb) { struct pico_dhcp_client_cookie *a = ka, *b = kb; if (a->xid < b->xid) return -1; else if (a->xid > b->xid) return 1; else return 0; } PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp); /************************* * function declarations * *************************/ static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len); static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg); //cb static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s); static void dhcp_timer_cb(unsigned long tick, void* param); //util static void pico_dhcp_retry(struct pico_dhcp_client_cookie *client); static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type); static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli); static int init_cookie(struct pico_dhcp_client_cookie* cli); static struct pico_dhcp_client_cookie* get_cookie_by_xid(uint32_t xid); static uint32_t get_xid(uint8_t* data); //fsm functions static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); //fsm implementation static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len); /*************** * entry point * ***************/ static uint32_t pico_dhcp_execute_init(struct pico_dhcp_client_cookie *cli) { if (!dhcp_client_mutex) { pico_timer_add(3000, pico_dhcp_reinitiate_negotiation, cli); return 0; } dhcp_client_mutex--; if (init_cookie(cli) < 0) return -1; dbg("DHCPC: cookie with xid %u\n", cli->xid); if (pico_tree_insert(&DHCPCookies, cli)) { pico_err = PICO_ERR_EAGAIN; if(cli->cb != NULL) { cli->cb(cli, PICO_DHCP_ERROR); } pico_free(cli); return -1; /* Element key already exists */ } if (dhclient_send(cli, PICO_DHCP_MSG_DISCOVER) < 0) return -1; return 0; } /* 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 */ int pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void *cli, int code), uint32_t *xid) { struct pico_dhcp_client_cookie *cli; if(!device || !callback || !xid){ pico_err = PICO_ERR_EINVAL; return -1; } cli = pico_zalloc(sizeof(struct pico_dhcp_client_cookie)); if(!cli){ pico_err = PICO_ERR_ENOMEM; return -1; } cli->device = device; cli->cb = callback; cli->xid_user = xid; *(cli->xid_user) = 0; return pico_dhcp_execute_init(cli); } static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg) { struct pico_dhcp_client_cookie *cli = (struct pico_dhcp_client_cookie *) arg; pico_dhcp_execute_init(cli); return; } /******************** * access functions * ********************/ struct pico_ip4 pico_dhcp_get_address(void* cli) { return ((struct pico_dhcp_client_cookie*)cli)->address; } struct pico_ip4 pico_dhcp_get_gateway(void* cli) { return ((struct pico_dhcp_client_cookie*)cli)->gateway; } struct pico_ip4 pico_dhcp_get_nameserver(void* cli) { return ((struct pico_dhcp_client_cookie*)cli)->nameserver; } /************* * callbacks * *************/ static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s) { uint8_t buf[DHCPC_DATAGRAM_SIZE]; int r=0; uint32_t peer; uint16_t port; int type; struct pico_dhcp_client_cookie *cli; dbg("DHCPC: called dhcp_wakeup\n"); if (ev == PICO_SOCK_EV_RD) { do { r = pico_socket_recvfrom(s, buf, DHCPC_DATAGRAM_SIZE, &peer, &port); cli = get_cookie_by_xid(get_xid(buf)); if(cli == NULL) return; if (r > 0 && port == PICO_DHCPD_PORT) { type = pico_dhcp_verify_and_identify_type(buf, r, cli); pico_dhcp_state_machine(type, cli, buf, r); } } while(r>0); } } static void dhcp_timer_cb(unsigned long tick, void* param) { struct dhcp_timer_param* param2 = (struct dhcp_timer_param*) param; if(param2->valid == 1){ //dbg("called timer cb on active timer type %d\n",param2->type); pico_dhcp_state_machine(param2->type, param2->cli, NULL, 0); } if(param2->cli->timer_param_1 == param){ param2->cli->timer_param_1 = NULL; } if(param2->cli->timer_param_2 == param){ param2->cli->timer_param_2 = NULL; } if(param2->cli->timer_param_lease == param){ param2->cli->timer_param_lease = NULL; } if(param2->cli->timer_param_retransmit == param){ param2->cli->timer_param_retransmit = NULL; } pico_free(param); } /***************** * fsm functions * *****************/ static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len) { struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data; uint8_t *nextopt, opt_data[20], opt_type; int opt_len = 20; uint8_t msg_type = 0xFF; int T1_set = 0; int T2_set = 0; cli->address.addr = dhdr->yiaddr; opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt); while (opt_type != PICO_DHCPOPT_END) { if (opt_type == PICO_DHCPOPT_MSGTYPE) msg_type = opt_data[0]; if ((opt_type == PICO_DHCPOPT_LEASETIME) && (opt_len == 4)){ memcpy(&cli->lease_time, opt_data, 4); cli->lease_time = long_be(cli->lease_time); } if ((opt_type == PICO_DHCPOPT_RENEWALTIME) && (opt_len == 4)){ memcpy(&cli->T1, opt_data, 4); cli->T1 = long_be(cli->T1); T1_set =1; } if ((opt_type == PICO_DHCPOPT_REBINDINGTIME) && (opt_len == 4)){ memcpy(&cli->T2, opt_data, 4); cli->T2 = long_be(cli->T2); T2_set =1; } if ((opt_type == PICO_DHCPOPT_ROUTER) && (opt_len == 4)) //XXX assuming only one router will be advertised... memcpy(&cli->gateway.addr, opt_data, 4); if ((opt_type == PICO_DHCPOPT_DNS) && (opt_len == 4)) memcpy(&cli->nameserver.addr, opt_data, 4); if ((opt_type == PICO_DHCPOPT_NETMASK) && (opt_len == 4)) memcpy(&cli->netmask.addr, opt_data, 4); if ((opt_type == PICO_DHCPOPT_SERVERID) && (opt_len == 4)) memcpy(&cli->server_id.addr, opt_data, 4); if (opt_type == PICO_DHCPOPT_OPTIONOVERLOAD) dbg("DHCPC: WARNING: option overload present (not processed)"); opt_len = 20; opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt); } /* default values for T1 and T2 if necessary */ if(T1_set != 1) cli->T1 = cli->lease_time >> 1; if(T2_set != 1) cli->T2 = (cli->lease_time * 875) / 1000; if ((msg_type != PICO_DHCP_MSG_OFFER) || !cli->lease_time || !cli->netmask.addr || !cli->server_id.addr ) return 0; dhclient_send(cli, PICO_DHCP_MSG_REQUEST); cli->state = DHCPSTATE_REQUEST; return 1; } static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len) { struct pico_ip4 address = {0}; if(cli->link_added == 0){ /* close the socket bound on address 0.0.0.0 */ pico_socket_close(cli->socket); cli->socket = NULL; pico_ipv4_link_del(cli->device, address); pico_ipv4_link_add(cli->device, cli->address, cli->netmask); cli->link_added = 1; } cli->state = DHCPSTATE_BOUND; dbg("DHCPC: T1: %d\n",cli->T1); dbg("DHCPC: T2: %d\n",cli->T2); dbg("DHCPC: lease time: %d\n",cli->lease_time); if(cli->timer_param_1) cli->timer_param_1->valid = 0; if(cli->timer_param_2) cli->timer_param_2->valid = 0; if(cli->timer_param_lease) cli->timer_param_lease->valid = 0; if(cli->timer_param_retransmit) cli->timer_param_retransmit->valid = 0; cli->timer_param_1 = pico_zalloc(sizeof(struct dhcp_timer_param)); if(!cli->timer_param_1){ if(cli->cb != NULL){ pico_err = PICO_ERR_ENOMEM; cli->cb(cli, PICO_DHCP_ERROR); } return 0; } cli->timer_param_2 = pico_zalloc(sizeof(struct dhcp_timer_param)); if(!cli->timer_param_2){ if(cli->cb != NULL){ pico_err = PICO_ERR_ENOMEM; cli->cb(cli, PICO_DHCP_ERROR); } return 0; } cli->timer_param_lease = pico_zalloc(sizeof(struct dhcp_timer_param)); if(!cli->timer_param_lease){ if(cli->cb != NULL){ pico_err = PICO_ERR_ENOMEM; cli->cb(cli, PICO_DHCP_ERROR); } return 0; } cli->timer_param_1->valid = 1; cli->timer_param_2->valid = 1; cli->timer_param_lease->valid = 1; cli->timer_param_1->cli = cli; cli->timer_param_2->cli = cli; cli->timer_param_lease->cli = cli; cli->timer_param_1->type = PICO_DHCP_EVENT_T1; cli->timer_param_2->type = PICO_DHCP_EVENT_T2; cli->timer_param_lease->type = PICO_DHCP_EVENT_LEASE; //add timer pico_timer_add(cli->T1*1000, dhcp_timer_cb, cli->timer_param_1); pico_timer_add(cli->T2*1000, dhcp_timer_cb, cli->timer_param_2); pico_timer_add(cli->lease_time*1000, dhcp_timer_cb, cli->timer_param_lease); *(cli->xid_user) = cli->xid; if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_SUCCESS); else dbg("DHCPC: no callback\n"); dhcp_client_mutex++; cli->state = DHCPSTATE_BOUND; return 0; } static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len) { uint16_t port = PICO_DHCP_CLIENT_PORT; /* open and bind to currently acquired address */ if (!cli->socket){ cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup); if (!cli->socket) { dbg("DHCPC: error opening socket on renew: %s\n", strerror(pico_err)); if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_ERROR); return -1; } if (pico_socket_bind(cli->socket, &cli->address, &port) != 0){ dbg("DHCPC: error binding socket on renew: %s\n", strerror(pico_err)); pico_socket_close(cli->socket); if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_ERROR); return -1; } } cli->state = DHCPSTATE_RENEWING; dhclient_send(cli, PICO_DHCP_MSG_REQUEST); return 0; } static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len) { if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_RESET); //reset pretty much everything if(cli->timer_param_1) cli->timer_param_1->valid = 0; if(cli->timer_param_2) cli->timer_param_2->valid = 0; if(cli->timer_param_lease) cli->timer_param_lease->valid = 0; if(cli->timer_param_retransmit) cli->timer_param_retransmit->valid = 0; pico_socket_close(cli->socket); pico_ipv4_link_del(cli->device, cli->address); //initiate negotiations again init_cookie(cli); pico_dhcp_retry(cli); dhclient_send(cli, PICO_DHCP_MSG_DISCOVER); return 0; } static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len) { pico_dhcp_retry(cli); if(cli->state == DHCPSTATE_DISCOVER) dhclient_send(cli, PICO_DHCP_MSG_DISCOVER); else if(cli->state == DHCPSTATE_RENEWING) dhclient_send(cli, PICO_DHCP_MSG_REQUEST); else dbg("DHCPC: WARNING: should not get here in state %d!\n", cli->state); return 0; } /********************** * fsm implementation * **********************/ struct dhcp_action_entry { uint16_t tcpstate; int (*offer)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); int (*ack)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); int (*nak)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); int (*timer1)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); int (*timer_lease)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); int (*timer_retransmit)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len); }; static struct dhcp_action_entry dhcp_fsm[] = { /* State offer ack nak timer1 timer_lease timer_retransmit*/ { DHCPSTATE_DISCOVER, recv_offer, NULL, NULL, NULL, reset, retransmit}, { DHCPSTATE_OFFER, NULL, NULL, NULL, NULL, reset, NULL}, { DHCPSTATE_REQUEST, NULL, recv_ack, reset, NULL, reset, retransmit}, { DHCPSTATE_BOUND, NULL, NULL, reset, renew, reset, NULL}, { DHCPSTATE_RENEWING, NULL, recv_ack, reset, NULL, reset, retransmit}, }; static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len) { dbg("DHCPC: received incoming event of type %d\n", type); switch(type){ case PICO_DHCP_MSG_OFFER: if(dhcp_fsm[cli->state].offer != NULL) dhcp_fsm[cli->state].offer(cli, data, len); break; case PICO_DHCP_MSG_ACK: if(dhcp_fsm[cli->state].ack != NULL){ dhcp_fsm[cli->state].ack(cli, data, len); } break; case PICO_DHCP_MSG_NAK: if(dhcp_fsm[cli->state].nak!= NULL){ dhcp_fsm[cli->state].nak(cli, data, len); } break; case PICO_DHCP_EVENT_T1: if(dhcp_fsm[cli->state].timer1!= NULL){ dhcp_fsm[cli->state].timer1(cli, NULL, 0); } break; case PICO_DHCP_EVENT_LEASE: if(dhcp_fsm[cli->state].timer_lease!= NULL){ dhcp_fsm[cli->state].timer_lease(cli, NULL, 0); } break; case PICO_DHCP_EVENT_RETRANSMIT: if(dhcp_fsm[cli->state].timer_retransmit!= NULL){ dhcp_fsm[cli->state].timer_retransmit(cli, NULL, 0); } break; default: dbg("DHCPC: event not supported yet!!\n"); break; } } /********************* * utility functions * *********************/ static void pico_dhcp_retry(struct pico_dhcp_client_cookie *cli) { const int MAX_RETRY = 3; uint32_t new_xid; if (++cli->attempt > MAX_RETRY) { cli->start_time = pico_tick; cli->attempt = 0; new_xid = pico_rand(); while(get_cookie_by_xid(new_xid) != NULL){ new_xid = pico_rand(); } cli->xid = new_xid; cli->state = DHCPSTATE_DISCOVER; } } static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type) { uint8_t buf_out[DHCPC_DATAGRAM_SIZE] = {0}; struct pico_dhcphdr *dh_out = (struct pico_dhcphdr *) buf_out; int sent = 0; int i = 0; struct pico_ip4 destination; uint16_t port = PICO_DHCPD_PORT; if(cli->state == DHCPSTATE_BOUND || cli->state == DHCPSTATE_RENEWING){ destination.addr = cli->server_id.addr; }else{ destination.addr = long_be(0xFFFFFFFF); } if(cli->device->eth == NULL){ pico_err = PICO_ERR_EOPNOTSUPP; if(cli->cb != NULL){ cli->cb(cli, PICO_DHCP_ERROR); } return -1; } memcpy(dh_out->hwaddr, &cli->device->eth->mac, PICO_HLEN_ETHER); dh_out->op = PICO_DHCP_OP_REQUEST; dh_out->htype = PICO_HTYPE_ETHER; dh_out->hlen = PICO_HLEN_ETHER; dh_out->xid = cli->xid; dh_out->secs = (msg_type == PICO_DHCP_MSG_REQUEST)?0:short_be((pico_tick - cli->start_time)/1000); dh_out->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE; if (cli->state == DHCPSTATE_RENEWING) dh_out->ciaddr = cli->address.addr; /* Option: msg type, len 1 */ dh_out->options[i++] = PICO_DHCPOPT_MSGTYPE; dh_out->options[i++] = 1; dh_out->options[i++] = msg_type; if (( msg_type == PICO_DHCP_MSG_REQUEST) && ( cli->state != DHCPSTATE_RENEWING )) { dh_out->options[i++] = PICO_DHCPOPT_REQIP; dh_out->options[i++] = 4; dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF000000) >> 24; dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF0000) >> 16; dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF00) >> 8; dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF); dh_out->options[i++] = PICO_DHCPOPT_SERVERID; dh_out->options[i++] = 4; dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF000000) >> 24; dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF0000) >> 16; dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF00) >> 8; dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF); } /* Option: req list, len 4 */ dh_out->options[i++] = PICO_DHCPOPT_PARMLIST; dh_out->options[i++] = 7; dh_out->options[i++] = PICO_DHCPOPT_NETMASK; dh_out->options[i++] = PICO_DHCPOPT_BCAST; dh_out->options[i++] = PICO_DHCPOPT_TIME; dh_out->options[i++] = PICO_DHCPOPT_ROUTER; dh_out->options[i++] = PICO_DHCPOPT_HOSTNAME; dh_out->options[i++] = PICO_DHCPOPT_RENEWALTIME; dh_out->options[i++] = PICO_DHCPOPT_REBINDINGTIME; /* Option : max message size */ if( msg_type == PICO_DHCP_MSG_REQUEST || msg_type == PICO_DHCP_MSG_DISCOVER){ uint16_t dds = DHCPC_DATAGRAM_SIZE; dh_out->options[i++] = PICO_DHCPOPT_MAXMSGSIZE; dh_out->options[i++] = 2; dh_out->options[i++] = (dds & 0xFF00) >> 8; dh_out->options[i++] = (dds & 0xFF); } dh_out->options[i] = PICO_DHCPOPT_END; sent = pico_socket_sendto(cli->socket, buf_out, DHCPC_DATAGRAM_SIZE, &destination, port); if (sent < 0) { dbg("DHCPC: sendto failed: %s\n", strerror(pico_err)); if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_ERROR); } //resend-timer : if(cli->timer_param_retransmit != NULL) cli->timer_param_retransmit->valid=0; cli->timer_param_retransmit = pico_zalloc(sizeof(struct dhcp_timer_param)); if(!cli->timer_param_retransmit){ if(cli->cb != NULL) pico_err = PICO_ERR_ENOMEM; cli->cb(cli, PICO_DHCP_ERROR); return -1; } cli->timer_param_retransmit->valid = 1; cli->timer_param_retransmit->cli = cli; cli->timer_param_retransmit->type = PICO_DHCP_EVENT_RETRANSMIT; pico_timer_add(4000, dhcp_timer_cb, cli->timer_param_retransmit); return 0; } //identifies type & does some preprocessing : checking if everything is valid static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli) { struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data; uint8_t *nextopt, opt_data[20], opt_type; int opt_len = 20; if (dhdr->xid != cli->xid) return 0; if (!is_options_valid(dhdr->options, len - sizeof(struct pico_dhcphdr))) return 0; if( dhdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE) return 0; opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt); while (opt_type != PICO_DHCPOPT_END) { /* parse interesting options here */ if (opt_type == PICO_DHCPOPT_MSGTYPE) { return *opt_data; } opt_len = 20; opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt); } return 0; } static int init_cookie(struct pico_dhcp_client_cookie* cli) { uint8_t n = 3; uint16_t port = PICO_DHCP_CLIENT_PORT; struct pico_ip4 address, netmask; address.addr = long_be(0x00000000); netmask.addr = long_be(0x00000000); cli->state = DHCPSTATE_DISCOVER; cli->start_time = pico_tick; cli->attempt = 0; cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup); if (!cli->socket) { dbg("DHCPC: error opening socket: %s\n", strerror(pico_err)); if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_ERROR); return -1; } if (pico_socket_bind(cli->socket, &address, &port) != 0){ dbg("DHCPC: error binding socket: %s\n", strerror(pico_err)); pico_socket_close(cli->socket); if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_ERROR); return -1; } cli->socket->dev = cli->device; if(pico_ipv4_link_add(cli->device, address, netmask) != 0){ dbg("DHCPC: error adding link: %s\n", strerror(pico_err)); if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_ERROR); return -1; } /* attempt to generate a correct xid 3 times, then fail */ do { cli->xid = pico_rand(); } while (!cli->xid && --n); if (!cli->xid) { if(cli->cb != NULL) cli->cb(cli, PICO_DHCP_ERROR); return -1; } return 0; } static struct pico_dhcp_client_cookie *get_cookie_by_xid(uint32_t xid) { struct pico_dhcp_client_cookie test = { }, *cookie = NULL; if (!xid) return NULL; test.xid = xid; cookie = pico_tree_findKey(&DHCPCookies, &test); if (!cookie) return NULL; else return cookie; } void *pico_dhcp_get_identifier(uint32_t xid) { return (void *) get_cookie_by_xid(xid); } static uint32_t get_xid(uint8_t* data) { struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data; return dhdr->xid; } #endif