示例#1
0
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;
		}
	}
}
示例#2
0
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;
}