dhcp client: create class SocketUtil to simplify class DHCPV4

This takes part of the code for socket handling out of class
DHCPV4.
The new class SocketUtil is a helper class for creating
sockets and sending packet through socket.
while there, this also fixes a typo for broadcast address.

Bug: 25642025
TEST=compile, and test using python scripts

Change-Id: I5ae3018498bfbfa23f6e50c72c4845be808ba5c4
diff --git a/dhcp.h b/dhcp.h
index 74f66ad..1129773 100644
--- a/dhcp.h
+++ b/dhcp.h
@@ -42,6 +42,9 @@
     REBOOT,
     RELEASE
   };
+  // UDP port numbers for DHCP.
+  const static uint16_t kDHCPServerPort = 67;
+  const static uint16_t kDHCPClientPort = 68;
 };
 
 }  // namespace dhcp_client
diff --git a/dhcp_client.gyp b/dhcp_client.gyp
index 7ed15aa..48897e9 100644
--- a/dhcp_client.gyp
+++ b/dhcp_client.gyp
@@ -85,6 +85,7 @@
         'message_loop_event_dispatcher.cc',
         'manager.cc',
         'service.cc',
+        'socket_util.cc',
       ],
     },
     {
diff --git a/dhcpv4.cc b/dhcpv4.cc
index 2b0a7b4..721609a 100644
--- a/dhcpv4.cc
+++ b/dhcpv4.cc
@@ -17,11 +17,9 @@
 #include "dhcp_client/dhcpv4.h"
 
 #include <arpa/inet.h>
-#include <linux/filter.h>
 #include <linux/if_packet.h>
 #include <net/ethernet.h>
 #include <net/if.h>
-#include <net/if_arp.h>
 #include <netinet/ip.h>
 #include <netinet/udp.h>
 
@@ -36,6 +34,7 @@
 #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"
 
@@ -47,9 +46,6 @@
 namespace dhcp_client {
 
 namespace {
-// UDP port numbers for DHCP.
-const uint16_t kDHCPServerPort = 67;
-const uint16_t kDHCPClientPort = 68;
 // Max length of a DHCP message.
 const size_t kDHCPMessageMaxLength = 548;
 // Renewal time in terms of lease duration percentage.
@@ -76,22 +72,6 @@
 const uint8_t kZeroHardwareAddress[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 const int kArpProbeReplyTimeoutSeconds = 1;
 
-// Socket filter for dhcp packet.
-const sock_filter dhcp_bpf_filter[] = {
-  BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23 - ETH_HLEN),
-  BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
-  BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 - ETH_HLEN),
-  BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
-  BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14 - ETH_HLEN),
-  BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16 - ETH_HLEN),
-  BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, kDHCPClientPort, 0, 1),
-  BPF_STMT(BPF_RET + BPF_K, 0x0fffffff),
-  BPF_STMT(BPF_RET + BPF_K, 0),
-};
-
-const int dhcp_bpf_filter_len =
-    sizeof(dhcp_bpf_filter) / sizeof(dhcp_bpf_filter[0]);
-
 // TODO(nywang): find a place for the lease file.
 const char kIPV4LeaseFilePathFormat[] =
       "/tmp/lease-ipv4-%s.conf";
@@ -142,6 +122,7 @@
       raw_socket_(kInvalidSocketDescriptor),
       udp_socket_(kInvalidSocketDescriptor),
       sockets_(new shill::Sockets()),
+      socket_util_(new SocketUtil(interface_name_, interface_index_)),
       random_engine_(time(nullptr)) {
   ResetState();
 }
