shill: rtnl: Handle setting of peer address

Bonus changes: Rename "subnet_cidr" to "subnet_prefix".  Set a default
subnet prefix for OpenVPNDriver IPConfigs.

BUG=chromium-os:28003
TEST=Unit tests.

Change-Id: I953748b3ecb1a45237cedc18482c647eebe27efa
Reviewed-on: https://gerrit.chromium.org/gerrit/18461
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Paul Stewart <pstew@chromium.org>
diff --git a/connection.cc b/connection.cc
index bdec018..cb1012b 100644
--- a/connection.cc
+++ b/connection.cc
@@ -56,7 +56,7 @@
     LOG(ERROR) << "Local address " << properties.address << " is invalid";
     return;
   }
-  local.set_prefix(properties.subnet_cidr);
+  local.set_prefix(properties.subnet_prefix);
 
   IPAddress broadcast(properties.address_family);
   if (!broadcast.SetAddressFromString(properties.broadcast_address) &&
@@ -66,7 +66,15 @@
     return;
   }
 
-  rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast);
+  IPAddress peer(properties.address_family);
+  if (!properties.peer_address.empty() &&
+      !peer.SetAddressFromString(properties.peer_address)) {
+    LOG(ERROR) << "Peer address " << properties.peer_address
+               << " is invalid";
+    return;
+  }
+
+  rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer);
 
   routing_table_->SetDefaultRoute(interface_index_, config,
                                   GetMetric(is_default_));
diff --git a/connection_unittest.cc b/connection_unittest.cc
index 728f347..0aeae1e 100644
--- a/connection_unittest.cc
+++ b/connection_unittest.cc
@@ -104,7 +104,7 @@
 
 TEST_F(ConnectionTest, AddConfig) {
   EXPECT_CALL(rtnl_handler_,
-              AddInterfaceAddress(kTestDeviceInterfaceIndex0, _, _));
+              AddInterfaceAddress(kTestDeviceInterfaceIndex0, _, _, _));
   EXPECT_CALL(routing_table_,
               SetDefaultRoute(kTestDeviceInterfaceIndex0,
                               ipconfig_,
@@ -165,7 +165,7 @@
   connection_->SetIsDefault(true);
 
   EXPECT_CALL(rtnl_handler_,
-              AddInterfaceAddress(kTestDeviceInterfaceIndex0, _, _));
+              AddInterfaceAddress(kTestDeviceInterfaceIndex0, _, _, _));
   EXPECT_CALL(routing_table_, SetDefaultRoute(kTestDeviceInterfaceIndex0,
                                              ipconfig_,
                                              Connection::kDefaultMetric));
diff --git a/dhcp_config.cc b/dhcp_config.cc
index ec74ff4..2511e2f 100644
--- a/dhcp_config.cc
+++ b/dhcp_config.cc
@@ -245,7 +245,7 @@
         return false;
       }
     } else if (key == kConfigurationKeySubnetCIDR) {
-      properties->subnet_cidr = value.reader().get_byte();
+      properties->subnet_prefix = value.reader().get_byte();
     } else if (key == kConfigurationKeyBroadcastAddress) {
       properties->broadcast_address =
           GetIPv4AddressString(value.reader().get_uint32());
diff --git a/dhcp_config_unittest.cc b/dhcp_config_unittest.cc
index aa853b1..db6ca29 100644
--- a/dhcp_config_unittest.cc
+++ b/dhcp_config_unittest.cc
@@ -133,7 +133,7 @@
   IPConfig::Properties properties;
   ASSERT_TRUE(config_->ParseConfiguration(conf, &properties));
   EXPECT_EQ("4.3.2.1", properties.address);
-  EXPECT_EQ(16, properties.subnet_cidr);
+  EXPECT_EQ(16, properties.subnet_prefix);
   EXPECT_EQ("64.48.32.16", properties.broadcast_address);
   EXPECT_EQ("8.6.4.2", properties.gateway);
   ASSERT_EQ(2, properties.dns_servers.size());
diff --git a/ip_address.cc b/ip_address.cc
index a6ae127..1e36d55 100644
--- a/ip_address.cc
+++ b/ip_address.cc
@@ -62,16 +62,21 @@
 }
 
 // static
