void Dhcp_PktHandler(PBYTE pInBuffer, USHORT usLen) { PDHCPMSG pDhcpMsg = (PDHCPMSG)pInBuffer; if (EqualMemory( (char *) pDhcpMsg->options, (char *) DHCP_MAGIC_COOKIE, 4)) { CopyMemory(ucDhcpTransId, pDhcpMsg->xid, 4); switch (_ParseOptions(&pDhcpMsg->options[4], DHCPMSG_OPTION_LENGTH)) { case DHCP_STATE_OFFER: _SendOffer((PDHCPMSG)pInBuffer); break; case DHCP_STATE_ACK: _SendAck((PDHCPMSG)pInBuffer); break; case DHCP_STATE_NAK: _SendNak((PDHCPMSG)pInBuffer); break; } } }
status_t DHCPClient::_Negotiate(dhcp_state state) { int socket = ::socket(AF_INET, SOCK_DGRAM, 0); if (socket < 0) return errno; sockaddr_in local; memset(&local, 0, sizeof(struct sockaddr_in)); local.sin_family = AF_INET; local.sin_len = sizeof(struct sockaddr_in); local.sin_port = htons(DHCP_CLIENT_PORT); local.sin_addr.s_addr = INADDR_ANY; // Enable reusing the port . This is needed in case there is more // than 1 interface that needs to be configured. Note that the only reason // this works is because there is code below to bind to a specific // interface. int option = 1; setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option)); if (bind(socket, (struct sockaddr *)&local, sizeof(local)) < 0) { close(socket); return errno; } sockaddr_in broadcast; memset(&broadcast, 0, sizeof(struct sockaddr_in)); broadcast.sin_family = AF_INET; broadcast.sin_len = sizeof(struct sockaddr_in); broadcast.sin_port = htons(DHCP_SERVER_PORT); broadcast.sin_addr.s_addr = INADDR_BROADCAST; option = 1; setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); if (state == INIT) { // The local interface does not have an address yet, bind the socket // to the device directly. int linkSocket = ::socket(AF_LINK, SOCK_DGRAM, 0); if (linkSocket >= 0) { // we need to know the index of the device to be able to bind to it ifreq request; prepare_request(request, Device()); if (ioctl(linkSocket, SIOCGIFINDEX, &request, sizeof(struct ifreq)) == 0) { setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &request.ifr_index, sizeof(int)); } close(linkSocket); } } bigtime_t previousLeaseTime = fLeaseTime; fLeaseTime = 0; fRenewalTime = 0; fRebindingTime = 0; status_t status = B_ERROR; time_t timeout; uint32 tries; _ResetTimeout(socket, timeout, tries); dhcp_message discover(DHCP_DISCOVER); _PrepareMessage(discover, state); dhcp_message request(DHCP_REQUEST); _PrepareMessage(request, state); // send discover/request message _SendMessage(socket, state == INIT ? discover : request, state != RENEWAL ? broadcast : fServer); // no need to check the status; in case of an error we'll just send // the message again // receive loop until we've got an offer and acknowledged it while (state != ACKNOWLEDGED) { char buffer[2048]; ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer), 0, NULL, NULL); if (bytesReceived < 0 && errno == B_TIMED_OUT) { // depending on the state, we'll just try again if (!_TimeoutShift(socket, timeout, tries)) { close(socket); return B_TIMED_OUT; } if (state == INIT) _SendMessage(socket, discover, broadcast); else { _SendMessage(socket, request, state != RENEWAL ? broadcast : fServer); } continue; } else if (bytesReceived < 0) break; dhcp_message *message = (dhcp_message *)buffer; if (message->transaction_id != htonl(fTransactionID) || !message->HasOptions() || memcmp(message->mac_address, discover.mac_address, discover.hardware_address_length)) { // this message is not for us continue; } switch (message->Type()) { case DHCP_NONE: default: // ignore this message break; case DHCP_OFFER: { // first offer wins if (state != INIT) break; // collect interface options fAssignedAddress = message->your_address; fConfiguration.MakeEmpty(); fConfiguration.AddString("device", Device()); fConfiguration.AddBool("auto", true); BMessage address; address.AddString("family", "inet"); address.AddString("address", _ToString(fAssignedAddress)); _ParseOptions(*message, address); fConfiguration.AddMessage("address", &address); // request configuration from the server _ResetTimeout(socket, timeout, tries); state = REQUESTING; _PrepareMessage(request, state); status = _SendMessage(socket, request, broadcast); // we're sending a broadcast so that all potential offers // get an answer break; } case DHCP_ACK: { if (state != REQUESTING && state != REBINDING && state != RENEWAL) continue; // TODO: we might want to configure the stuff, don't we? BMessage address; _ParseOptions(*message, address); // TODO: currently, only lease time and DNS is updated this way // our address request has been acknowledged state = ACKNOWLEDGED; // configure interface BMessage reply; status = Target().SendMessage(&fConfiguration, &reply); if (status == B_OK) status = reply.FindInt32("status", &fStatus); break; } case DHCP_NACK: if (state != REQUESTING) continue; // try again (maybe we should prefer other servers if this // happens more than once) status = _SendMessage(socket, discover, broadcast); if (status == B_OK) state = INIT; break; } } close(socket); if (status == B_OK && fLeaseTime > 0) { // notify early enough when the lease is if (fRenewalTime == 0) fRenewalTime = fLeaseTime * 2/3; if (fRebindingTime == 0) fRebindingTime = fLeaseTime * 5/6; bigtime_t now = system_time(); _RestartLease(fRenewalTime); fLeaseTime += now; fRenewalTime += now; fRebindingTime += now; // make lease times absolute } else { fLeaseTime = previousLeaseTime; bigtime_t now = system_time(); fRenewalTime = (fLeaseTime - now) * 2/3 + now; fRebindingTime = (fLeaseTime - now) * 5/6 + now; } return status; }