@@ -225,7 +206,7 @@
 }
 
 bool DHCPV4::Start() {
-  if (!CreateRawSocket()) {
+  if (!socket_util_->CreateRawSocket(&raw_socket_)) {
     return false;
   }
   input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
@@ -287,100 +268,6 @@
   arp_client_->Stop();
 }
 
-bool DHCPV4::CreateRawSocket() {
-  int fd = sockets_->Socket(PF_PACKET,
-                            SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                            htons(ETHERTYPE_IP));
-  if (fd == kInvalidSocketDescriptor) {
-    PLOG(ERROR) << "Failed to create socket";
-    return false;
-  }
-  shill::ScopedSocketCloser socket_closer(sockets_.get(), fd);
-
-  // Apply the socket filter.
-  sock_fprog pf;
-  memset(&pf, 0, sizeof(pf));
-  pf.filter = const_cast<sock_filter*>(dhcp_bpf_filter);
-  pf.len = dhcp_bpf_filter_len;
-
-  if (sockets_->AttachFilter(fd, &pf) != 0) {
-    PLOG(ERROR) << "Failed to attach filter";
-    return false;
-  }
-
-  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_ll local;
-  memset(&local, 0, sizeof(local));
-  local.sll_family = PF_PACKET;
-  local.sll_protocol = htons(ETHERTYPE_IP);
-  local.sll_ifindex = static_cast<int>(interface_index_);
-
-  if (sockets_->Bind(fd,
-                     reinterpret_cast<struct sockaddr*>(&local),
-                     sizeof(local)) < 0) {
-    PLOG(ERROR) << "Failed to bind to address";
-    return false;
-  }
-
-  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;
-}
-
 void DHCPV4::HandleOffer(const DHCPMessage& msg) {
   LOG(INFO) << __func__;
   if (state_ != State::SELECT) {
@@ -483,7 +370,7 @@
   // Set the option parameters.
   subnet_mask_ = msg.subnet_mask();
   interface_mtu_ = msg.interface_mtu();
-  broadcast_address = msg.broadcast_address();
+  broadcast_address_ = msg.broadcast_address();
   router_ = msg.router();
   dns_server_ = msg.dns_server();
   vendor_specific_info_ = msg.vendor_specific_info();
@@ -494,7 +381,7 @@
   }
   // Send the DHCP configuration to Shill.
   EmitEvent(kReasonBound);
-  CreateUdpSocket();
+  socket_util_->CreateUdpSocket(&udp_socket_);
 }
 
 void DHCPV4::HandleNak(const DHCPMessage& msg) {
@@ -551,7 +438,7 @@
     LOG(ERROR) << "Failed to serialize a DHCP discover message";
     return false;
   }
-  if (!SendBroadcastPacket(packet)) {
+  if (!socket_util_->SendBroadcastPacket(raw_socket_, packet)) {
     LOG(ERROR) << "Failed to send a DHCP discover packet";
     return false;
   }
@@ -656,7 +543,7 @@
       LOG(ERROR) << "Failed to make a DHCP request packet";
       return false;
     }
-    if (!SendUnicastPacket(packet)) {
+    if (!socket_util_->SendUnicastPacket(udp_socket_, packet, server_ip_)) {
       LOG(ERROR) << "Failed to send a DHCP request packet";
       return false;
     }
@@ -666,7 +553,7 @@
       LOG(ERROR) << "Failed to make a DHCP request raw packet";
       return false;
     }
-    if (!SendBroadcastPacket(packet)) {
+    if (!socket_util_->SendBroadcastPacket(raw_socket_, packet)) {
       LOG(ERROR) << "Failed to send a DHCP request packet";
       return false;
     }
@@ -698,7 +585,7 @@
                   "there is no socket for unicast";
     return false;
   }
-  if (!SendUnicastPacket(packet)) {
+  if (!socket_util_->SendUnicastPacket(udp_socket_, packet, server_ip_)) {
     LOG(ERROR) << "Failed to send a DHCP release packet";
     return false;
   }
@@ -780,53 +667,6 @@
   return true;
 }
 
-bool DHCPV4::SendBroadcastPacket(const ByteString& packet) {
-  LOG(INFO) << __func__;
-  struct sockaddr_ll remote;
-  memset(&remote, 0, sizeof(remote));
-  remote.sll_family = AF_PACKET;
-  remote.sll_protocol = htons(ETHERTYPE_IP);
-  remote.sll_ifindex = interface_index_;
-  remote.sll_hatype = htons(ARPHRD_ETHER);
-  // Use broadcast hardware address.
-  remote.sll_halen = IFHWADDRLEN;
-  memset(remote.sll_addr, 0xff, IFHWADDRLEN);
-
-  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,
-                                   reinterpret_cast<struct sockaddr *>(&remote),
-                                   sizeof(remote));
-
-  if (result != packet.GetLength()) {
-    PLOG(ERROR) << "Socket sento failed";
-    return false;
-  }
-  return true;
-}
 
 bool DHCPV4::ValidateOptions(const DHCPMessage& msg) {
   uint32_t lease_time = msg.lease_time();
@@ -978,4 +818,3 @@
 }
 
 }  // namespace dhcp_client
