diff --git a/src/core/dhcp.c b/src/core/dhcp.c index 69f0f3ad..be3cb40a 100644 --- a/src/core/dhcp.c +++ b/src/core/dhcp.c @@ -37,7 +37,8 @@ * RFC 2131 and RFC 2132. * * KNOWN BUG: - * - This client will fail on servers using file/sname field overloading + * - Parsing of DHCP messages which use file/sname field overloading will + * probably fail. Additional support for this must go into dhcp_unfold(). * TODO: * - Add JavaDoc style documentation (API, internals). * - Make the unfold routine smarter to handle this @@ -62,10 +63,11 @@ * * Then, use * struct dhcp_state *client = dhcp_start(struct netif *netif) - * to start the dhcp client to configure the interface by + * starts a DHCP client instance which configures the interface by * obtaining an IP address lease and maintaining it. * - * Use dhcp_stop(client) to stop the lease. + * Use dhcp_release(client) to end the lease and use dhcp_stop(client) + * to remove the DHCP client. * */ #include "lwip/debug.h" @@ -78,12 +80,15 @@ #include "lwipopts.h" #include "lwip/dhcp.h" -/// transaction identifier, unique over all DHCP client and all DHCP requests +/** find the active DHCP attached to the given network interface */ +struct dhcp_state *dhcp_find_client(struct netif *netif); + +/** transaction identifier, unique over all DHCP requests */ static u32_t xid = 0xABCD0000; -/// singly-linked list of DHCP clients that are active +/** singly-linked list of DHCP clients that are active */ static struct dhcp_state *client_list = NULL; -/// DHCP client state machine functions +/** DHCP client state machine functions */ static void dhcp_handle_ack(struct dhcp_state *state); static void dhcp_handle_nak(struct dhcp_state *state); static void dhcp_handle_offer(struct dhcp_state *state); @@ -96,7 +101,7 @@ static err_t dhcp_rebind(struct dhcp_state *state); static err_t dhcp_release(struct dhcp_state *state); static void dhcp_set_state(struct dhcp_state *state, unsigned char new_state); -// receive, unfold, process and free incoming messages +/** 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_state *state); static u8_t *dhcp_get_option_ptr(struct dhcp_state *state, u8_t option_type); @@ -104,12 +109,18 @@ static u8_t dhcp_get_option_byte(u8_t *ptr); static u16_t dhcp_get_option_short(u8_t *ptr); static u32_t dhcp_get_option_long(u8_t *ptr); static void dhcp_free_reply(struct dhcp_state *state); + +/** set the DHCP timers */ static void dhcp_timeout(struct dhcp_state *state); static void dhcp_t1_timeout(struct dhcp_state *state); static void dhcp_t2_timeout(struct dhcp_state *state); -void dhcp_arp_reply(struct ip_addr *addr); -// build outgoing messages +#if DHCP_DOES_ARP_CHECK +/** called by ARP to notify us of ARP replies */ +void dhcp_arp_reply(struct ip_addr *addr); +#endif + +/** build outgoing messages */ static err_t dhcp_create_request(struct dhcp_state *state); static void dhcp_delete_request(struct dhcp_state *state); static void dhcp_option(struct dhcp_state *state, u8_t option_type, u8_t option_len); @@ -118,9 +129,13 @@ static void dhcp_option_short(struct dhcp_state *state, u16_t value); static void dhcp_option_long(struct dhcp_state *state, u32_t value); static void dhcp_option_trailer(struct dhcp_state *state); -// find a client in the singly-linked list of DHCP clients -struct dhcp_state *dhcp_find_client(struct netif *netif); - +/** + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We back-off and will end up restarting a fresh DHCP negotiation later. + */ static void dhcp_handle_nak(struct dhcp_state *state) { u16_t msecs = 10 * 1000; DEBUGF(DHCP_DEBUG, ("dhcp_handle_nak()")); @@ -129,8 +144,13 @@ static void dhcp_handle_nak(struct dhcp_state *state) { dhcp_set_state(state, DHCP_BACKING_OFF); } -// checks if the offered address is already in use -// it does so by sending an ARP query +/** + * Checks if the offered address from the server is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + */ static void dhcp_check(struct dhcp_state *state) { struct pbuf *p; @@ -143,8 +163,6 @@ static void dhcp_check(struct dhcp_state *state) DEBUGF(DHCP_DEBUG, ("dhcp_check(): sending ARP request len %u", p->tot_len)); result = state->netif->linkoutput(state->netif, p); pbuf_free(p); - - //return /*result*/; } state->tries++; msecs = state->tries * 1000; @@ -153,6 +171,10 @@ static void dhcp_check(struct dhcp_state *state) dhcp_set_state(state, DHCP_CHECKING); } +/** + * Remember the configuration offered by a DHCP server. + * + */ static void dhcp_handle_offer(struct dhcp_state *state) { u8_t *option_ptr = dhcp_get_option_ptr(state, DHCP_OPTION_SERVER_ID); @@ -174,7 +196,7 @@ static void dhcp_handle_offer(struct dhcp_state *state) /** * Select a DHCP server offer out of all offers. * - * Simply select the first offer received, ignore others + * Simply select the first offer received. */ static err_t dhcp_select(struct dhcp_state *state) { @@ -223,6 +245,10 @@ static err_t dhcp_select(struct dhcp_state *state) return result; } +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + * + */ void dhcp_coarse_tmr() { struct dhcp_state *list_state = client_list; @@ -249,6 +275,10 @@ void dhcp_coarse_tmr() } } +/** + * The DHCP timer that handles DHCP negotiotion transaction timeouts. + * + */ void dhcp_fine_tmr() { struct dhcp_state *list_state = client_list; @@ -268,7 +298,11 @@ void dhcp_fine_tmr() } } -// something has timed out, handle this +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * + */ static void dhcp_timeout(struct dhcp_state *state) { DEBUGF(DHCP_DEBUG, ("dhcp_timeout()")); @@ -319,13 +353,17 @@ static void dhcp_timeout(struct dhcp_state *state) } else { - DEBUGF(DHCP_DEBUG, ("dhcp_timeout(): REBINDING, release, restart")); + DEBUGF(DHCP_DEBUG, ("dhcp_timeout(): RELEASING, DISCOVERING")); dhcp_release(state); dhcp_discover(state); } } } +/** + * The renewal period has timed out. + * + */ static void dhcp_t1_timeout(struct dhcp_state *state) { DEBUGF(DHCP_DEBUG, ("dhcp_t1_timeout()")); @@ -335,6 +373,11 @@ static void dhcp_t1_timeout(struct dhcp_state *state) dhcp_renew(state); } } + +/** + * The rebind period has timed out. + * + */ static void dhcp_t2_timeout(struct dhcp_state *state) { DEBUGF(DHCP_DEBUG, ("dhcp_t2_timeout()")); @@ -352,6 +395,7 @@ static void dhcp_t2_timeout(struct dhcp_state *state) static void dhcp_handle_ack(struct dhcp_state *state) { u8_t *option_ptr; + /* clear options we might not get from the ACK */ state->offered_sn_mask.addr = 0; state->offered_gw_addr.addr = 0; state->offered_bc_addr.addr = 0; @@ -363,31 +407,36 @@ static void dhcp_handle_ack(struct dhcp_state *state) state->offered_t1_renew = state->offered_t0_lease / 2; state->offered_t2_rebind = state->offered_t0_lease; } - + /* renewal period */ option_ptr = dhcp_get_option_ptr(state, DHCP_OPTION_T1); if (option_ptr != NULL) { state->offered_t1_renew = dhcp_get_option_long(option_ptr + 2); } + /* rebind period */ option_ptr = dhcp_get_option_ptr(state, DHCP_OPTION_T2); if (option_ptr != NULL) { state->offered_t2_rebind = dhcp_get_option_long(option_ptr + 2); } + /* (y)our internet address */ ip_addr_set(&state->offered_ip_addr, &state->msg_in->yiaddr); + /* subnet mask */ option_ptr = dhcp_get_option_ptr(state, DHCP_OPTION_SUBNET_MASK); if (option_ptr != NULL) { state->offered_sn_mask.addr = htonl(dhcp_get_option_long(&option_ptr[2])); } + /* gateway router */ option_ptr = dhcp_get_option_ptr(state, DHCP_OPTION_ROUTER); if (option_ptr != NULL) { state->offered_gw_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); } + /* broadcast address */ option_ptr = dhcp_get_option_ptr(state, DHCP_OPTION_BROADCAST); if (option_ptr != NULL) { @@ -415,7 +464,14 @@ void dhcp_init(void) * Start DHCP negotiation for a network interface. * * If no DHCP client instance was attached to this interface, - * a new client is created first. + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @return The DHCP client state, which must be passed for + * all subsequential dhcp_*() calls. NULL means there is + * no (longer a) DHCP client attached to the interface + * (due to unavailable memory or network resources). + * */ struct dhcp_state *dhcp_start(struct netif *netif) { @@ -425,14 +481,14 @@ struct dhcp_state *dhcp_start(struct netif *netif) DEBUGF(DHCP_DEBUG, ("dhcp_start()")); - // find the DHCP client attached to the given interface + /* find the DHCP client attached to the given interface */ state = dhcp_find_client(netif); DEBUGF(DHCP_DEBUG, ("dhcp_start(): finished parsing through list")); - // a DHCP client already attached to this interface + /* a DHCP client already attached to this interface? */ if (state != NULL) { DEBUGF(DHCP_DEBUG, ("dhcp_start(): already active on interface")); - // just restart the DHCP negotiation + /* just restart the DHCP negotiation */ result = dhcp_discover(state); if (result == ERR_OK) { @@ -464,26 +520,33 @@ struct dhcp_state *dhcp_start(struct netif *netif) } DEBUGF(DHCP_DEBUG, ("dhcp_start(): created new udp pcb")); state->netif = netif; - // enqueue in list of clients - // we are last in list + /* enqueue in list of clients */ + /* we are last in list */ state->next = NULL; - // empty list? + /* empty list? */ if (client_list == NULL) { - // single item at head of list + /* single item at head of list */ client_list = state; } else { - // proceed to the last DHCP client state + /* proceed to the last DHCP client state while (list_state->next != NULL) list_state = list_state->next; - // { list_state->next == NULL } list_state->next = state; } dhcp_discover(state); return state; } +/** + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by send an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice. + * + */ void dhcp_inform(struct netif *netif) { struct dhcp_state *state = NULL; @@ -568,7 +631,11 @@ void dhcp_arp_reply(struct ip_addr *addr) } } -// decline a +/** + * Decline a + * + * + */ static err_t dhcp_decline(struct dhcp_state *state) { err_t result = ERR_OK; @@ -604,7 +671,7 @@ static err_t dhcp_decline(struct dhcp_state *state) /** - * Start the DHCP process, discover a server + * Start the DHCP process, discover a DHCP server. * */ static err_t dhcp_discover(struct dhcp_state *state) @@ -658,14 +725,15 @@ static void dhcp_bind(struct dhcp_state *state) { struct ip_addr sn_mask, gw_addr; dhcp_set_state(state, DHCP_BOUND); - - if (state->offered_t1_renew != 0xffffffffUL) - { + /* temporary DHCP lease? */ + if (state->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ DEBUGF(DHCP_DEBUG, ("dhcp_bind(): t1 renewal timer %lu secs", state->offered_t1_renew)); state->t1_timeout = (state->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; if (state->t1_timeout == 0) state->t1_timeout = 1; DEBUGF(DHCP_DEBUG, ("dhcp_bind(): request timeout %u msecs", state->offered_t1_renew*1000)); } + /* set renewal period timer */ if (state->offered_t2_rebind != 0xffffffffUL) { DEBUGF(DHCP_DEBUG, ("dhcp_bind(): t2 rebind timer %lu secs", state->offered_t2_rebind)); @@ -701,8 +769,10 @@ static void dhcp_bind(struct dhcp_state *state) netif_set_ipaddr(state->netif, &state->offered_ip_addr); } - - +/** + * Renew an existing DHCP lease at the involved DHCP server. + * + */ err_t dhcp_renew(struct dhcp_state *state) { err_t result; @@ -748,6 +818,10 @@ err_t dhcp_renew(struct dhcp_state *state) return result; } +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + */ static err_t dhcp_rebind(struct dhcp_state *state) { err_t result; @@ -791,6 +865,10 @@ static err_t dhcp_rebind(struct dhcp_state *state) return result; } +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + */ static err_t dhcp_release(struct dhcp_state *state) { err_t result; @@ -825,7 +903,11 @@ static err_t dhcp_release(struct dhcp_state *state) netif_set_netmask(state->netif, IP_ADDR_ANY); return result; } - +/** + * Remove the DHCP client from the interface. + * + * @param state The DHCP client state + */ void dhcp_stop(struct dhcp_state *state) { struct dhcp_state *list_state = client_list; @@ -1158,8 +1240,15 @@ static void dhcp_option_trailer(struct dhcp_state *state) } } -// return a byte offset into the udp message where the option was found, or -// zero if the given option was not found. +/** + * Find the offset of a DHCP option inside the DHCP message. + * + * @param client 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_state *state, u8_t option_type) { u8_t overload = DHCP_OVERLOAD_NONE; @@ -1244,12 +1333,28 @@ static u8_t *dhcp_get_option_ptr(struct dhcp_state *state, u8_t option_type) return 0; } +/** + * 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) { DEBUGF(DHCP_DEBUG, ("option byte value=%u", *ptr)); return *ptr; } +/** + * 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; @@ -1259,6 +1364,14 @@ static u16_t dhcp_get_option_short(u8_t *ptr) return value; } +/** + * 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;