diff --git a/include/net/uip/dhcpc.h b/include/net/uip/dhcpc.h index 606d818b0cbc7075067fb2d3fe273646b450d717..a5d86da9db42e0c8542aaa6e1119f3e40c4df009 100644 --- a/include/net/uip/dhcpc.h +++ b/include/net/uip/dhcpc.h @@ -55,7 +55,7 @@ struct dhcpc_state { uint16 lease_time[2]; - uint8 serverid[4]; + struct in_addr serverid; struct in_addr ipaddr; struct in_addr netmask; struct in_addr dnsaddr; diff --git a/netutils/dhcpc/dhcpc.c b/netutils/dhcpc/dhcpc.c index e0ee702c22a174303e282841f715cde9dc679454..539ee60cca9f83eb1da7e72c843b5ea8f3aeba93 100644 --- a/netutils/dhcpc/dhcpc.c +++ b/netutils/dhcpc/dhcpc.c @@ -61,9 +61,8 @@ ****************************************************************************/ #define STATE_INITIAL 0 -#define STATE_SENDING 1 -#define STATE_OFFER_RECEIVED 2 -#define STATE_CONFIG_RECEIVED 3 +#define STATE_HAVE_OFFER 1 +#define STATE_HAVE_LEASE 2 #define BOOTP_BROADCAST 0x8000 @@ -127,6 +126,7 @@ struct dhcpc_state_s const void *ds_macaddr; int ds_maclen; int sockfd; + struct in_addr serverid; struct dhcp_msg packet; }; @@ -141,7 +141,11 @@ static const uint8 magic_cookie[4] = {99, 130, 83, 99}; * Private Functions ****************************************************************************/ -static uint8 *add_msg_type(uint8 *optptr, uint8 type) +/**************************************************************************** + * Name: dhcpc_add<option> + ****************************************************************************/ + +static uint8 *dhcpc_addmsgtype(uint8 *optptr, uint8 type) { *optptr++ = DHCP_OPTION_MSG_TYPE; *optptr++ = 1; @@ -149,15 +153,15 @@ static uint8 *add_msg_type(uint8 *optptr, uint8 type) return optptr; } -static uint8 *add_server_id(struct dhcpc_state *presult, uint8 *optptr) +static uint8 *dhcpc_addserverid(struct in_addr *serverid, uint8 *optptr) { *optptr++ = DHCP_OPTION_SERVER_ID; *optptr++ = 4; - memcpy(optptr, presult->serverid, 4); + memcpy(optptr, &serverid->s_addr, 4); return optptr + 4; } -static uint8 *add_req_ipaddr(struct dhcpc_state *presult, uint8 *optptr) +static uint8 *dhcpc_addreqipaddr(struct dhcpc_state *presult, uint8 *optptr) { *optptr++ = DHCP_OPTION_REQ_IPADDR; *optptr++ = 4; @@ -165,7 +169,7 @@ static uint8 *add_req_ipaddr(struct dhcpc_state *presult, uint8 *optptr) return optptr + 4; } -static uint8 *add_req_options(uint8 *optptr) +static uint8 *dhcpc_addreqoptions(uint8 *optptr) { *optptr++ = DHCP_OPTION_REQ_LIST; *optptr++ = 3; @@ -175,83 +179,90 @@ static uint8 *add_req_options(uint8 *optptr) return optptr; } -static uint8 *add_end(uint8 *optptr) +static uint8 *dhcpc_addend(uint8 *optptr) { *optptr++ = DHCP_OPTION_END; return optptr; } -static void create_msg(struct dhcpc_state_s *pdhcpc) +/**************************************************************************** + * Name: dhcpc_sendmsg + ****************************************************************************/ + +static int dhcpc_sendmsg(struct dhcpc_state_s *pdhcpc, + struct dhcpc_state *presult, int msgtype) { + struct sockaddr_in addr; + uint8 *pend; + in_addr_t serverid = INADDR_BROADCAST; + int len; + + /* Create the common message header settings */ + memset(&pdhcpc->packet, 0, sizeof(struct dhcp_msg)); pdhcpc->packet.op = DHCP_REQUEST; pdhcpc->packet.htype = DHCP_HTYPE_ETHERNET; pdhcpc->packet.hlen = pdhcpc->ds_maclen; memcpy(pdhcpc->packet.xid, xid, 4); - pdhcpc->packet.flags = HTONS(BOOTP_BROADCAST); /* Broadcast bit. */ memcpy(pdhcpc->packet.chaddr, pdhcpc->ds_macaddr, pdhcpc->ds_maclen); memset(&pdhcpc->packet.chaddr[pdhcpc->ds_maclen], 0, 16 - pdhcpc->ds_maclen); memcpy(pdhcpc->packet.options, magic_cookie, sizeof(magic_cookie)); -} -static int send_discover(struct dhcpc_state_s *pdhcpc) -{ - struct sockaddr_in addr; - uint8 *pend; - int len; - - /* Create the basic message header */ + /* Add the common header options */ - create_msg(pdhcpc); + pend = &pdhcpc->packet.options[4]; + pend = dhcpc_addmsgtype(pend, msgtype); - /* Add the options */ + /* Handle the message specific settings */ - pend = &pdhcpc->packet.options[4]; - pend = add_msg_type(pend, DHCPDISCOVER); - pend = add_req_options(pend); - pend = add_end(pend); - len = pend - (uint8*)&pdhcpc->packet; + switch (msgtype) + { + /* Broadcast DISCOVER message to all servers */ - /* Send the request */ + case DHCPDISCOVER: + pdhcpc->packet.flags = HTONS(BOOTP_BROADCAST); /* Broadcast bit. */ + pend = dhcpc_addreqoptions(pend); + break; - addr.sin_family = AF_INET; - addr.sin_port = HTONS(DHCPC_SERVER_PORT); - addr.sin_addr.s_addr = INADDR_BROADCAST; + /* Send REQUEST message to the server that send the OFFER */ - return sendto(pdhcpc->sockfd, &pdhcpc->packet, len, 0, - (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); -} -static int send_request(struct dhcpc_state_s *pdhcpc, struct dhcpc_state *presult) -{ - struct sockaddr_in addr; - uint8 *pend; - int len; + case DHCPREQUEST: + pdhcpc->packet.flags = HTONS(BOOTP_BROADCAST); /* Broadcast bit. */ + memcpy(pdhcpc->packet.ciaddr, &presult->ipaddr.s_addr, 4); + pend = dhcpc_addserverid(&pdhcpc->serverid, pend); + pend = dhcpc_addreqipaddr(presult, pend); + break; - /* Create the basic message header */ + /* Send DECLINE message to the server that sent the OFFER */ - create_msg(pdhcpc); - memcpy(pdhcpc->packet.ciaddr, &presult->ipaddr.s_addr, 4); + case DHCPDECLINE: + memcpy(pdhcpc->packet.ciaddr, &presult->ipaddr.s_addr, 4); + pend = dhcpc_addserverid(&presult->serverid, pend); + serverid = presult->serverid.s_addr; + break; - /* Add the options */ + default: + return ERROR; + } - pend = &pdhcpc->packet.options[4]; - pend = add_msg_type(pend, DHCPREQUEST); - pend = add_server_id(presult, pend); - pend = add_req_ipaddr(presult, pend); - pend = add_end(pend); + pend = dhcpc_addend(pend); len = pend - (uint8*)&pdhcpc->packet; /* Send the request */ addr.sin_family = AF_INET; addr.sin_port = HTONS(DHCPC_SERVER_PORT); - addr.sin_addr.s_addr = INADDR_BROADCAST; + addr.sin_addr.s_addr = serverid; return sendto(pdhcpc->sockfd, &pdhcpc->packet, len, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); } -static uint8 parse_options(struct dhcpc_state *presult, uint8 *optptr, int len) +/**************************************************************************** + * Name: dhcpc_parseoptions + ****************************************************************************/ + +static uint8 dhcpc_parseoptions(struct dhcpc_state *presult, uint8 *optptr, int len) { uint8 *end = optptr + len; uint8 type = 0; @@ -273,7 +284,7 @@ static uint8 parse_options(struct dhcpc_state *presult, uint8 *optptr, int len) type = *(optptr + 2); break; case DHCP_OPTION_SERVER_ID: - memcpy(presult->serverid, optptr + 2, 4); + memcpy(&presult->serverid.s_addr, optptr + 2, 4); break; case DHCP_OPTION_LEASE_TIME: memcpy(presult->lease_time, optptr + 2, 4); @@ -287,15 +298,19 @@ static uint8 parse_options(struct dhcpc_state *presult, uint8 *optptr, int len) return type; } -static uint8 parse_msg(struct dhcpc_state_s *pdhcpc, int buflen, - struct dhcpc_state *presult) +/**************************************************************************** + * Name: dhcpc_parsemsg + ****************************************************************************/ + +static uint8 dhcpc_parsemsg(struct dhcpc_state_s *pdhcpc, int buflen, + struct dhcpc_state *presult) { if (pdhcpc->packet.op == DHCP_REPLY && memcmp(pdhcpc->packet.xid, xid, sizeof(xid)) == 0 && memcmp(pdhcpc->packet.chaddr, pdhcpc->ds_macaddr, pdhcpc->ds_maclen) == 0) { memcpy(&presult->ipaddr.s_addr, pdhcpc->packet.yiaddr, 4); - return parse_options(presult, &pdhcpc->packet.options[4], buflen); + return dhcpc_parseoptions(presult, &pdhcpc->packet.options[4], buflen); } return 0; } @@ -304,12 +319,20 @@ static uint8 parse_msg(struct dhcpc_state_s *pdhcpc, int buflen, * Global Functions ****************************************************************************/ +/**************************************************************************** + * Name: dhcpc_open + ****************************************************************************/ + void *dhcpc_open(const void *macaddr, int maclen) { struct dhcpc_state_s *pdhcpc; struct sockaddr_in addr; struct timeval tv; + dbg("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + ((uint8*)macaddr)[0], ((uint8*)macaddr)[1], ((uint8*)macaddr)[2], + ((uint8*)macaddr)[3], ((uint8*)macaddr)[4], ((uint8*)macaddr)[5]); + /* Allocate an internal DHCP structure */ pdhcpc = (struct dhcpc_state_s *)malloc(sizeof(struct dhcpc_state_s)); @@ -358,6 +381,10 @@ void *dhcpc_open(const void *macaddr, int maclen) return (void*)pdhcpc; } +/**************************************************************************** + * Name: dhcpc_close + ****************************************************************************/ + void dhcpc_close(void *handle) { struct dchcpc_state_internal *pdhcpc = (struct dchcpc_state_internal *)handle; @@ -367,72 +394,158 @@ void dhcpc_close(void *handle) } } +/**************************************************************************** + * Name: dhcpc_request + ****************************************************************************/ + int dhcpc_request(void *handle, struct dhcpc_state *presult) { struct dhcpc_state_s *pdhcpc = (struct dhcpc_state_s *)handle; struct in_addr oldaddr; + struct in_addr newaddr; ssize_t result; - int state; + uint8 msgtype; + int retries; + int state; - /* Save the currently assigned IP address (should be zero) */ + /* Save the currently assigned IP address (should be INADDR_ANY) */ oldaddr.s_addr = 0; uip_gethostaddr("eth0", &oldaddr); - /* Loop until we receive the offer */ + /* Loop until we receive the lease (or an error occurs) */ do { - state = STATE_SENDING; + /* Set the IP address to INADDR_ANY. */ + + newaddr.s_addr = INADDR_ANY; + (void)uip_sethostaddr("eth0", &newaddr); + + /* Loop sending DISCOVER until we receive an OFFER from a DHCP + * server. We will lock on to the first OFFER and decline any + * subsequent offers (which will happen if there are more than one + * DHCP servers on the network. + */ + state = STATE_INITIAL; do { - /* Send the command */ + /* Send the DISCOVER command */ - if (send_discover(pdhcpc) < 0) + dbg("Broadcast DISCOVER\n"); + if (dhcpc_sendmsg(pdhcpc, presult, DHCPDISCOVER) < 0) { return ERROR; } - /* Get the response */ + /* Get the DHCPOFFER response */ result = recv(pdhcpc->sockfd, &pdhcpc->packet, sizeof(struct dhcp_msg), 0); if (result >= 0) { - if (parse_msg(pdhcpc, result, presult) == DHCPOFFER) + msgtype = dhcpc_parsemsg(pdhcpc, result, presult); + if (msgtype == DHCPOFFER) { + /* Save the servid from the presult so that it is not clobbered + * by a new OFFER. + */ + + dbg("Received OFFER from %08x\n", ntohl(presult->serverid.s_addr)); + pdhcpc->serverid.s_addr = presult->serverid.s_addr; + + /* Temporarily use the address offered by the server and break + * out of the loop. + */ + (void)uip_sethostaddr("eth0", &presult->ipaddr); - state = STATE_OFFER_RECEIVED; + state = STATE_HAVE_OFFER; } } + + /* An error has occurred. If this was a timeout error (meaning that + * nothing was received on this socket for a long period of time). + * Then loop and send the DISCOVER command again. + */ + else if (*get_errno_ptr() != EAGAIN) { - /* An error other than a timeout was received */ + /* An error other than a timeout was received -- error out */ return ERROR; } } - while (state != STATE_OFFER_RECEIVED); + while (state == STATE_INITIAL); + + /* Loop sending the REQUEST up to three times (if there is no response) */ + + retries = 0; do { - /* Send the request */ + /* Send the REQUEST message to obtain the lease that was offered to us. */ - if (send_request(pdhcpc, presult) < 0) + dbg("Send REQUEST\n"); + if (dhcpc_sendmsg(pdhcpc, presult, DHCPREQUEST) < 0) { return ERROR; } + retries++; - /* Get the response */ + /* Get the ACK/NAK response to the REQUEST (or timeout) */ result = recv(pdhcpc->sockfd, &pdhcpc->packet, sizeof(struct dhcp_msg), 0); if (result >= 0) { - if (parse_msg(pdhcpc, result, presult) == DHCPACK) + /* Parse the response */ + + msgtype = dhcpc_parsemsg(pdhcpc, result, presult); + + /* The ACK response means that the server has accepted our request + * and we have the lease. + */ + + if (msgtype == DHCPACK) { - state = STATE_CONFIG_RECEIVED; + dbg("Received ACK\n"); + state = STATE_HAVE_LEASE; + } + + /* NAK means the the server has refused our request. Break out of + * this loop with state == STATE_HAVE_OFFER and send DISCOVER again + */ + + else if (msgtype == DHCPNAK) + { + dbg("Received NAK\n"); + break; + } + + /* If we get any OFFERs from other servers, then decline them now + * and continue waiting for the ACK from the server that we + * requested from. + */ + + else if (msgtype == DHCPOFFER) + { + dbg("Received another OFFER, send DECLINE\n"); + (void)dhcpc_sendmsg(pdhcpc, presult, DHCPDECLINE); + } + + /* Otherwise, it is something that we do not recognize */ + + else + { + dbg("Ignoring msgtype=%d\n", msgtype); } } + + /* An error has occurred. If this was a timeout error (meaning + * that nothing was received on this socket for a long period of time). + * Then break out and send the DISCOVER command again (at most + * 3 times). + */ + else if (*get_errno_ptr() != EAGAIN) { /* An error other than a timeout was received */ @@ -441,9 +554,9 @@ int dhcpc_request(void *handle, struct dhcpc_state *presult) return ERROR; } } - while (state != STATE_CONFIG_RECEIVED); + while (state == STATE_HAVE_OFFER && retries < 3); } - while(state != STATE_CONFIG_RECEIVED); + while (state != STATE_HAVE_LEASE); dbg("Got IP address %d.%d.%d.%d\n", (presult->ipaddr.s_addr >> 24 ) & 0xff, diff --git a/netutils/dhcpd/dhcpd.c b/netutils/dhcpd/dhcpd.c index f160dcc21644fc417412f49975a328f31779f0b6..118b991f119a45d344692083bd8a38686773ba54 100644 --- a/netutils/dhcpd/dhcpd.c +++ b/netutils/dhcpd/dhcpd.c @@ -1283,7 +1283,7 @@ int dhcpd_run(void) #ifdef CONFIG_NETUTILS_DHCPD_HOST /* Get the poor little uC a change to get its recvfrom in place */ - sleep(2); + usleep(500*1000); #endif /* Now process the incoming DHCP message by its message type */ @@ -1298,7 +1298,7 @@ int dhcpd_run(void) case DHCPREQUEST: vdbg("DHCPREQUEST\n"); dhcpd_request(); - break; + break; case DHCPDECLINE: vdbg("DHCPDECLINE\n");