blob: 721609a228e3c3f2789402e2c60f302995f24e4f [file] [log] [blame] [edit]
//
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "dhcp_client/dhcpv4.h"
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <random>
#include <vector>
#include <base/bind.h>
#include <base/logging.h>
#include <brillo/variant_dictionary.h>
#include "dhcp_client/dhcp_message.h"
#include "dhcp_client/dhcp_options.h"
#include "dhcp_client/file_io.h"
#include "dhcp_client/service_adaptor_interface.h"
#include "dhcp_client/socket_util.h"
#include "shill/net/arp_packet.h"
#include "shill/net/ip_address.h"
using base::Bind;
using base::Unretained;
using shill::ByteString;
using shill::IOHandlerFactoryContainer;
namespace dhcp_client {
namespace {
// Max length of a DHCP message.
const size_t kDHCPMessageMaxLength = 548;
// Renewal time in terms of lease duration percentage.
const uint32_t kRenewalTimePercentage = 50;
// Rebinding time in terms of lease duration percentage.
const uint32_t kRebindTimePercentage = 87;
const int kInvalidSocketDescriptor = shill::Sockets::kInvalidFileDescriptor;
// RFC 791: the minimum value for a correct header is 20 octets.
// The maximum value is 60 octets.
const size_t kIPHeaderMinLength = 20;
const size_t kIPHeaderMaxLength = 60;
// DHCP parameters we request from server.
const uint8_t kDefaultParameterRequestList[] = {
kDHCPOptionSubnetMask,
kDHCPOptionInterfaceMTU,
kDHCPOptionBroadcastAddr,
kDHCPOptionRouter,
kDHCPOptionDNSServer,
kDHCPOptionDomainName,
kDHCPOptionLeaseTime,
kDHCPOptionRenewalTime,
kDHCPOptionRebindingTime
};
const uint8_t kZeroHardwareAddress[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const int kArpProbeReplyTimeoutSeconds = 1;
// TODO(nywang): find a place for the lease file.
const char kIPV4LeaseFilePathFormat[] =
"/tmp/lease-ipv4-%s.conf";
// TODO(nywang): These constant will be moved to:
// <dbus/dhcp_client/dbus-constants.h>
// In this way shill can include this header and parse
// the messages.
const char kConfigurationKeyBroadcastAddress[] =
"BroadcastAddress";
const char kConfigurationKeyDNS[] = "DomainNameServers";
const char kConfigurationKeyDomainName[] = "DomainName";
const char kConfigurationKeyIPAddress[] = "IPAddress";
const char kConfigurationKeyMTU[] = "InterfaceMTU";
const char kConfigurationKeyRouters[] = "Routers";
const char kConfigurationKeyVendorEncapsulatedOptions[] =
"VendorEncapsulatedOptions";
const char kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
const char kReasonBound[] = "BOUND";
const char kReasonFail[] = "FAIL";
const char kReasonNak[] = "NAK";
} // namespace
DHCPV4::DHCPV4(ServiceAdaptorInterface* adaptor,
const std::string& interface_name,
const ByteString& hardware_address,
unsigned int interface_index,
const std::string& network_id,
bool request_hostname,
bool arp_gateway,
bool unicast_arp,
EventDispatcherInterface* event_dispatcher)
: adaptor_(adaptor),
interface_name_(interface_name),
hardware_address_(hardware_address),
interface_index_(interface_index),
network_id_(network_id),
request_hostname_(request_hostname),
arp_gateway_(arp_gateway),
unicast_arp_(unicast_arp),
arp_client_(new shill::ArpClient(interface_index)),
event_dispatcher_(event_dispatcher),
io_handler_factory_(
IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
weak_ptr_factory_(this),
state_(State::INIT),
raw_socket_(kInvalidSocketDescriptor),
udp_socket_(kInvalidSocketDescriptor),
sockets_(new shill::Sockets()),
socket_util_(new SocketUtil(interface_name_, interface_index_)),
random_engine_(time(nullptr)) {
ResetState();
}
DHCPV4::~DHCPV4() {
Stop();
}
void DHCPV4::ResetState() {
state_ = State::INIT;
server_identifier_ = 0;
transaction_id_ = 0;
offered_ip_address_ = 0;
client_ip_ = INADDR_ANY;
server_ip_ = INADDR_BROADCAST;
renewal_task_callback_.Cancel();
rebind_task_callback_.Cancel();
arp_probe_reply_timeout_task_callback_.Cancel();
}
void DHCPV4::ParseRawPacket(shill::InputData* data) {
LOG(INFO) << __func__;
if (data->len < sizeof(iphdr)) {
LOG(ERROR) << "Invalid packet length from buffer";
return;
}
// The socket filter has finished part the header validation.
// This function will perform the remaining part.
int header_len = ValidatePacketHeader(data->buf, data->len);
if (header_len == -1) {
return;
}
unsigned char* buffer = data->buf + header_len;
DHCPMessage msg;
if (!DHCPMessage::InitFromBuffer(buffer, data->len - header_len, &msg)) {
LOG(ERROR) << "Failed to initialize DHCP message from buffer";
return;
}
// In INIT state the client ignores all messages from server.
if (state_ == State::INIT) {
return;
}
// Check transaction id with the existing one.
if (msg.transaction_id() != transaction_id_) {
LOG(ERROR) << "Transaction id(xid) doesn't match";
return;
}
uint8_t message_type = msg.message_type();
switch (message_type) {
case kDHCPMessageTypeOffer:
HandleOffer(msg);
break;
case kDHCPMessageTypeAck:
HandleAck(msg);
break;
case kDHCPMessageTypeNak:
HandleNak(msg);
break;
default:
LOG(ERROR) << "Invalid message type: "
<< static_cast<int>(message_type);
}
}
bool DHCPV4::WriteLease() {
std::string lease_file =
base::StringPrintf(kIPV4LeaseFilePathFormat, network_id_.c_str());
LOG(INFO) << "Write lease to file";
shill::ByteString lease =
ByteString(reinterpret_cast<unsigned char*>(&offered_ip_address_),
sizeof(uint32_t));
if (!FileIO::GetInstance()->Write(lease_file, lease)) {
LOG(ERROR) << "Failed to write lease to file";
return false;
}
return true;
}
void DHCPV4::OnReadError(const std::string& error_msg) {
LOG(INFO) << __func__;
}
bool DHCPV4::Start() {
if (!socket_util_->CreateRawSocket(&raw_socket_)) {
return false;
}
input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
raw_socket_,
Bind(&DHCPV4::ParseRawPacket, Unretained(this)),
Bind(&DHCPV4::OnReadError, Unretained(this))));
if (!network_id_.empty() && ReadLease()) {
state_ = State::INIT_REBOOT;
LOG(INFO) << "Start from INIT_REBOOT state";
if (!SendRequest()) {
ResetState();
EmitEvent(kReasonFail);
return false;
}
state_ = State::REBOOT;
return true;
}
if (!SendDiscover()) {
return false;
}
return true;
}
bool DHCPV4::ReadLease() {
std::string lease_file =
base::StringPrintf(kIPV4LeaseFilePathFormat, network_id_.c_str());
if (!FileIO::GetInstance()->PathExists(lease_file)) {
LOG(INFO) << "Lease file does not exist";
return false;
}
ByteString config;
if (!FileIO::GetInstance()->Read(lease_file, &config)) {
LOG(ERROR) << "Failed to read lease file";
}
transaction_id_ = static_cast<uint32_t>(
std::uniform_int_distribution<unsigned int>()
(random_engine_) % UINT32_MAX + 1);
offered_ip_address_ =
*reinterpret_cast<const uint32_t*>(config.GetConstData());
return true;
}
void DHCPV4::Stop() {
input_handler_.reset();
if (HasALease()) {
if (!SendRelease()) {
LOG(ERROR) << "Failed to send DHCP release message";
}
ResetState();
}
if (raw_socket_ != kInvalidSocketDescriptor) {
sockets_->Close(raw_socket_);
raw_socket_ = kInvalidSocketDescriptor;
}
if (udp_socket_ != kInvalidSocketDescriptor) {
sockets_->Close(udp_socket_);
udp_socket_ = kInvalidSocketDescriptor;
}
arp_client_->Stop();
}
void DHCPV4::HandleOffer(const DHCPMessage& msg) {
LOG(INFO) << __func__;
if (state_ != State::SELECT) {
LOG(WARNING) << "Ignore DHCP Offer message";
return;
}
if (msg.server_identifier() == 0) {
LOG(ERROR) << "Option server identifier is missing";
return;
}
LOG(INFO) << "Server IP: " << IPtoString(msg.server_identifier());
uint32_t your_ip_address = msg.your_ip_address();
if (your_ip_address == INADDR_ANY ||
your_ip_address == INADDR_BROADCAST) {
LOG(ERROR) << "Reject invalid IP address from server";
return;
}
LOG(INFO) << "Lease IP: " << IPtoString(your_ip_address);
if (!ValidateOptions(msg)) {
return;
}
// Accept this offer.
LOG(INFO) << "Accept offer with ip: " << IPtoString(your_ip_address);
// Set states variables
offered_ip_address_ = your_ip_address;
server_identifier_ = msg.server_identifier();
transaction_id_ = msg.transaction_id();
if (!SendRequest()) {
return;
}
// Set state upon success.
state_ = State::REQUEST;
}
void DHCPV4::HandleAck(const DHCPMessage& msg) {
LOG(INFO) << __func__;
if (state_ != State::REQUEST &&
state_ != State::RENEW &&
state_ != State::REBIND &&
state_ != State::REBOOT) {
LOG(WARNING) << "Ignore DHCP Ack message";
return;
}
if (msg.server_identifier() == 0) {
LOG(ERROR) << "Option server identifier is missing";
return;
}
if (state_ == State::REBOOT) {
server_identifier_ = msg.server_identifier();
}
if (msg.server_identifier() != server_identifier_) {
LOG(ERROR) << "Option server doesn't match";
return;
}
LOG(INFO) << "Server IP: " << IPtoString(server_identifier_);
uint32_t your_ip_address = msg.your_ip_address();
// In case that this offered ip is different from we got from DHCP offer.
if (offered_ip_address_ != msg.your_ip_address()) {
LOG(ERROR) << "Reject invalid IP address from server";
return;
}
LOG(INFO) << "Lease IP: " << IPtoString(your_ip_address);
if (!ValidateOptions(msg)) {
return;
}
CheckIpCollision();
// TODO(nywang): Validate the gateway address using ARP.
// Accept the ACK.
uint32_t lease_time = msg.lease_time();
uint32_t renewal_time = lease_time * kRenewalTimePercentage / 100;
uint32_t rebinding_time = lease_time * kRebindTimePercentage / 100;
// Override the renewal time or rebinding time if ACK specifies the value.
if (msg.renewal_time() != 0) {
renewal_time = msg.renewal_time();
}
if (msg.rebinding_time() != 0) {
rebinding_time = msg.rebinding_time();
}
renewal_task_callback_.Reset(
Bind(&DHCPV4::RenewalTask, weak_ptr_factory_.GetWeakPtr()));
rebind_task_callback_.Reset(
Bind(&DHCPV4::RebindTask, weak_ptr_factory_.GetWeakPtr()));
event_dispatcher_->PostDelayedTask(renewal_task_callback_.callback(),
1000 * renewal_time);
event_dispatcher_->PostDelayedTask(rebind_task_callback_.callback(),
1000 * rebinding_time);
// Set state variables upon a valid Ack.
state_ = State::BOUND;
client_ip_ = offered_ip_address_;
server_ip_ = server_identifier_;
// Set the option parameters.
subnet_mask_ = msg.subnet_mask();
interface_mtu_ = msg.interface_mtu();
broadcast_address_ = msg.broadcast_address();
router_ = msg.router();
dns_server_ = msg.dns_server();
vendor_specific_info_ = msg.vendor_specific_info();
domain_name_ = msg.domain_name();
// Write lease to persistent stotrage.
if (!network_id_.empty()) {
WriteLease();
}
// Send the DHCP configuration to Shill.
EmitEvent(kReasonBound);
socket_util_->CreateUdpSocket(&udp_socket_);
}
void DHCPV4::HandleNak(const DHCPMessage& msg) {
LOG(INFO) << __func__;
if (state_ != State::REBIND &&
state_ != State::RENEW &&
state_ != State::REQUEST &&
state_ != State::REBOOT) {
LOG(WARNING) << "Ignore DHCP Nak message";
return;
}
if (msg.server_identifier() == 0) {
LOG(ERROR) << "Option server identifier is missing";
return;
}
if (msg.server_identifier() != server_identifier_) {
LOG(ERROR) << "Option server doesn't match";
return;
}
if (msg.transaction_id() != transaction_id_) {
LOG(ERROR) << "transaction id doesn't match";
return;
}
// A Nak message should contain the error message.
if (msg.error_message().size()) {
LOG(INFO) << "Received DHCP NAK message with the following error message: "
<< msg.error_message();
}
// Set state variables upon receiving a valid Nak.
ResetState();
EmitEvent(kReasonNak);
}
bool DHCPV4::SendDiscover() {
LOG(INFO) << __func__;
DHCPMessage message;
DHCPMessage::InitRequest(&message);
message.SetMessageType(kDHCPMessageTypeDiscover);
message.SetClientHardwareAddress(hardware_address_);
uint32_t transaction_id =
static_cast<uint32_t>(
std::uniform_int_distribution<unsigned int>()(
random_engine_) % UINT32_MAX + 1);
message.SetTransactionID(transaction_id);
message.SetParameterRequestList(
std::vector<uint8_t>(kDefaultParameterRequestList,
kDefaultParameterRequestList +
sizeof(kDefaultParameterRequestList)));
ByteString packet;
if (!MakeRawPacket(message, &packet)) {
LOG(ERROR) << "Failed to serialize a DHCP discover message";
return false;
}
if (!socket_util_->SendBroadcastPacket(raw_socket_, packet)) {
LOG(ERROR) << "Failed to send a DHCP discover packet";
return false;
}
// Set state variables upon success.
state_ = State::SELECT;
transaction_id_ = transaction_id;
return true;
}
void DHCPV4::CheckIpCollision() {
if (!arp_client_->StartReplyListener()) {
LOG(ERROR) << "Failed to start ARP client";
return;
}
receive_arp_response_handler_.reset(
io_handler_factory_->CreateIOReadyHandler(
arp_client_->socket(),
shill::IOHandler::kModeInput,
Bind(&DHCPV4::ArpProbeReplyReceivedTask,
weak_ptr_factory_.GetWeakPtr())));
// According to RFC 5227, ARP Probe uses all-zero 'sender address' to avoid
// polluting ARP caches. The 'target address' should be set to address being
// probed.
// RFC 5227 also states that target MAC address should be all-zero.
shill::ArpPacket request(shill::IPAddress(IPtoString(INADDR_ANY)),
shill::IPAddress(IPtoString(offered_ip_address_)),
hardware_address_,
ByteString(kZeroHardwareAddress, IFHWADDRLEN));
LOG(INFO) << "Probing expected ip address: "
<< IPtoString(offered_ip_address_);
if (!arp_client_->TransmitRequest(request)) {
LOG(ERROR) << "Failed to send ARP Probe request";
arp_client_->Stop();
receive_arp_response_handler_.reset();
return;
}
// We succeeded to send ARP probe:
arp_probe_reply_timeout_task_callback_.Reset(
Bind(&DHCPV4::ArpProbeReplyTimeoutTask, weak_ptr_factory_.GetWeakPtr()));
event_dispatcher_->PostDelayedTask(
arp_probe_reply_timeout_task_callback_.callback(),
1000 * kArpProbeReplyTimeoutSeconds);
}
void DHCPV4::ArpProbeReplyReceivedTask(int fd) {
shill::ArpPacket packet;
shill::ByteString sender;
if (!arp_client_->ReceivePacket(&packet, &sender)) {
return;
}
// According to RFC 5227, we only check the sender's ip address.
shill::IPAddress offered_ip_address =
shill::IPAddress(IPtoString(offered_ip_address_));
if (!offered_ip_address.Equals(packet.local_ip_address())) {
LOG(ERROR) << "Response is not for the expecte IP address.";
return;
}
// Collision is found:
LOG(ERROR) << "IP Collision found. Discard the lease.";
arp_client_->Stop();
receive_arp_response_handler_.reset();
arp_probe_reply_timeout_task_callback_.Cancel();
// TODO(nywang): send DHCP_DECLINE to server.
ResetState();
}
void DHCPV4::ArpProbeReplyTimeoutTask() {
// TODO(nywang): Broadcast ARP announcement.
arp_client_->Stop();
receive_arp_response_handler_.reset();
arp_probe_reply_timeout_task_callback_.Cancel();
}
bool DHCPV4::SendRequest() {
LOG(INFO) << __func__;
DHCPMessage message;
DHCPMessage::InitRequest(&message);
message.SetMessageType(kDHCPMessageTypeRequest);
message.SetClientHardwareAddress(hardware_address_);
if (HasALease()) {
message.SetClientIPAddress(client_ip_);
}
if (state_ == State::SELECT || state_ == State::INIT_REBOOT) {
message.SetRequestedIpAddress(offered_ip_address_);
}
message.SetTransactionID(transaction_id_);
if (state_ == State::SELECT) {
message.SetServerIdentifier(server_identifier_);
}
message.SetParameterRequestList(
std::vector<uint8_t>(kDefaultParameterRequestList,
kDefaultParameterRequestList +
sizeof(kDefaultParameterRequestList)));
ByteString packet;
// Use unicast for renewal.
if (state_ == State::BOUND && udp_socket_ != kInvalidSocketDescriptor) {
if (!MakePacket(message, &packet)) {
LOG(ERROR) << "Failed to make a DHCP request packet";
return false;
}
if (!socket_util_->SendUnicastPacket(udp_socket_, packet, server_ip_)) {
LOG(ERROR) << "Failed to send a DHCP request packet";
return false;
}
} else {
// Use broadcast for other cases.
if (!MakeRawPacket(message, &packet)) {
LOG(ERROR) << "Failed to make a DHCP request raw packet";
return false;
}
if (!socket_util_->SendBroadcastPacket(raw_socket_, packet)) {
LOG(ERROR) << "Failed to send a DHCP request packet";
return false;
}
}
return true;
}
bool DHCPV4::SendRelease() {
LOG(INFO) << __func__;
DHCPMessage message;
DHCPMessage::InitRequest(&message);
message.SetMessageType(kDHCPMessageTypeRelease);
message.SetClientHardwareAddress(hardware_address_);
uint32_t transaction_id =
static_cast<uint32_t>(
std::uniform_int_distribution<unsigned int>()(
random_engine_) % UINT32_MAX + 1);
message.SetTransactionID(transaction_id);
message.SetServerIdentifier(server_identifier_);
ByteString packet;
if (!MakePacket(message, &packet)) {
LOG(ERROR) << "Failed to make a DHCP release packet";
return false;
}
// Use unicast for release.
if (udp_socket_ == kInvalidSocketDescriptor) {
LOG(ERROR) << "Failed to send a DHCP release message becasuse "
"there is no socket for unicast";
return false;
}
if (!socket_util_->SendUnicastPacket(udp_socket_, packet, server_ip_)) {
LOG(ERROR) << "Failed to send a DHCP release packet";
return false;
}
// Set state variables upon success.
ResetState();
return true;
}
bool DHCPV4::MakePacket(const DHCPMessage& message, ByteString* output) {
ByteString payload;
if (!message.Serialize(&payload)) {
LOG(ERROR) << "Failed to serialzie dhcp message";
return false;
}
*output = payload;
return true;
}
bool DHCPV4::MakeRawPacket(const DHCPMessage& message, ByteString* output) {
ByteString payload;
if (!message.Serialize(&payload)) {
LOG(ERROR) << "Failed to serialzie dhcp message";
return false;
}
const size_t header_len = sizeof(struct iphdr) + sizeof(struct udphdr);
const size_t payload_len = payload.GetLength();
char buffer[header_len + payload_len];
memset(buffer, 0, header_len + payload_len);
struct iphdr* ip = reinterpret_cast<struct iphdr*>(buffer);
struct udphdr* udp = reinterpret_cast<struct udphdr*>(buffer + sizeof(*ip));
if (!payload.CopyData(payload_len, buffer + header_len)) {
LOG(ERROR) << "Failed to copy data from payload";
return false;
}
udp->uh_sport = htons(kDHCPClientPort);
udp->uh_dport = htons(kDHCPServerPort);
udp->uh_ulen =
htons(static_cast<uint16_t>(sizeof(*udp) + payload.GetLength()));
// Fill pseudo header (for UDP checksum computing):
// Protocol.
ip->protocol = IPPROTO_UDP;
// Source IP address.
ip->saddr = htonl(client_ip_);
// Destination IP address.
ip->daddr = htonl(server_ip_);
// Total length, use udp packet length for pseudo header.
ip->tot_len = udp->uh_ulen;
// Calculate udp checksum based on:
// IPV4 pseudo header, UDP header, and payload.
udp->uh_sum = htons(DHCPMessage::ComputeChecksum(
reinterpret_cast<const uint8_t*>(buffer),
header_len + payload_len));
// IP version.
ip->version = IPVERSION;
// IP header length.
ip->ihl = sizeof(*ip) >> 2;
// Fragment offset field.
// The DHCP packet is always smaller than MTU,
// so fragmentation is not needed.
ip->frag_off = 0;
// Identification.
ip->id = static_cast<uint16_t>(
std::uniform_int_distribution<unsigned int>()(
random_engine_) % UINT16_MAX + 1);
// Time to live.
ip->ttl = IPDEFTTL;
// Total length.
ip->tot_len = htons(static_cast<uint16_t>(header_len+ payload.GetLength()));
// Calculate IP Checksum only based on IP header.
ip->check = htons(DHCPMessage::ComputeChecksum(
reinterpret_cast<const uint8_t*>(ip),
sizeof(*ip)));
*output = ByteString(buffer, header_len + payload_len);
return true;
}
bool DHCPV4::ValidateOptions(const DHCPMessage& msg) {
uint32_t lease_time = msg.lease_time();
if (lease_time == 0) {
LOG(ERROR) << "Reject invalid lease time";
return false;
}
LOG(INFO) << "Lease Time: " << lease_time;
if (msg.subnet_mask() == 0) {
LOG(ERROR) << "Reject invalid subnet mask";
return false;
}
LOG(INFO) << "Subnet Mask: " << IPtoString(msg.subnet_mask());
if (msg.interface_mtu() == 0) {
LOG(WARNING) << "Failed to get a valid Interface MTU";
// Shill will use a default MTU
// in case no MTU is provided by DHCP.
}
DLOG(INFO) << "Interface MTU: " << msg.interface_mtu();
if (msg.broadcast_address() == 0) {
LOG(WARNING) << "Failed to get a valid Broadcast Address";
// Shill will use a default broadcast address
// in case no broadcast address is provided by DHCP.
}
DLOG(INFO) << "Broadcast Address: " << IPtoString(msg.broadcast_address());
std::vector<uint32_t> router = msg.router();
if (router.size() == 0) {
LOG(ERROR) << "Failed to get default gateway address";
return false;
}
DLOG(INFO) << "Routers:";
for (uint32_t ip : router) {
DLOG(INFO) << IPtoString(ip);
}
std::vector<uint32_t> dns_server = msg.dns_server();
if (dns_server.size() == 0) {
LOG(WARNING) << "Failed to get DNS server address";
// Shill will use Google DNS server
// in case no DNS server is provided by DHCP.
} else {
DLOG(INFO) << "DNS Server:";
for (uint32_t ip : dns_server) {
DLOG(INFO) << IPtoString(ip);
}
}
return true;
}
int DHCPV4::ValidatePacketHeader(const unsigned char* buffer, size_t len) {
const struct iphdr* ip =
reinterpret_cast<const struct iphdr*>(buffer);
const size_t ip_header_len = static_cast<size_t>(ip->ihl) << 2;
if (ip_header_len < kIPHeaderMinLength ||
ip_header_len > kIPHeaderMaxLength) {
LOG(ERROR) << "Invalid internet header length: "
<< ip_header_len << " bytes";
return -1;
}
if (ntohs(ip->tot_len) != len) {
LOG(ERROR) << "Invalid IP total length: " << ntohs(ip->tot_len)
<< " Real packet length: " << len;
return -1;
}
// TODO(nywang): Validate other ip header fields.
const struct udphdr* udp =
reinterpret_cast<const struct udphdr*>(buffer + ip_header_len);
if (ntohs(udp->uh_sport) != kDHCPServerPort ||
ntohs(udp->uh_dport) != kDHCPClientPort) {
LOG(ERROR) << "Invlaid UDP ports";
return -1;
}
if (ntohs(udp->uh_ulen) != len - ip_header_len) {
LOG(ERROR) << "Invalid UDP total length";
return -1;
}
// TODO(nywang): Validate UDP checksum.
return ip_header_len + sizeof(*udp);
}
void DHCPV4::RenewalTask() {
LOG(INFO) << __func__;
if (state_ != State::BOUND) {
LOG(ERROR) << "Ingore renewal task";
}
SendRequest();
state_ = State::RENEW;
}
void DHCPV4::RebindTask() {
LOG(INFO) << __func__;
if (state_ != State::RENEW) {
LOG(ERROR) << "Ingore rebind task";
}
SendRequest();
state_ = State::REBIND;
}
void DHCPV4::EmitEvent(const std::string& reason) {
brillo::VariantDictionary configs;
if (reason == kReasonBound) {
configs.emplace(kConfigurationKeyIPAddress, client_ip_);
configs.emplace(kConfigurationKeyMTU, interface_mtu_);
configs.emplace(kConfigurationKeyBroadcastAddress, broadcast_address_);
configs.emplace(kConfigurationKeyRouters, router_);
configs.emplace(kConfigurationKeyDNS, dns_server_);
configs.emplace(kConfigurationKeyVendorEncapsulatedOptions,
vendor_specific_info_);
configs.emplace(kConfigurationKeyDomainName, domain_name_);
uint32_t subnet_cidr = MasktoCIDR(subnet_mask_);
configs.emplace(kConfigurationKeySubnetCIDR, subnet_cidr);
}
adaptor_->EmitEvent(reason, configs);
}
bool DHCPV4::HasALease() {
if (state_ == State::BOUND ||
state_ == State::RENEW ||
state_ == State::REBIND) {
return true;
}
return false;
}
uint32_t DHCPV4::MasktoCIDR(uint32_t subnet_mask) {
subnet_mask = ~subnet_mask;
uint32_t count = 0;
while (subnet_mask & 1) {
count++;
subnet_mask = subnet_mask >> 1;
}
return 32 - count;
}
const std::string DHCPV4::IPtoString(uint32_t ip) {
char buffer[INET_ADDRSTRLEN];
ip = htonl(ip);
const char* ip_str = inet_ntop(AF_INET, &ip, buffer, sizeof(buffer));
if (ip_str == NULL) {
return std::string("invalid ip address");
}
return std::string(ip_str);
}
} // namespace dhcp_client