+size_t IPAddress::GetMaxPrefixLength(Family family) {
+  return GetAddressLength(family) * 8;
+}
+
+// static
 size_t IPAddress::GetPrefixLengthFromMask(Family family, const string &mask) {
   switch (family) {
     case kFamilyIPv4: {
       in_addr_t mask_val = inet_network(mask.c_str());
-      int subnet_cidr = 0;
+      int subnet_prefix = 0;
       while (mask_val) {
-        subnet_cidr++;
+        subnet_prefix++;
         mask_val <<= 1;
       }
-      return subnet_cidr;
+      return subnet_prefix;
     }
     case kFamilyIPv6:
       NOTIMPLEMENTED();
diff --git a/ip_address.h b/ip_address.h
index df872c7..d69f896 100644
--- a/ip_address.h
+++ b/ip_address.h
@@ -42,6 +42,10 @@
   // Get the length in bytes of addresses of the given family
   static size_t GetAddressLength(Family family);
 
+  // Returns the maximum prefix length for address family |family|, i.e.,
+  // the length of this address type in bits.
+  static size_t GetMaxPrefixLength(Family family);
+
   // Returns the prefix length given an address |family| and a |mask|. For
   // example, returns 24 for an IPv4 mask 255.255.255.0.
   static size_t GetPrefixLengthFromMask(Family family, const std::string &mask);
diff --git a/ipconfig.cc b/ipconfig.cc
index ed28ba1..489f326 100644
--- a/ipconfig.cc
+++ b/ipconfig.cc
@@ -58,7 +58,8 @@
                          &properties_.dns_servers);
   store_.RegisterString(flimflam::kPeerAddressProperty,
                         &properties_.peer_address);
-  store_.RegisterInt32(flimflam::kPrefixlenProperty, &properties_.subnet_cidr);
+  store_.RegisterInt32(flimflam::kPrefixlenProperty,
+                       &properties_.subnet_prefix);
   // TODO(cmasone): Does anyone use this?
   // store_.RegisterStrings(flimflam::kSearchDomainsProperty,
   //                        &properties_.domain_search);
diff --git a/ipconfig.h b/ipconfig.h
index e0f8340..4d341e4 100644
--- a/ipconfig.h
+++ b/ipconfig.h
@@ -37,12 +37,12 @@
 
   struct Properties {
     Properties() : address_family(IPAddress::kFamilyUnknown),
-                   subnet_cidr(0),
+                   subnet_prefix(0),
                    mtu(0) {}
 
     IPAddress::Family address_family;
     std::string address;
-    int32 subnet_cidr;
+    int32 subnet_prefix;
     std::string broadcast_address;
     std::vector<std::string> dns_servers;
     std::string domain_name;
diff --git a/ipconfig_unittest.cc b/ipconfig_unittest.cc
index 832285e..16051cb 100644
--- a/ipconfig_unittest.cc
+++ b/ipconfig_unittest.cc
@@ -71,7 +71,7 @@
 TEST_F(IPConfigTest, UpdateProperties) {
   IPConfig::Properties properties;
   properties.address = "1.2.3.4";
-  properties.subnet_cidr = 24;
+  properties.subnet_prefix = 24;
   properties.broadcast_address = "11.22.33.44";
   properties.gateway = "5.6.7.8";
   properties.dns_servers.push_back("10.20.30.40");
@@ -82,7 +82,7 @@
   properties.mtu = 700;
   ipconfig_->UpdateProperties(properties, true);
   EXPECT_EQ("1.2.3.4", ipconfig_->properties().address);
-  EXPECT_EQ(24, ipconfig_->properties().subnet_cidr);
+  EXPECT_EQ(24, ipconfig_->properties().subnet_prefix);
   EXPECT_EQ("11.22.33.44", ipconfig_->properties().broadcast_address);
   EXPECT_EQ("5.6.7.8", ipconfig_->properties().gateway);
   ASSERT_EQ(2, ipconfig_->properties().dns_servers.size());
diff --git a/mock_rtnl_handler.h b/mock_rtnl_handler.h
index f630b75..a5b14c4 100644
--- a/mock_rtnl_handler.h
+++ b/mock_rtnl_handler.h
@@ -25,9 +25,10 @@
   MOCK_METHOD3(SetInterfaceFlags, void(int interface_index,
                                        unsigned int flags,
                                        unsigned int change));
-  MOCK_METHOD3(AddInterfaceAddress, bool(int interface_index,
+  MOCK_METHOD4(AddInterfaceAddress, bool(int interface_index,
                                          const IPAddress &local,
-                                         const IPAddress &broadcast));
+                                         const IPAddress &broadcast,
+                                         const IPAddress &peer));
   MOCK_METHOD2(RemoveInterfaceAddress, bool(int interface_index,
                                             const IPAddress &local));
   MOCK_METHOD1(RequestDump, void(int request_flags));
diff --git a/openvpn_driver.cc b/openvpn_driver.cc
index 6bcddde..990c74a 100644
--- a/openvpn_driver.cc
+++ b/openvpn_driver.cc
@@ -183,6 +183,8 @@
   ForeignOptions foreign_options;
   RouteOptions routes;
   properties->address_family = IPAddress::kFamilyIPv4;
+  properties->subnet_prefix = IPAddress::GetMaxPrefixLength(
+      properties->address_family);
   for (map<string, string>::const_iterator it = configuration.begin();
        it != configuration.end(); ++it) {
     const string &key = it->first;
@@ -193,7 +195,7 @@
     } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
       properties->broadcast_address = value;
     } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
-      properties->subnet_cidr =
+      properties->subnet_prefix =
           IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
     } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
       properties->peer_address = value;
diff --git a/openvpn_driver_unittest.cc b/openvpn_driver_unittest.cc
index 2b087a6..6e39ea6 100644
--- a/openvpn_driver_unittest.cc
+++ b/openvpn_driver_unittest.cc
@@ -289,7 +289,7 @@
   EXPECT_EQ(IPAddress::kFamilyIPv4, props.address_family);
   EXPECT_EQ("4.5.6.7", props.address);
   EXPECT_EQ("1.2.255.255", props.broadcast_address);
-  EXPECT_EQ(24, props.subnet_cidr);
+  EXPECT_EQ(24, props.subnet_prefix);
   EXPECT_EQ("33.44.55.66", props.peer_address);
   EXPECT_EQ("192.168.1.1", props.gateway);
   EXPECT_EQ("99.88.77.66", props.trusted_ip);
diff --git a/rtnl_handler.cc b/rtnl_handler.cc
index 0ab508b..8936c94 100644
--- a/rtnl_handler.cc
+++ b/rtnl_handler.cc
@@ -268,8 +268,10 @@
                                  RTNLMessage::Mode mode,
                                  int flags,
                                  const IPAddress &local,
-                                 const IPAddress &gateway) {
+                                 const IPAddress &gateway,
+                                 const IPAddress &peer) {
   CHECK(local.family() == gateway.family());
+  CHECK(local.family() == peer.family());
 
   RTNLMessage msg(
       RTNLMessage::kTypeAddress,
@@ -285,24 +287,27 @@
       0,
       0));
 
