diff --git a/CHANGELOG b/CHANGELOG index 52576c79..89ba1eae 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,7 +19,13 @@ HISTORY ++ New features: - 2010-01-30. simon Goldschmidt + 2010-01-31: Simon Goldschmidt + * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse + them once instead of parsing for every option. This also removes + the need for mem_malloc from dhcp_recv and makes it possible to + correctly retrieve the BOOTP file. + + 2010-01-30: simon Goldschmidt * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect the sockets array. diff --git a/src/core/dhcp.c b/src/core/dhcp.c index ebfcaa69..52877294 100644 --- a/src/core/dhcp.c +++ b/src/core/dhcp.c @@ -43,8 +43,6 @@ * with RFC 2131 and RFC 2132. * * TODO: - * - Proper parsing of DHCP messages exploiting file/sname field overloading. - * - Add JavaDoc style documentation (API, internals). * - Support for interfaces other than Ethernet (SLIP, PPP, ...) * * Please coordinate changes and requests with Leon Woestenberg @@ -104,16 +102,44 @@ #define REBOOT_TRIES 2 -/* DHCP client state machine functions */ -static void dhcp_handle_ack(struct netif *netif); -static void dhcp_handle_nak(struct netif *netif); -static void dhcp_handle_offer(struct netif *netif); +/** Option handling: options are parsed in dhcp_parse_reply + * and saved in an array where other functions can load them from. + * This might be moved into the struct dhcp (not necessarily since + * lwIP is single-threaded and the array is only used while in recv + * callback). */ +#define DHCP_OPTION_IDX_OVERLOAD 0 +#define DHCP_OPTION_IDX_MSG_TYPE 1 +#define DHCP_OPTION_IDX_SERVER_ID 2 +#define DHCP_OPTION_IDX_LEASE_TIME 3 +#define DHCP_OPTION_IDX_T1 4 +#define DHCP_OPTION_IDX_T2 5 +#define DHCP_OPTION_IDX_SUBNET_MASK 6 +#define DHCP_OPTION_IDX_ROUTER 7 +#define DHCP_OPTION_IDX_BROADCAST 8 +#define DHCP_OPTION_IDX_DNS_SERVER 9 +#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS) +/** Holds the decoded option values, only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX]; +/** Holds a flag which option was received and is contained in dhcp_rx_options_val, + only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; + +#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0) +#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1) +#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0) +#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given))) +#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx]) +#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val)) + + +/* DHCP client state machine functions */ static err_t dhcp_discover(struct netif *netif); static err_t dhcp_select(struct netif *netif); static void dhcp_bind(struct netif *netif); #if DHCP_DOES_ARP_CHECK -static void dhcp_check(struct netif *netif); static err_t dhcp_decline(struct netif *netif); #endif /* DHCP_DOES_ARP_CHECK */ static err_t dhcp_rebind(struct netif *netif); @@ -122,14 +148,6 @@ static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); /* receive, unfold, parse and free incoming messages */ static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); -static err_t dhcp_unfold_reply(struct dhcp *dhcp, struct pbuf *p); -static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type); -static u8_t dhcp_get_option_byte(u8_t *ptr); -#if 0 -static u16_t dhcp_get_option_short(u8_t *ptr); -#endif -static u32_t dhcp_get_option_long(u8_t *ptr); -static void dhcp_free_reply(struct dhcp *dhcp); /* set the DHCP timers */ static void dhcp_timeout(struct netif *netif); @@ -221,12 +239,11 @@ static void dhcp_handle_offer(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; - /* obtain the server address */ - u8_t *option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SERVER_ID); LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); - if (option_ptr != NULL) { - dhcp->server_ip_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + /* obtain the server address */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) { + dhcp->server_ip_addr.addr = htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)); LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", dhcp->server_ip_addr.addr)); /* remember offered address */ ip_addr_set(&dhcp->offered_ip_addr, (struct ip_addr *)&dhcp->msg_in->yiaddr); @@ -311,7 +328,6 @@ dhcp_select(struct netif *netif) /** * The DHCP timer that checks for lease renewal/rebind timeouts. - * */ void dhcp_coarse_tmr() @@ -344,7 +360,6 @@ dhcp_coarse_tmr() * * A DHCP server is expected to respond within a short period of time. * This timer checks whether an outstanding DHCP request is timed out. - * */ void dhcp_fine_tmr() @@ -362,7 +377,7 @@ dhcp_fine_tmr() netif->dhcp->request_timeout--; /* { netif->dhcp->request_timeout == 0 } */ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); - /* this clients' request timeout triggered */ + /* this client's request timeout triggered */ dhcp_timeout(netif); } } @@ -481,33 +496,34 @@ static void dhcp_handle_ack(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; - u8_t *option_ptr; + u8_t n; + /* clear options we might not get from the ACK */ dhcp->offered_sn_mask.addr = 0; dhcp->offered_gw_addr.addr = 0; dhcp->offered_bc_addr.addr = 0; +#if LWIP_DHCP_BOOTP_FILE + dhcp->offered_si_addr.addr = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ /* lease time given? */ - option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_LEASE_TIME); - if (option_ptr != NULL) { + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) { /* remember offered lease time */ - dhcp->offered_t0_lease = dhcp_get_option_long(option_ptr + 2); + dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME); } /* renewal period given? */ - option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T1); - if (option_ptr != NULL) { + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) { /* remember given renewal period */ - dhcp->offered_t1_renew = dhcp_get_option_long(option_ptr + 2); + dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1); } else { /* calculate safe periods for renewal */ dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; } /* renewal period given? */ - option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T2); - if (option_ptr != NULL) { + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) { /* remember given rebind period */ - dhcp->offered_t2_rebind = dhcp_get_option_long(option_ptr + 2); + dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2); } else { /* calculate safe periods for rebinding */ dhcp->offered_t2_rebind = dhcp->offered_t0_lease; @@ -516,57 +532,38 @@ dhcp_handle_ack(struct netif *netif) /* (y)our internet address */ ip_addr_set(&dhcp->offered_ip_addr, &dhcp->msg_in->yiaddr); -/** - * Patch #1308 - * TODO: we must check if the file field is not overloaded by DHCP options! - */ -#if 0 - /* boot server address */ +#if LWIP_DHCP_BOOTP_FILE + /* copy boot server address, + boot file name copied in dhcp_parse_reply if not overloaded */ ip_addr_set(&dhcp->offered_si_addr, &dhcp->msg_in->siaddr); - /* boot file name */ - if (dhcp->msg_in->file[0]) { - dhcp->boot_file_name = mem_malloc(strlen(dhcp->msg_in->file) + 1); - strcpy(dhcp->boot_file_name, dhcp->msg_in->file); - } -#endif +#endif /* LWIP_DHCP_BOOTP_FILE */ - /* subnet mask */ - option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SUBNET_MASK); /* subnet mask given? */ - if (option_ptr != NULL) { - dhcp->offered_sn_mask.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) { + /* remember given subnet mask */ + dhcp->offered_sn_mask.addr = htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)); } /* gateway router */ - option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_ROUTER); - if (option_ptr != NULL) { - dhcp->offered_gw_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) { + dhcp->offered_gw_addr.addr = htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)); } /* broadcast address */ - option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_BROADCAST); - if (option_ptr != NULL) { - dhcp->offered_bc_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_BROADCAST)) { + dhcp->offered_bc_addr.addr = htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_BROADCAST)); } +#if LWIP_DNS /* DNS servers */ - option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_DNS_SERVER); - if (option_ptr != NULL) { - u8_t n; - dhcp->dns_count = dhcp_get_option_byte(&option_ptr[1]) / (u32_t)sizeof(struct ip_addr); - /* limit to at most DHCP_MAX_DNS DNS servers */ - if (dhcp->dns_count > DHCP_MAX_DNS) - dhcp->dns_count = DHCP_MAX_DNS; - for (n = 0; n < dhcp->dns_count; n++) { - dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2 + n * 4])); -#if LWIP_DNS - dns_setserver( n, (struct ip_addr *)(&(dhcp->offered_dns_addr[n].addr))); -#endif /* LWIP_DNS */ - } -#if LWIP_DNS - dns_setserver( n, (struct ip_addr *)(&ip_addr_any)); -#endif /* LWIP_DNS */ + n = 0; + while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) { + struct ip_addr dns_addr; + dns_addr.addr = htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)); + dns_setserver(n, &dns_addr); + n++; } +#endif /* LWIP_DNS */ } /** @@ -594,6 +591,12 @@ dhcp_start(struct netif *netif) it is set when we succeeded starting. */ netif->flags &= ~NETIF_FLAG_DHCP; + /* check hwtype of the netif */ + if ((netif->flags & NETIF_FLAG_ETHARP) == 0) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n")); + return ERR_ARG; + } + /* check MTU of the netif */ if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) { LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n")); @@ -618,8 +621,7 @@ dhcp_start(struct netif *netif) udp_remove(dhcp->pcb); } LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL); - LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL && - dhcp->options_in == NULL && dhcp->options_in_len == 0); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL ); } /* clear data structure */ @@ -1160,8 +1162,10 @@ dhcp_release(struct netif *netif) dhcp->server_ip_addr.addr = 0; dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0; dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0; +#if LWIP_DHCP_BOOTP_FILE + dhcp->offered_si_addr.addr = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; - dhcp->dns_count = 0; /* create and initialize the DHCP message header */ result = dhcp_create_request(netif); @@ -1190,7 +1194,6 @@ dhcp_release(struct netif *netif) netif_set_gw(netif, IP_ADDR_ANY); netif_set_netmask(netif, IP_ADDR_ANY); - /* TODO: netif_down(netif); */ return result; } @@ -1221,8 +1224,7 @@ dhcp_stop(struct netif *netif) udp_remove(dhcp->pcb); dhcp->pcb = NULL; } - LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL && - dhcp->options_in == NULL && dhcp->options_in_len == 0); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); mem_free((void *)dhcp); netif->dhcp = NULL; } @@ -1296,79 +1298,196 @@ dhcp_option_long(struct dhcp *dhcp, u32_t value) * */ static err_t -dhcp_unfold_reply(struct dhcp *dhcp, struct pbuf *p) +dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p) { - u16_t ret; - LWIP_ERROR("dhcp != NULL", (dhcp != NULL), return ERR_ARG;); - /* free any left-overs from previous unfolds */ - dhcp_free_reply(dhcp); - /* options present? */ - if (p->tot_len > (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)) { - dhcp->options_in_len = p->tot_len - (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); - dhcp->options_in = mem_malloc(dhcp->options_in_len); - if (dhcp->options_in == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, - ("dhcp_unfold_reply(): could not allocate dhcp->options\n")); - dhcp->options_in_len = 0; - return ERR_MEM; + u8_t *options; + u16_t offset; + u16_t offset_max; + u16_t options_idx; + u16_t options_idx_max; + struct pbuf *q; + u8_t file_overloaded = 0; + + /* clear received options */ + dhcp_clear_all_options(dhcp); + /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */ + if (p->len < DHCP_SNAME_OFS) { + return ERR_BUF; + } + dhcp->msg_in = p->payload; +#if LWIP_DHCP_BOOTP_FILE + /* clear boot file name */ + dhcp->boot_file_name[0] = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* parse options */ + + /* start with options field */ + options_idx = DHCP_OPTIONS_OFS; + /* parse options to the end of the received packet */ + options_idx_max = p->tot_len; +again: + q = p; + while((q != NULL) && (options_idx >= q->len)) { + options_idx -= q->len; + options_idx_max -= q->len; + q = q->next; + } + if (q == NULL) { + return ERR_BUF; + } + offset = options_idx; + offset_max = options_idx_max; + options = (u8_t*)q->payload; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) { + u8_t op = options[offset]; + u8_t len; + u8_t decode_len = 0; + int decode_idx = -1; + u16_t val_offset = offset + 2; + /* len byte might be in the next pbuf */ + if (offset + 1 < q->len) { + len = options[offset + 1]; + } else { + len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0); + } + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + decode_len = len; + switch(op) { + case(DHCP_OPTION_PAD): + /* special option: no len encoded */ + decode_len = len = 0; + /* will be increased below */ + offset--; + case(DHCP_OPTION_SUBNET_MASK): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ASSERT("len >= decode_len", len >= decode_len); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ASSERT("len % 4 == 0", len % 4 == 0); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ASSERT("len >= decode_len", len >= decode_len); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; + case(DHCP_OPTION_BROADCAST): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_BROADCAST; + break; + case(DHCP_OPTION_LEASE_TIME): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; + case(DHCP_OPTION_OVERLOAD): + LWIP_ASSERT("len == 1", len == 1); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ASSERT("len == 1", len == 1); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_T2; + break; + default: + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op)); + break; + } + offset += len + 2; + if (decode_len > 0) { + u32_t value = 0; + u16_t copy_len; +decode_next: + LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX); + LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx)); + copy_len = LWIP_MIN(decode_len, 4); + pbuf_copy_partial(q, &value, copy_len, val_offset); + if (decode_len > 4) { + /* decode more than one u32_t */ + LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0); + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, htonl(value)); + decode_len -= 4; + decode_idx++; + goto decode_next; + } else if (decode_len == 4) { + value = ntohl(value); + } else { + LWIP_ASSERT("invalid decode_len", decode_len == 1); + } + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, value); + } + if (offset >= q->len) { + offset -= q->len; + offset_max -= q->len; + q = q->next; } } - dhcp->msg_in = (struct dhcp_msg *)mem_malloc(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); - if (dhcp->msg_in == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, - ("dhcp_unfold_reply(): could not allocate dhcp->msg_in\n")); - if (dhcp->options_in != NULL) { - mem_free(dhcp->options_in); - dhcp->options_in = NULL; - dhcp->options_in_len = 0; + /* is this an overloaded message? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) { + u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD); + dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD); + if (overload == DHCP_OVERLOAD_FILE) { + file_overloaded = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); + options_idx = DHCP_FILE_OFS; + options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN; + goto again; + } else if (overload == DHCP_OVERLOAD_SNAME) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); + options_idx = DHCP_SNAME_OFS; + options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN; + goto again; + } else if (overload == DHCP_OVERLOAD_SNAME_FILE) { + file_overloaded = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); + options_idx = DHCP_SNAME_OFS; + options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN + DHCP_FILE_LEN; + goto again; + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload)); } - return ERR_MEM; } - - /** copy the DHCP message without options */ - ret = pbuf_copy_partial(p, dhcp->msg_in, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN, 0); - LWIP_ASSERT("ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN", ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes into dhcp->msg_in[]\n", - sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)); - - if (dhcp->options_in != NULL) { - /** copy the DHCP options */ - ret = pbuf_copy_partial(p, dhcp->options_in, dhcp->options_in_len, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); - LWIP_ASSERT("ret == dhcp->options_in_len", ret == dhcp->options_in_len); - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes to dhcp->options_in[]\n", - dhcp->options_in_len)); +#if LWIP_DHCP_BOOTP_FILE + if (!file_overloaded) { + /* only do this for ACK messages */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) && + (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK)) + /* copy bootp file name, don't care for sname (server hostname) */ + pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS); + /* make sure the string is really NULL-terminated */ + dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0; } - LWIP_UNUSED_ARG(ret); +#endif /* LWIP_DHCP_BOOTP_FILE */ return ERR_OK; } -/** - * Free the incoming DHCP message including contiguous copy of - * its DHCP options. - */ -static void dhcp_free_reply(struct dhcp *dhcp) -{ - if (dhcp->msg_in != NULL) { - mem_free((void *)dhcp->msg_in); - dhcp->msg_in = NULL; - } - if (dhcp->options_in) { - mem_free(dhcp->options_in); - dhcp->options_in = NULL; - dhcp->options_in_len = 0; - } - LWIP_DEBUGF(DHCP_DEBUG, ("dhcp_free_reply(): free'd\n")); -} - /** * If an incoming DHCP message is in response to us, then trigger the state machine */ -static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +static void +dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) { struct netif *netif = (struct netif *)arg; struct dhcp *dhcp = netif->dhcp; struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; - u8_t *options_ptr; u8_t msg_type; u8_t i; LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, @@ -1381,11 +1500,10 @@ static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_ LWIP_UNUSED_ARG(addr); LWIP_UNUSED_ARG(port); - LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL && - dhcp->options_in == NULL && dhcp->options_in_len == 0); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); if (p->len < DHCP_MIN_REPLY_LEN) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message too short\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n")); goto free_pbuf_and_return; } @@ -1409,7 +1527,7 @@ static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_ goto free_pbuf_and_return; } /* option fields could be unfold? */ - if (dhcp_unfold_reply(dhcp, p) != ERR_OK) { + if (dhcp_parse_reply(dhcp, p) != ERR_OK) { LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("problem unfolding DHCP message - too short on memory?\n")); goto free_pbuf_and_return; @@ -1417,14 +1535,13 @@ static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); /* obtain pointer to DHCP message type */ - options_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_MESSAGE_TYPE); - if (options_ptr == NULL) { + if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) { LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); goto free_pbuf_and_return; } /* read DHCP message type */ - msg_type = dhcp_get_option_byte(options_ptr + 2); + msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE); /* message type is DHCP ACK? */ if (msg_type == DHCP_ACK) { LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n")); @@ -1462,7 +1579,7 @@ static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_ dhcp_handle_offer(netif); } free_pbuf_and_return: - dhcp_free_reply(dhcp); + dhcp->msg_in = NULL; pbuf_free(p); } @@ -1470,6 +1587,7 @@ free_pbuf_and_return: * Create a DHCP request, fill in common headers * * @param netif the netif under DHCP control + * @todo: pass struct dhcp as a parameter to prevent changing netif->dhcp from dhcp_inform */ static err_t dhcp_create_request(struct netif *netif) @@ -1505,8 +1623,9 @@ dhcp_create_request(struct netif *netif) (dhcp->p_out->len >= sizeof(struct dhcp_msg))); /* reuse transaction identifier in retransmissions */ - if (dhcp->tries==0) + if (dhcp->tries == 0) { xid++; + } dhcp->xid = xid; LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("transaction id xid(%"X32_F")\n", xid)); @@ -1516,8 +1635,7 @@ dhcp_create_request(struct netif *netif) dhcp->msg_out->op = DHCP_BOOTREQUEST; /* TODO: make link layer independent */ dhcp->msg_out->htype = DHCP_HTYPE_ETH; - /* TODO: make link layer independent */ - dhcp->msg_out->hlen = DHCP_HLEN_ETH; + dhcp->msg_out->hlen = netif->hwaddr_len; dhcp->msg_out->hops = 0; dhcp->msg_out->xid = htonl(dhcp->xid); dhcp->msg_out->secs = 0; @@ -1539,7 +1657,7 @@ dhcp_create_request(struct netif *netif) for (i = 0; i < DHCP_FILE_LEN; i++) { dhcp->msg_out->file[i] = 0; } - dhcp->msg_out->cookie = htonl(0x63825363UL); + dhcp->msg_out->cookie = htonl(DHCP_MAGIC_COOKIE); dhcp->options_out_len = 0; /* fill options field with an incrementing array (for debugging purposes) */ for (i = 0; i < DHCP_OPTIONS_LEN; i++) { @@ -1593,136 +1711,4 @@ dhcp_option_trailer(struct dhcp *dhcp) } } -/** - * Find the offset of a DHCP option inside the DHCP message. - * - * @param dhcp DHCP client - * @param option_type - * - * @return a byte offset into the UDP message where the option was found, or - * zero if the given option was not found. - */ -static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type) -{ - u8_t overload = DHCP_OVERLOAD_NONE; - - /* options available? */ - if ((dhcp->options_in != NULL) && (dhcp->options_in_len > 0)) { - /* start with options field */ - u8_t *options = (u8_t *)dhcp->options_in; - u16_t offset = 0; - /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ - while ((offset < dhcp->options_in_len) && (options[offset] != DHCP_OPTION_END)) { - /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ - /* are the sname and/or file field overloaded with options? */ - if (options[offset] == DHCP_OPTION_OVERLOAD) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded message detected\n")); - /* skip option type and length */ - offset += 2; - overload = options[offset++]; - } - /* requested option found */ - else if (options[offset] == option_type) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset %"U16_F" in options\n", offset)); - return &options[offset]; - /* skip option */ - } else { - LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", options[offset])); - /* skip option type */ - offset++; - /* skip option length, and then length bytes */ - offset += 1 + options[offset]; - } - } - /* is this an overloaded message? */ - if (overload != DHCP_OVERLOAD_NONE) { - u16_t field_len; - if (overload == DHCP_OVERLOAD_FILE) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); - options = (u8_t *)&dhcp->msg_in->file; - field_len = DHCP_FILE_LEN; - } else if (overload == DHCP_OVERLOAD_SNAME) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); - options = (u8_t *)&dhcp->msg_in->sname; - field_len = DHCP_SNAME_LEN; - /* TODO: check if else if () is necessary */ - } else { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); - options = (u8_t *)&dhcp->msg_in->sname; - field_len = DHCP_FILE_LEN + DHCP_SNAME_LEN; - } - offset = 0; - - /* at least 1 byte to read and no end marker */ - while ((offset < field_len) && (options[offset] != DHCP_OPTION_END)) { - if (options[offset] == option_type) { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset=%"U16_F"\n", offset)); - return &options[offset]; - /* skip option */ - } else { - LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("skipping option %"U16_F"\n", options[offset])); - /* skip option type */ - offset++; - offset += 1 + options[offset]; - } - } - } - } - return NULL; -} - -/** - * Return the byte of DHCP option data. - * - * @param client DHCP client. - * @param ptr pointer obtained by dhcp_get_option_ptr(). - * - * @return byte value at the given address. - */ -static u8_t -dhcp_get_option_byte(u8_t *ptr) -{ - LWIP_DEBUGF(DHCP_DEBUG, ("option byte value=%"U16_F"\n", (u16_t)(*ptr))); - return *ptr; -} - -#if 0 /* currently unused */ -/** - * Return the 16-bit value of DHCP option data. - * - * @param client DHCP client. - * @param ptr pointer obtained by dhcp_get_option_ptr(). - * - * @return byte value at the given address. - */ -static u16_t -dhcp_get_option_short(u8_t *ptr) -{ - u16_t value; - value = *ptr++ << 8; - value |= *ptr; - LWIP_DEBUGF(DHCP_DEBUG, ("option short value=%"U16_F"\n", value)); - return value; -} -#endif - -/** - * Return the 32-bit value of DHCP option data. - * - * @param client DHCP client. - * @param ptr pointer obtained by dhcp_get_option_ptr(). - * - * @return byte value at the given address. - */ -static u32_t dhcp_get_option_long(u8_t *ptr) -{ - u32_t value; - value = (u32_t)(*ptr++) << 24; - value |= (u32_t)(*ptr++) << 16; - value |= (u32_t)(*ptr++) << 8; - value |= (u32_t)(*ptr++); - LWIP_DEBUGF(DHCP_DEBUG, ("option long value=%"U32_F"\n", value)); - return value; -} - #endif /* LWIP_DHCP */ diff --git a/src/include/lwip/dhcp.h b/src/include/lwip/dhcp.h index db37e884..678b40d3 100644 --- a/src/include/lwip/dhcp.h +++ b/src/include/lwip/dhcp.h @@ -22,6 +22,10 @@ extern "C" { /** period (in milliseconds) of the application calling dhcp_fine_tmr() */ #define DHCP_FINE_TIMER_MSECS 500 +#define DHCP_CHADDR_LEN 16U +#define DHCP_SNAME_LEN 64U +#define DHCP_FILE_LEN 128U + struct dhcp { /** transaction identifier of last sent request */ @@ -30,10 +34,6 @@ struct dhcp struct udp_pcb *pcb; /** incoming msg */ struct dhcp_msg *msg_in; - /** incoming msg options */ - void *options_in; - /** ingoing msg options length */ - u16_t options_in_len; /** current DHCP state machine state */ u8_t state; /** retries of current request */ @@ -50,9 +50,6 @@ struct dhcp struct ip_addr offered_sn_mask; struct ip_addr offered_gw_addr; struct ip_addr offered_bc_addr; -#define DHCP_MAX_DNS 2 - u32_t dns_count; /* actual number of DNS servers obtained */ - struct ip_addr offered_dns_addr[DHCP_MAX_DNS]; /* DNS server addresses */ u32_t offered_t0_lease; /* lease period (in seconds) */ u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ @@ -60,13 +57,12 @@ struct dhcp #if LWIP_DHCP_AUTOIP_COOP u8_t autoip_coop_state; #endif -/** Patch #1308 - * TODO: See dhcp.c "TODO"s - */ -#if 0 + /* @todo: LWIP_DHCP_BOOTP_FILE configuration option? + integrate with possible TFTP-client for booting? */ +#if LWIP_DHCP_BOOTP_FILE struct ip_addr offered_si_addr; - u8_t *boot_file_name; -#endif + char boot_file_name[DHCP_FILE_LEN]; +#endif /* LWIP_DHCP_BOOTPFILE */ }; /* MUST be compiled with "pack structs" or equivalent! */ @@ -88,11 +84,8 @@ struct dhcp_msg PACK_STRUCT_FIELD(struct ip_addr yiaddr); PACK_STRUCT_FIELD(struct ip_addr siaddr); PACK_STRUCT_FIELD(struct ip_addr giaddr); -#define DHCP_CHADDR_LEN 16U PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]); -#define DHCP_SNAME_LEN 64U PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]); -#define DHCP_FILE_LEN 128U PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]); PACK_STRUCT_FIELD(u32_t cookie); #define DHCP_MIN_OPTIONS_LEN 68U @@ -136,66 +129,66 @@ void dhcp_coarse_tmr(void); void dhcp_fine_tmr(void); /** DHCP message item offsets and length */ -#define DHCP_MSG_OFS (UDP_DATA_OFS) - #define DHCP_OP_OFS (DHCP_MSG_OFS + 0) - #define DHCP_HTYPE_OFS (DHCP_MSG_OFS + 1) - #define DHCP_HLEN_OFS (DHCP_MSG_OFS + 2) - #define DHCP_HOPS_OFS (DHCP_MSG_OFS + 3) - #define DHCP_XID_OFS (DHCP_MSG_OFS + 4) - #define DHCP_SECS_OFS (DHCP_MSG_OFS + 8) - #define DHCP_FLAGS_OFS (DHCP_MSG_OFS + 10) - #define DHCP_CIADDR_OFS (DHCP_MSG_OFS + 12) - #define DHCP_YIADDR_OFS (DHCP_MSG_OFS + 16) - #define DHCP_SIADDR_OFS (DHCP_MSG_OFS + 20) - #define DHCP_GIADDR_OFS (DHCP_MSG_OFS + 24) - #define DHCP_CHADDR_OFS (DHCP_MSG_OFS + 28) - #define DHCP_SNAME_OFS (DHCP_MSG_OFS + 44) - #define DHCP_FILE_OFS (DHCP_MSG_OFS + 108) -#define DHCP_MSG_LEN 236 +#define DHCP_OP_OFS 0 +#define DHCP_HTYPE_OFS 1 +#define DHCP_HLEN_OFS 2 +#define DHCP_HOPS_OFS 3 +#define DHCP_XID_OFS 4 +#define DHCP_SECS_OFS 8 +#define DHCP_FLAGS_OFS 10 +#define DHCP_CIADDR_OFS 12 +#define DHCP_YIADDR_OFS 16 +#define DHCP_SIADDR_OFS 20 +#define DHCP_GIADDR_OFS 24 +#define DHCP_CHADDR_OFS 28 +#define DHCP_SNAME_OFS 44 +#define DHCP_FILE_OFS 108 +#define DHCP_MSG_LEN 236 -#define DHCP_COOKIE_OFS (DHCP_MSG_OFS + DHCP_MSG_LEN) -#define DHCP_OPTIONS_OFS (DHCP_MSG_OFS + DHCP_MSG_LEN + 4) +#define DHCP_COOKIE_OFS DHCP_MSG_LEN +#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4) -#define DHCP_CLIENT_PORT 68 -#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 /** DHCP client states */ -#define DHCP_REQUESTING 1 -#define DHCP_INIT 2 -#define DHCP_REBOOTING 3 -#define DHCP_REBINDING 4 -#define DHCP_RENEWING 5 -#define DHCP_SELECTING 6 -#define DHCP_INFORMING 7 -#define DHCP_CHECKING 8 -#define DHCP_PERMANENT 9 -#define DHCP_BOUND 10 +#define DHCP_REQUESTING 1 +#define DHCP_INIT 2 +#define DHCP_REBOOTING 3 +#define DHCP_REBINDING 4 +#define DHCP_RENEWING 5 +#define DHCP_SELECTING 6 +#define DHCP_INFORMING 7 +#define DHCP_CHECKING 8 +#define DHCP_PERMANENT 9 +#define DHCP_BOUND 10 /** not yet implemented #define DHCP_RELEASING 11 */ -#define DHCP_BACKING_OFF 12 -#define DHCP_OFF 13 +#define DHCP_BACKING_OFF 12 +#define DHCP_OFF 13 /** AUTOIP cooperatation flags */ -#define DHCP_AUTOIP_COOP_STATE_OFF 0 -#define DHCP_AUTOIP_COOP_STATE_ON 1 +#define DHCP_AUTOIP_COOP_STATE_OFF 0 +#define DHCP_AUTOIP_COOP_STATE_ON 1 -#define DHCP_BOOTREQUEST 1 -#define DHCP_BOOTREPLY 2 +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 +/** DHCP message types */ #define DHCP_DISCOVER 1 -#define DHCP_OFFER 2 -#define DHCP_REQUEST 3 -#define DHCP_DECLINE 4 -#define DHCP_ACK 5 -#define DHCP_NAK 6 -#define DHCP_RELEASE 7 -#define DHCP_INFORM 8 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 +/** DHCP hardware type, currently only ethernet is supported */ #define DHCP_HTYPE_ETH 1 -#define DHCP_HLEN_ETH 6 +#define DHCP_MAGIC_COOKIE 0x63825363UL -#define DHCP_BROADCAST_FLAG 15 -#define DHCP_BROADCAST_MASK (1 << DHCP_FLAG_BROADCAST) +/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */ /** BootP options */ #define DHCP_OPTION_PAD 0 @@ -217,7 +210,6 @@ void dhcp_fine_tmr(void); #define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ #define DHCP_OPTION_MESSAGE_TYPE_LEN 1 - #define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ #define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */