dhcp client: support unicast for renewing and release am: bb41434
am: 3851c78
* commit '3851c780756a65b4b789a71e4533d199b6b176e9':
dhcp client: support unicast for renewing and release
Change-Id: I281c63c7c241ec7f0103dbe0a0542546ab7ce626
diff --git a/dhcpv4.cc b/dhcpv4.cc
index 90d73a5..2b0a7b4 100644
--- a/dhcpv4.cc
+++ b/dhcpv4.cc
@@ -56,7 +56,7 @@
const uint32_t kRenewalTimePercentage = 50;
// Rebinding time in terms of lease duration percentage.
const uint32_t kRebindTimePercentage = 87;
-const int kInvalidSocketDescriptor = -1;
+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;
@@ -73,8 +73,7 @@
kDHCPOptionRenewalTime,
kDHCPOptionRebindingTime
};
-const uint8_t kZeroHardwareAddress[] =
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+const uint8_t kZeroHardwareAddress[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const int kArpProbeReplyTimeoutSeconds = 1;
// Socket filter for dhcp packet.
@@ -140,16 +139,15 @@
IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
weak_ptr_factory_(this),
state_(State::INIT),
- socket_(kInvalidSocketDescriptor),
+ raw_socket_(kInvalidSocketDescriptor),
+ udp_socket_(kInvalidSocketDescriptor),
sockets_(new shill::Sockets()),
random_engine_(time(nullptr)) {
ResetState();
}
DHCPV4::~DHCPV4() {
- if (socket_ != kInvalidSocketDescriptor) {
Stop();
- }
}
void DHCPV4::ResetState() {
@@ -231,7 +229,7 @@
return false;
}
input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
- socket_,
+ raw_socket_,
Bind(&DHCPV4::ParseRawPacket, Unretained(this)),
Bind(&DHCPV4::OnReadError, Unretained(this))));
if (!network_id_.empty() && ReadLease()) {
@@ -272,12 +270,19 @@
void DHCPV4::Stop() {
input_handler_.reset();
- if (!SendRelease()) {
- LOG(ERROR) << "Failed to send DHCP release message";
+ if (HasALease()) {
+ if (!SendRelease()) {
+ LOG(ERROR) << "Failed to send DHCP release message";
+ }
+ ResetState();
}
- if (socket_ != kInvalidSocketDescriptor) {
- sockets_->Close(socket_);
- socket_ = kInvalidSocketDescriptor;
+ 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();
}
@@ -326,7 +331,53 @@
return false;
}
- socket_ = socket_closer.Release();
+ raw_socket_ = socket_closer.Release();
+ return true;
+}
+
+bool DHCPV4::CreateUdpSocket() {
+ // Close previous Udp Socket.
+ if (udp_socket_ != kInvalidSocketDescriptor) {
+ sockets_->Close(udp_socket_);
+ udp_socket_ = kInvalidSocketDescriptor;
+ }
+
+ int fd = sockets_->Socket(PF_INET,
+ SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ IPPROTO_IP);
+ if (fd == kInvalidSocketDescriptor) {
+ PLOG(ERROR) << "Failed to create socket";
+ return false;
+ }
+ shill::ScopedSocketCloser socket_closer(sockets_.get(), fd);
+
+ if (sockets_->ReuseAddress(fd) == -1) {
+ PLOG(ERROR) << "Failed to reuse socket address";
+ return false;
+ }
+
+ if (sockets_->BindToDevice(fd, interface_name_) < 0) {
+ PLOG(ERROR) << "Failed to bind socket to device";
+ return false;
+ }
+
+ struct sockaddr_in local;
+ memset(&local, 0, sizeof(local));
+ local.sin_family = PF_INET;
+ // We do not receive packet from this socket.
+ // At this time the ip address may not be setup by shill yet.
+ // It is a safe choice to use INADDR_ANY.
+ local.sin_addr.s_addr = htonl(INADDR_ANY);
+ local.sin_port = htons(kDHCPClientPort);
+
+ if (sockets_->Bind(fd,
+ reinterpret_cast<struct sockaddr*>(&local),
+ sizeof(local)) < 0) {
+ PLOG(ERROR) << "Failed to bind to address";
+ return false;
+ }
+
+ udp_socket_ = socket_closer.Release();
return true;
}
@@ -443,8 +494,7 @@
}
// Send the DHCP configuration to Shill.
EmitEvent(kReasonBound);
- // TODO(nywang): Setup a udp socket for future unicast, so that kernel can
- // fill the ethernet header with gateway mac address for us.
+ CreateUdpSocket();
}
void DHCPV4::HandleNak(const DHCPMessage& msg) {
@@ -501,7 +551,7 @@
LOG(ERROR) << "Failed to serialize a DHCP discover message";
return false;
}
- if (!SendRawPacket(packet)) {
+ if (!SendBroadcastPacket(packet)) {
LOG(ERROR) << "Failed to send a DHCP discover packet";
return false;
}
@@ -584,9 +634,7 @@
DHCPMessage::InitRequest(&message);
message.SetMessageType(kDHCPMessageTypeRequest);
message.SetClientHardwareAddress(hardware_address_);
- if (state_ == State::BOUND ||
- state_ == State::RENEW ||
- state_ == State::REBIND) {
+ if (HasALease()) {
message.SetClientIPAddress(client_ip_);
}
if (state_ == State::SELECT || state_ == State::INIT_REBOOT) {
@@ -601,14 +649,27 @@
kDefaultParameterRequestList +
sizeof(kDefaultParameterRequestList)));
ByteString packet;
- if (!MakeRawPacket(message, &packet)) {
- LOG(ERROR) << "Failed to serialize a DHCP request message";
- return false;
- }
- // TODO(nywang): Use unicast for renewal.
- if (!SendRawPacket(packet)) {
- LOG(ERROR) << "Failed to send a DHCP request packet";
- return false;
+
+ // 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 (!SendUnicastPacket(packet)) {
+ 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 (!SendBroadcastPacket(packet)) {
+ LOG(ERROR) << "Failed to send a DHCP request packet";
+ return false;
+ }
}
return true;
}
@@ -627,11 +688,17 @@
message.SetTransactionID(transaction_id);
message.SetServerIdentifier(server_identifier_);
ByteString packet;
- if (!MakeRawPacket(message, &packet)) {
- LOG(ERROR) << "Failed to serialize a DHCP release message";
+ if (!MakePacket(message, &packet)) {
+ LOG(ERROR) << "Failed to make a DHCP release packet";
return false;
}
- if (!SendRawPacket(packet)) {
+ // 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 (!SendUnicastPacket(packet)) {
LOG(ERROR) << "Failed to send a DHCP release packet";
return false;
}
@@ -640,6 +707,16 @@
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)) {
@@ -703,7 +780,7 @@
return true;
}
-bool DHCPV4::SendRawPacket(const ByteString& packet) {
+bool DHCPV4::SendBroadcastPacket(const ByteString& packet) {
LOG(INFO) << __func__;
struct sockaddr_ll remote;
memset(&remote, 0, sizeof(remote));
@@ -715,7 +792,29 @@
remote.sll_halen = IFHWADDRLEN;
memset(remote.sll_addr, 0xff, IFHWADDRLEN);
- size_t result = sockets_->SendTo(socket_,
+ size_t result = sockets_->SendTo(raw_socket_,
+ packet.GetConstData(),
+ packet.GetLength(),
+ 0,
+ reinterpret_cast<struct sockaddr *>(&remote),
+ sizeof(remote));
+
+ if (result != packet.GetLength()) {
+ PLOG(ERROR) << "Socket sento failed";
+ return false;
+ }
+ return true;
+}
+
+bool DHCPV4::SendUnicastPacket(const ByteString& packet) {
+ LOG(INFO) << __func__;
+ struct sockaddr_in remote;
+ memset(&remote, 0, sizeof(remote));
+ remote.sin_family = AF_INET;
+ remote.sin_port = htons(kDHCPServerPort);
+ remote.sin_addr.s_addr = htonl(server_ip_);
+
+ size_t result = sockets_->SendTo(udp_socket_,
packet.GetConstData(),
packet.GetLength(),
0,
@@ -819,9 +918,7 @@
if (state_ != State::BOUND) {
LOG(ERROR) << "Ingore renewal task";
}
- if (!SendRequest()) {
- return;
- }
+ SendRequest();
state_ = State::RENEW;
}
@@ -830,9 +927,7 @@
if (state_ != State::RENEW) {
LOG(ERROR) << "Ingore rebind task";
}
- if (!SendRequest()) {
- return;
- }
+ SendRequest();
state_ = State::REBIND;
}
@@ -853,6 +948,15 @@
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;
diff --git a/dhcpv4.h b/dhcpv4.h
index 411cf66..8631cc5 100644
--- a/dhcpv4.h
+++ b/dhcpv4.h
@@ -59,6 +59,9 @@
void ArpProbeReplyTimeoutTask();
void CheckIpCollision();
bool CreateRawSocket();
+ bool CreateUdpSocket();
+ bool HasALease();
+ bool MakePacket(const DHCPMessage& message, shill::ByteString* buffer);
bool MakeRawPacket(const DHCPMessage& message, shill::ByteString* buffer);
void OnReadError(const std::string& error_msg);
void ParseRawPacket(shill::InputData* data);
@@ -67,7 +70,8 @@
bool SendDiscover();
bool SendRequest();
bool SendRelease();
- bool SendRawPacket(const shill::ByteString& buffer);
+ bool SendBroadcastPacket(const shill::ByteString& buffer);
+ bool SendUnicastPacket(const shill::ByteString& buffer);
// Validate the IP and UDP header and return the total headers length.
// Return -1 if any header is invalid.
int ValidatePacketHeader(const unsigned char* buffer, size_t len);
@@ -148,7 +152,9 @@
base::CancelableClosure arp_probe_reply_timeout_task_callback_;
// Socket used for sending and receiving DHCP messages.
- int socket_;
+ int raw_socket_;
+ // Socket used for sending unicast DHCP messages.
+ int udp_socket_;
// Helper class with wrapped socket relavent functions.
std::unique_ptr<shill::Sockets> sockets_;