-  // TODO(pstew): This code only works for Ethernet-like setups,
-  //              not with devices that have a peer address like PPP.
   msg.SetAttribute(IFA_LOCAL, local.address());
   if (!gateway.IsDefault()) {
     msg.SetAttribute(IFA_BROADCAST, gateway.address());
   }
+  if (!peer.IsDefault()) {
+    msg.SetAttribute(IFA_ADDRESS, peer.address());
+  }
 
   return SendMessage(&msg);
 }
 
 bool RTNLHandler::AddInterfaceAddress(int interface_index,
                                       const IPAddress &local,
-                                      const IPAddress &broadcast) {
+                                      const IPAddress &broadcast,
+                                      const IPAddress &peer) {
     return AddressRequest(interface_index,
                           RTNLMessage::kModeAdd,
                           NLM_F_CREATE | NLM_F_EXCL,
                           local,
-                          broadcast);
+                          broadcast,
+                          peer);
 }
 
 bool RTNLHandler::RemoveInterfaceAddress(int interface_index,
@@ -311,6 +316,7 @@
                         RTNLMessage::kModeDelete,
                         0,
                         local,
+                        IPAddress(local.family()),
                         IPAddress(local.family()));
 }
 
diff --git a/rtnl_handler.h b/rtnl_handler.h
index 235950b..d6fe967 100644
--- a/rtnl_handler.h
+++ b/rtnl_handler.h
@@ -72,7 +72,8 @@
   // 'interface_index'.
   virtual bool AddInterfaceAddress(int interface_index,
                                    const IPAddress &local,
-                                   const IPAddress &gateway);
+                                   const IPAddress &gateway,
+                                   const IPAddress &peer);
 
   // Remove address from a network interface that has a kernel index of
   // 'interface_index'.
@@ -126,7 +127,8 @@
                       RTNLMessage::Mode mode,
                       int flags,
                       const IPAddress &local,
-                      const IPAddress &gateway);
+                      const IPAddress &gateway,
+                      const IPAddress &peer);
   Sockets *sockets_;
   bool in_request_;