/*=========================================================================*/ int DHCPGetOptionInfo(unsigned char *dhcpOptCodes, int dhcpOptCodeCnt, DHCPInfoCallBack *dhcpInfoCB, void *context) /* Calls dhcpInfoCB once for each requested option in dhcpOptCodes. Returns - zero on success, non-zero on failure errno ENOTCONN error during read ETIME read timed out ENOMEM out of memory EINVAL parse error BOOTP/DHCP packet header format: Offs Len Name Description 0 1 opcode Message opcode: 1 = BOOTREQUEST, 2 = BOOTREPLY 1 1 htype Hardware address type (eg., 1 = 10mb ethernet) 2 1 hlen Hardware address length (eg., 6 = 10mb ethernet) 3 1 hops Client sets to zero, optionally used by relay agents 4 4 xid Transaction ID, random number chosen by client 8 2 secs Client sets to seconds since start of boot process 10 2 flags Bit 0: broadcast response bit 12 4 ciaddr Client IP address - only filled if client is bound 16 4 yiaddr 'your' (Client) IP address 20 4 siaddr IP address of next server to use in bootstrap 24 4 giaddr Relay agent IP address, used in booting via RA 28 16 chaddr Client hardware address 44 64 sname Optional server host name, null-terminated string 108 128 file Boot file name, null-terminated string 236 var options Optional parameters field The options field has the following format: Offs Len Name Description 0 4 cookie 4-byte cookie field: 99.130.83.99 (0x63825363) Followed by 1-byte option codes and 1-byte option lengths, except for the two special fixed length options, pad (0) and end (255). Options are defined in slp_dhcp.h as TAG_XXX values. The two we really care about here are options TAG_SLP_DA and TAG_SLP_SCOPE, 78 and 79, respectively. The format for TAG_SLP_DA (starting with the tag) is: Offs Len Name Description 0 1 tag TAG_SLP_DA - directory agent ip addresses 1 1 length length of remaining data in the option 2 1 mand flag: the use of these DA's is mandatory 3 4 a(0) 4-byte ip address ... 3+n*4 4 a(n) 4-byte ip address The format for TAG_SLP_SCOPE (starting with the tag) is: Offs Len Name Description 0 1 tag TAG_SLP_SCOPE - directory scopes to use 1 1 length length of remaining data in the option 2 1 mand flag: the use of these scopes is mandatory 3 var scopes a null-terminated, comma-separated string of scopes The "DHCP Message Type" option must be included in every DHCP message. All tags except for TAG_PAD(0) and TAG_END(255) begin with a tag value followed by a length of remaining data value. =========================================================================*/ { UINT32 xid; time_t timer; struct timeval tv; int sockfd, retries; struct sockaddr_in sendaddr; unsigned char chaddr[MAX_MACADDR_SIZE]; unsigned char hlen, htype; unsigned char sndbuf[512]; unsigned char rcvbuf[512]; struct hostent *hep; unsigned char *p; size_t rcvbufsz = 0; char host[256]; /* Get our IP and MAC addresses */ if(gethostname(host, (int)sizeof(host)) || !(hep = gethostbyname(host)) || dhcpGetAddressInfo((unsigned char *)hep->h_addr, chaddr, &hlen, &htype)) return -1; /* get a reasonably random transaction id value */ xid = (UINT32)time(&timer); /* BOOTP request header */ memset(sndbuf, 0, 236); /* clear bootp header */ p = sndbuf; *p++ = BOOTREQUEST; /* opcode */ *p++ = htype; *p++ = hlen; p++; /* hops */ ToUINT32(p, xid); p += 2 * sizeof(UINT32); /* xid, secs, flags */ memcpy(p, hep->h_addr, 4); p += 4 * sizeof(UINT32); /* ciaddr, yiaddr, siaddr, giaddr */ memcpy(p, chaddr, hlen); p += 16 + 64 + 128; /* chaddr, sname and file */ *p++ = DHCP_COOKIE1; /* options, cookies 1-4 */ *p++ = DHCP_COOKIE2; *p++ = DHCP_COOKIE3; *p++ = DHCP_COOKIE4; /* DHCP Message Type option */ *p++ = TAG_DHCP_MSG_TYPE; *p++ = 1; /* option length */ *p++ = DHCP_MSG_INFORM; /* message type is DHCPINFORM */ /* DHCP Parameter Request option */ *p++ = TAG_DHCP_PARAM_REQ; /* request for DHCP parms */ *p++ = (unsigned char)dhcpOptCodeCnt; memcpy(p, dhcpOptCodes, dhcpOptCodeCnt); p += dhcpOptCodeCnt; /* DHCP Client Identifier option */ *p++ = TAG_CLIENT_IDENTIFIER; *p++ = hlen + 1; /* option length */ *p++ = htype; /* client id is htype/haddr */ memcpy(p, chaddr, hlen); p += hlen; /* End option */ *p++ = TAG_END; /* get a broadcast send/recv socket and address */ if((sockfd = dhcpCreateBCSkt(&sendaddr)) < 0) return -1; /* setup select timeout */ tv.tv_sec = 0; tv.tv_usec = INIT_TMOUT_USECS; retries = 0; srand((unsigned)time(&timer)); while (retries++ < MAX_DHCP_RETRIES) { if(dhcpSendRequest(sockfd, sndbuf, p - sndbuf, (struct sockaddr *)&sendaddr, &tv) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if((rcvbufsz = dhcpRecvResponse(sockfd, rcvbuf, sizeof(rcvbuf), &tv)) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if(rcvbufsz >= 236 && AsUINT32(&rcvbuf[4]) == xid) break; /* exponential backoff randomized by a uniform number between -1 and 1 */ tv.tv_usec = tv.tv_usec * 2 + (rand() % 3) - 1; tv.tv_sec = tv.tv_usec / USECS_PER_SEC; tv.tv_usec %= USECS_PER_SEC; } closesocket(sockfd); return rcvbufsz? dhcpProcessOptions(rcvbuf + 236, rcvbufsz - 236, dhcpInfoCB, context): -1; }
/** * @ingroup dhcpc * * DHCP (Dynamic Host Configuration Protocol) client for IPv4 autoconfiguration. * * TODO: This function returns immediately once the IP address has been * assigned. It does not handle renewing leases. * * @param[in] descrp * Device descriptor for the network device on which to open the DHCP * client on. This must be a network device that is open but has no * network interface brought up on it. * @param[in] timeout * Seconds to wait before timing out (must be a positive integer). * @param[out] data * On success, this structure is filled in with the information about the * IPv4 configuration. At least the @ref dhcpData::ip "ip" and @ref * dhcpData::mask "mask" members will be valid. The @ref dhcpData::gateway * "gateway", @ref dhcpData::bootfile "bootfile", and @ref * dhcpData::next_server "next_server" members are optional and will be * left as all 0's if not provided. * * @return * ::OK if successful; ::SYSERR if sending a DHCPDISCOVER packet failed or * if a parameter was invalid; ::TIMEOUT if timed out. */ syscall dhcpClient(int descrp, uint timeout, struct dhcpData *data) { int retval; uint recvTimeout = 2000; /* Milliseconds to wait before timing out a given receive (not the whole client) */ uint delay = 1000; /* Milliseconds to wait after non-timeout error */ ulong starttime = clktime; /* Check for invalid parameters. */ #if NETHER if (descrp < ETH0 || descrp >= ETH0 + NETHER) #endif { DHCP_TRACE("Bad device descriptor.\n"); return SYSERR; } if (0 == timeout) { DHCP_TRACE("Timeout cannot be zero.\n"); return SYSERR; } if (NULL == data) { DHCP_TRACE("No data buffer provided.\n"); return SYSERR; } if (NULL != netLookup(descrp)) { DHCP_TRACE("Network interface is up on device.\n"); return SYSERR; } bzero(data, sizeof(*data)); data->state = DHCPC_STATE_INIT; while (clktime <= starttime + timeout) { switch (data->state) { case DHCPC_STATE_INIT: /* Initialize transfer data. */ data->cxid = rand(); /* Choose random context ID */ data->starttime = clktime; /* Set starting time */ data->clientIpv4Addr = 0; /* Client IP address is unknown */ data->serverIpv4Addr = 0; /* Server IP address is unknown */ /* Client hardware address is known; get it from network device. */ if (SYSERR == control(descrp, NET_GET_HWADDR, (long)data->clientHwAddr, 0)) { DHCP_TRACE("Failed to get client hardware address"); return SYSERR; } /* Server hardware address is unknown */ bzero(data->serverHwAddr, ETH_ADDR_LEN); /* Broadcast DHCPDISCOVER. */ DHCP_TRACE("Sending DHCPDISCOVER"); retval = dhcpSendRequest(descrp, data); if (OK == retval) { DHCP_TRACE("Sent DHCPDISCOVER"); data->state = DHCPC_STATE_SELECTING; } else { DHCP_TRACE("Failed to send DHCPDISCOVER; returning failure"); return SYSERR; } break; case DHCPC_STATE_SELECTING: /* Wait for DHCPOFFER from any server. */ DHCP_TRACE("Waiting for DHCPOFFER"); retval = dhcpRecvReply(descrp, data, recvTimeout); if (OK != retval) { DHCP_TRACE("Failed to receive DHCPOFFER"); data->state = DHCPC_STATE_INIT; if (TIMEOUT != retval) { sleep(delay); } break; } /* Send DHCPREQUEST to the server based on the DHCPOFFER. */ DHCP_TRACE("Sending DHCPREQUEST"); retval = dhcpSendRequest(descrp, data); if (OK == retval) { DHCP_TRACE("Sent DHCPREQUEST"); data->state = DHCPC_STATE_REQUESTING; } else { DHCP_TRACE("Failed to send DHCPREQUEST"); data->state = DHCPC_STATE_INIT; sleep(delay); } break; case DHCPC_STATE_REQUESTING: /* Wait for DHCPACK from the server to whom we sent the DHCPREQUEST. * */ DHCP_TRACE("Waiting for DHCPACK"); retval = dhcpRecvReply(descrp, data, recvTimeout); if (OK == retval) { DHCP_TRACE("Received DHCPACK"); data->state = DHCPC_STATE_BOUND; return OK; } else { DHCP_TRACE("Failed to receive DHCPACK"); data->state = DHCPC_STATE_INIT; if (TIMEOUT != retval) { sleep(delay); } } break; } } return TIMEOUT; }
/** Queries a DHCP server and calls a callback once for each option. * * Queries the sub-net DHCP server for a specified set of DHCP options * and then calls a user-supplied callback function once for each of * the desired options returned by the server. * * @param[in] dhcpOptCodes - An array of option codes to query for. * @param[in] dhcpOptCodeCnt - The number of elements in @p dhcpOptCodes. * @param[in] dhcpInfoCB - The callback function to call for each option. * @param[in] context - callback context. * * @return Zero on success; non-zero on failure, with errno set to * ENOTCONN (read error), ETIMEDOUT (read timeout), ENOMEM (out of * memory), or EINVAL (on parse error). */ int DHCPGetOptionInfo(unsigned char * dhcpOptCodes, int dhcpOptCodeCnt, DHCPInfoCallBack * dhcpInfoCB, void * context) { uint32_t xid; time_t timer; struct timeval tv; sockfd_t sockfd; int retries; struct sockaddr_storage sendaddr; unsigned char chaddr[MAX_MACADDR_SIZE]; unsigned char hlen, htype; uint8_t sndbuf[512]; uint8_t rcvbuf[512]; struct hostent * hep; uint8_t * p; int rcvbufsz = 0; char host[256]; /* Get our IP and MAC addresses */ if (gethostname(host, (int)sizeof(host)) || (hep = gethostbyname(host)) == 0 || dhcpGetAddressInfo((unsigned char *)hep->h_addr, chaddr, &hlen, &htype)) return -1; /* get a reasonably random transaction id value */ xid = (uint32_t)time(&timer); /* BOOTP request header */ p = sndbuf; *p++ = BOOTREQUEST; /* opcode */ *p++ = htype; *p++ = hlen; *p++ = 0; /* hops */ PutUINT32(&p, xid); PutUINT16(&p, 0); /* seconds */ PutUINT16(&p, 0); /* flags */ memcpy(p, hep->h_addr, 4); /* ciaddr */ p += 4; PutUINT32(&p, 0); /* yiaddr */ PutUINT32(&p, 0); /* siaddr */ PutUINT32(&p, 0); /* giaddr */ memcpy(p, chaddr, hlen); /* chaddr */ p += hlen; memset(p, 0, 16-hlen); /* remaining chaddr space */ p += 16-hlen; memset(p, 0, 64 + 128); /* server host name, boot file */ p += 64 + 128; /* BOOTP options field */ *p++ = DHCP_COOKIE1; /* options, cookies 1-4 */ *p++ = DHCP_COOKIE2; *p++ = DHCP_COOKIE3; *p++ = DHCP_COOKIE4; /* DHCP Message Type option */ *p++ = TAG_DHCP_MSG_TYPE; *p++ = 1; /* option length */ *p++ = DHCP_MSG_INFORM; /* message type is DHCPINFORM */ /* DHCP Parameter Request option */ *p++ = TAG_DHCP_PARAM_REQ; /* request for DHCP parms */ *p++ = (unsigned char)dhcpOptCodeCnt; memcpy(p, dhcpOptCodes, dhcpOptCodeCnt); p += dhcpOptCodeCnt; /* DHCP Client Identifier option */ *p++ = TAG_CLIENT_IDENTIFIER; *p++ = hlen + 1; /* option length */ *p++ = htype; /* client id is htype/haddr */ memcpy(p, chaddr, hlen); p += hlen; /* End option */ *p++ = TAG_END; /* get a broadcast send/recv socket and address */ if ((sockfd = dhcpCreateBCSkt(&sendaddr)) == SLP_INVALID_SOCKET) return -1; /* setup select timeout */ tv.tv_sec = 0; tv.tv_usec = INIT_TMOUT_USECS; retries = 0; srand((unsigned)time(&timer)); while (retries++ < MAX_DHCP_RETRIES) { if (dhcpSendRequest(sockfd, sndbuf, p - sndbuf, &sendaddr, sizeof(sendaddr), &tv) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if ((rcvbufsz = dhcpRecvResponse(sockfd, rcvbuf, sizeof(rcvbuf), &tv)) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if (rcvbufsz >= 236 && AS_UINT32(&rcvbuf[4]) == xid) break; /* exponential backoff randomized by a * uniform number between -1 and 1 */ tv.tv_usec = tv.tv_usec * 2 + (rand() % 3) - 1; tv.tv_sec = tv.tv_usec / USECS_PER_SEC; tv.tv_usec %= USECS_PER_SEC; } closesocket(sockfd); return rcvbufsz? dhcpProcessOptions(rcvbuf + 236, rcvbufsz - 236, dhcpInfoCB, context): -1; }