-
diff --git a/dhcpv4.h b/dhcpv4.h
index 8631cc5..6fd5949 100644
--- a/dhcpv4.h
+++ b/dhcpv4.h
@@ -36,6 +36,7 @@
 namespace dhcp_client {
 
 class ServiceAdaptorInterface;
+class SocketUtil;
 
 class DHCPV4 : public DHCP {
  public:
@@ -58,8 +59,6 @@
   void ArpProbeReplyReceivedTask(int fd);
   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);
@@ -70,8 +69,6 @@
   bool SendDiscover();
   bool SendRequest();
   bool SendRelease();
-  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);
@@ -157,6 +154,8 @@
   int udp_socket_;
   // Helper class with wrapped socket relavent functions.
   std::unique_ptr<shill::Sockets> sockets_;
+  // Helper class handling sockets for DHCP.
+  SocketUtil* socket_util_;
 
   std::default_random_engine random_engine_;
 
diff --git a/socket_util.cc b/socket_util.cc
new file mode 100644
index 0000000..d4fa1f0
--- /dev/null
+++ b/socket_util.cc
@@ -0,0 +1,206 @@
+//
+// Copyright (C) 2016 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/socket_util.h"
+
+#include <linux/filter.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <base/logging.h>
+
+#include "dhcp_client/dhcp.h"
+#include "shill/net/byte_string.h"
+#include "shill/net/sockets.h"
+
+namespace dhcp_client {
+
+namespace {
+
+// Socket filter for dhcp packet.
+const sock_filter dhcp_bpf_filter[] = {
+  BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23 - ETH_HLEN),
+  BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+  BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 - ETH_HLEN),
+  BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+  BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14 - ETH_HLEN),
+  BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16 - ETH_HLEN),
+  BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP::kDHCPClientPort, 0, 1),
+  BPF_STMT(BPF_RET + BPF_K, 0x0fffffff),
+  BPF_STMT(BPF_RET + BPF_K, 0),
+};
+
+const int dhcp_bpf_filter_len =
+    sizeof(dhcp_bpf_filter) / sizeof(dhcp_bpf_filter[0]);
+
+}  // namespace
+
+SocketUtil::SocketUtil(const std::string& interface_name,
+                       unsigned int interface_index)
+    : sockets_(new shill::Sockets()),
+      interface_name_(interface_name),
+      interface_index_(interface_index) {
+}
+
+SocketUtil::~SocketUtil() {}
+
+bool SocketUtil::CreateUdpSocket(int* udp_socket) {
+  // Close previous Udp Socket.
+  if (*udp_socket != shill::Sockets::kInvalidFileDescriptor) {
+    sockets_->Close(*udp_socket);
+    *udp_socket = shill::Sockets::kInvalidFileDescriptor;
+  }
+  int fd = sockets_->Socket(PF_INET,
+                            SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                            IPPROTO_IP);
+  if (fd == shill::Sockets::kInvalidFileDescriptor) {
+    PLOG(ERROR) << "Failed to create socket";
+    return false;
+  }
+  shill::ScopedSocketCloser socket_closer(sockets_, 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(DHCP::kDHCPServerPort);
+
+  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;
+}
+
+bool SocketUtil::CreateRawSocket(int* raw_socket) {
+  int fd = sockets_->Socket(PF_PACKET,
+                            SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                            htons(ETHERTYPE_IP));
+  if (fd == shill::Sockets::kInvalidFileDescriptor) {
+    PLOG(ERROR) << "Failed to create socket";
+    return false;
+  }
+  shill::ScopedSocketCloser socket_closer(sockets_, fd);
+
+  // Apply the socket filter.
+  sock_fprog pf;
+  memset(&pf, 0, sizeof(pf));
+  pf.filter = const_cast<sock_filter*>(dhcp_bpf_filter);
+  pf.len = dhcp_bpf_filter_len;
+
+  if (sockets_->AttachFilter(fd, &pf) != 0) {
+    PLOG(ERROR) << "Failed to attach filter";
+    return false;
+  }
+
+  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_ll local;
+  memset(&local, 0, sizeof(local));
+  local.sll_family = PF_PACKET;
+  local.sll_protocol = htons(ETHERTYPE_IP);
+  local.sll_ifindex = static_cast<int>(interface_index_);
+
+  if (sockets_->Bind(fd,
+                     reinterpret_cast<struct sockaddr*>(&local),
+                     sizeof(local)) < 0) {
+    PLOG(ERROR) << "Failed to bind to address";
+    return false;
+  }
+
+  *raw_socket = socket_closer.Release();
+  return true;
+}
+
+bool SocketUtil::SendBroadcastPacket(int raw_socket,
+                                     const shill::ByteString& packet) {
+  LOG(INFO) << __func__;
+  struct sockaddr_ll remote;
+  memset(&remote, 0, sizeof(remote));
+  remote.sll_family = AF_PACKET;
+  remote.sll_protocol = htons(ETHERTYPE_IP);
+  remote.sll_ifindex = interface_index_;
+  remote.sll_hatype = htons(ARPHRD_ETHER);
+  // Use broadcast hardware address.
+  remote.sll_halen = IFHWADDRLEN;
+  memset(remote.sll_addr, 0xff, IFHWADDRLEN);
+
+  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 SocketUtil::SendUnicastPacket(int udp_socket,
+                                   const shill::ByteString& packet,
+                                   uint32_t server_ip) {
+  LOG(INFO) << __func__;
+  struct sockaddr_in remote;
+  memset(&remote, 0, sizeof(remote));
+  remote.sin_family = AF_INET;
+  remote.sin_port = htons(DHCP::kDHCPServerPort);
+  remote.sin_addr.s_addr = htonl(server_ip);
+
+  size_t result = sockets_->SendTo(udp_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;
+}
+
+}  // namespace dhcp_client
diff --git a/socket_util.h b/socket_util.h
new file mode 100644
index 0000000..64e4528
--- /dev/null
+++ b/socket_util.h
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2016 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.
+//
+
+#ifndef DHCP_CLIENT_SOCKET_UTIL_H_
+#define DHCP_CLIENT_SOCKET_UTIL_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+namespace shill {
+
+class ByteString;
+class Sockets;
+
+}  // namespace shill
+
+namespace dhcp_client {
+
+class SocketUtil {
+ public:
+  SocketUtil(const std::string& interface_name,
+             unsigned int interface_index);
+  virtual ~SocketUtil();
+  // Create a raw socket and configure it with a UDP socket filter.
+  bool CreateRawSocket(int* raw_socket);
+  // Create a udp socket.
+  bool CreateUdpSocket(int* udp_socket);
+  // Send broadcast packet using a given raw socekt.
+  bool SendBroadcastPacket(int raw_socket,
+                           const shill::ByteString& buffer);
+  // Send unicast packet using a given udp socekt.
+  bool SendUnicastPacket(int udp_socket,
+                         const shill::ByteString& buffer,
+                         uint32_t server_ip);
+ private:
+  // Helper class with wrapped socket relavent functions.
+  shill::Sockets* sockets_;
+  std::string interface_name_;
+  unsigned int interface_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketUtil);
+};
+
+}  // namespace dhcp_client
+
+#endif  // DHCP_CLIENT_SOCKET_UTIL_H_