Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of PicoTCP by
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
Generated on Thu Jul 14 2022 08:24:58 by
1.7.2
