]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common/pick_address: add {public,cluster}_network_interface option 18131/head
authorSage Weil <sage@redhat.com>
Thu, 28 Sep 2017 21:47:17 +0000 (17:47 -0400)
committerSage Weil <sage@redhat.com>
Thu, 5 Oct 2017 17:04:59 +0000 (12:04 -0500)
Add _interfaces option to constrain the choice of IPs in the network
list to those on interfaces matching the provided list of interface names.
The _interfaces options only work in concert with the _network options,
so you must also specify a list of networks if you want to use a specific
interface, e.g., by specifying a broad network like "::" or "0.0.0.0/0".

Signed-off-by: Sage Weil <sage@redhat.com>
(cherry picked from commit 95cc790acddb597d6fef2d9e444f0b6e0436f16f)

src/common/options.cc
src/common/pick_address.cc
src/common/pick_address.h
src/test/test_ipaddr.cc

index cb4aadb47cdf8ba958c4b812ed93dba65b2714c9..605cbc8d170e9a40c57b4f58c48793e4e3098aff 100644 (file)
@@ -160,12 +160,24 @@ std::vector<Option> get_global_options() {
     Option("public_network", Option::TYPE_STR, Option::LEVEL_ADVANCED)
     .add_service({"mon", "mds", "osd", "mgr"})
     .add_tag("network")
-    .set_description(""),
+    .set_description("Network(s) from which to choose a public address to bind to"),
+
+    Option("public_network_interface", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .add_service({"mon", "mds", "osd", "mgr"})
+    .add_tag("network")
+    .set_description("Interface name(s) from which to choose an address from a public_network to bind to; public_network must also be specified.")
+    .add_see_also("public_network"),
 
     Option("cluster_network", Option::TYPE_STR, Option::LEVEL_ADVANCED)
     .add_service("osd")
     .add_tag("network")
-    .set_description(""),
+    .set_description("Network(s) from which to choose a cluster address to bind to"),
+
+    Option("cluster_network_interface", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .add_service({"mon", "mds", "osd", "mgr"})
+    .add_tag("network")
+    .set_description("Interface name(s) from which to choose an address from a cluster_network to bind to; cluster_network must also be specified.")
+    .add_see_also("cluster_network"),
 
     Option("monmap", Option::TYPE_STR, Option::LEVEL_ADVANCED)
     .set_description("path to MonMap file")
index dfea843e9cb13c34dcb63cd17f84664d66e3c0d2..4bda7ba1bff2390dba7fb77d8b65dffdf0ba8cd6 100644 (file)
 
 #define dout_subsys ceph_subsys_
 
-static const struct sockaddr *find_ip_in_subnet_list(CephContext *cct,
-                                                    const struct ifaddrs *ifa,
-                                                    const std::string &networks)
+const struct sockaddr *find_ip_in_subnet_list(
+  CephContext *cct,
+  const struct ifaddrs *ifa,
+  const std::string &networks,
+  const std::string &interfaces)
 {
   std::list<string> nets;
   get_str_list(networks, nets);
+  std::list<string> ifs;
+  get_str_list(interfaces, ifs);
+
+  // filter interfaces by name
+  const struct ifaddrs *filtered = 0;
+  if (ifs.empty()) {
+    filtered = ifa;
+  } else {
+    if (nets.empty()) {
+      lderr(cct) << "interface names specified but not network names" << dendl;
+      exit(1);
+    }
+    const struct ifaddrs *t = ifa;
+    struct ifaddrs *head = 0;
+    while (t != NULL) {
+      bool match = false;
+      for (auto& i : ifs) {
+       if (strcmp(i.c_str(), t->ifa_name) == 0) {
+         match = true;
+         break;
+       }
+      }
+      if (match) {
+       struct ifaddrs *n = new ifaddrs;
+       memcpy(n, t, sizeof(*t));
+       n->ifa_next = head;
+       head = n;
+      }
+      t = t->ifa_next;
+    }
+    if (head == NULL) {
+      lderr(cct) << "no interfaces matching " << ifs << dendl;
+      exit(1);
+    }
+    filtered = head;
+  }
 
-  for(std::list<string>::iterator s = nets.begin(); s != nets.end(); ++s) {
-      struct sockaddr_storage net;
-      unsigned int prefix_len;
+  struct sockaddr *r = NULL;
+  for (std::list<string>::iterator s = nets.begin(); s != nets.end(); ++s) {
+    struct sockaddr_storage net;
+    unsigned int prefix_len;
 
-      if (!parse_network(s->c_str(), &net, &prefix_len)) {
-       lderr(cct) << "unable to parse network: " << *s << dendl;
-       exit(1);
-      }
+    if (!parse_network(s->c_str(), &net, &prefix_len)) {
+      lderr(cct) << "unable to parse network: " << *s << dendl;
+      exit(1);
+    }
+
+    const struct ifaddrs *found = find_ip_in_subnet(
+      filtered,
+      (struct sockaddr *) &net, prefix_len);
+    if (found) {
+      r = found->ifa_addr;
+      break;
+    }
+  }
 
-      const struct ifaddrs *found = find_ip_in_subnet(ifa,
-                                      (struct sockaddr *) &net, prefix_len);
-      if (found)
-       return found->ifa_addr;
+  if (filtered != ifa) {
+    while (filtered) {
+      struct ifaddrs *t = filtered->ifa_next;
+      delete filtered;
+      filtered = t;
     }
+  }
 
-  return NULL;
+  return r;
 }
 
 // observe this change
@@ -67,11 +117,14 @@ struct Observer : public md_config_obs_t {
 static void fill_in_one_address(CephContext *cct,
                                const struct ifaddrs *ifa,
                                const string networks,
+                               const string interfaces,
                                const char *conf_var)
 {
-  const struct sockaddr *found = find_ip_in_subnet_list(cct, ifa, networks);
+  const struct sockaddr *found = find_ip_in_subnet_list(cct, ifa, networks,
+                                                       interfaces);
   if (!found) {
-    lderr(cct) << "unable to find any IP address in networks: " << networks << dendl;
+    lderr(cct) << "unable to find any IP address in networks '" << networks
+              << "' interfaces '" << interfaces << "'" << dendl;
     exit(1);
   }
 
@@ -111,22 +164,29 @@ void pick_addresses(CephContext *cct, int needs)
     exit(1);
   }
 
-
   if ((needs & CEPH_PICK_ADDRESS_PUBLIC)
       && cct->_conf->public_addr.is_blank_ip()
       && !cct->_conf->public_network.empty()) {
-    fill_in_one_address(cct, ifa, cct->_conf->public_network, "public_addr");
+    fill_in_one_address(cct, ifa, cct->_conf->public_network,
+                       cct->_conf->get_val<string>("public_network_interface"),
+                       "public_addr");
   }
 
   if ((needs & CEPH_PICK_ADDRESS_CLUSTER)
       && cct->_conf->cluster_addr.is_blank_ip()) {
     if (!cct->_conf->cluster_network.empty()) {
-      fill_in_one_address(cct, ifa, cct->_conf->cluster_network, "cluster_addr");
+      fill_in_one_address(
+       cct, ifa, cct->_conf->cluster_network,
+       cct->_conf->get_val<string>("cluster_network_interface"),
+       "cluster_addr");
     } else {
       if (!cct->_conf->public_network.empty()) {
         lderr(cct) << "Public network was set, but cluster network was not set " << dendl;
         lderr(cct) << "    Using public network also for cluster network" << dendl;
-        fill_in_one_address(cct, ifa, cct->_conf->public_network, "cluster_addr");
+        fill_in_one_address(
+         cct, ifa, cct->_conf->public_network,
+         cct->_conf->get_val<string>("public_network_interface"),
+         "cluster_addr");
       }
     }
   }
index c7c813d640a1b258543fbc42fdfdb937de8daeaf..73020602b7eef303bd1b5c6a2dbe020877d2fe54 100644 (file)
@@ -47,4 +47,11 @@ std::string pick_iface(CephContext *cct, const struct sockaddr_storage &network)
  */
 bool have_local_addr(CephContext *cct, const list<entity_addr_t>& ls, entity_addr_t *match);
 
+
+const struct sockaddr *find_ip_in_subnet_list(
+  CephContext *cct,
+  const struct ifaddrs *ifa,
+  const std::string &networks,
+  const std::string &interfaces);
+
 #endif
index 6f5a42f8fabc30f542d9f690f08415f22c2e26b9..8cd0281af157f611dee9d8000da329e0a4de1fb6 100644 (file)
@@ -1,4 +1,6 @@
 #include "include/ipaddr.h"
+#include "common/pick_address.h"
+#include "global/global_context.h"
 #include "gtest/gtest.h"
 
 #if defined(__FreeBSD__)
@@ -537,3 +539,52 @@ TEST(CommonIPAddr, ParseNetwork_IPv6_9000)
   ipv6(&want, "2001:1234:5678:90ab::dead:beef");
   ASSERT_EQ(0, memcmp(want.sin6_addr.s6_addr, network.sin6_addr.s6_addr, sizeof(network.sin6_addr.s6_addr)));
 }
+
+TEST(pick_address, find_ip_in_subnet_list)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in a_one;
+  struct sockaddr_in a_two;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+  one.ifa_name = eth0;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+  two.ifa_name = eth1;
+
+  ipv4(&a_one, "10.1.1.2");
+  ipv4(&a_two, "10.2.1.123");
+
+  // match by network
+  result = find_ip_in_subnet_list(
+    g_ceph_context,
+    &one,
+    "10.1.0.0/16",
+    "eth0");
+  ASSERT_EQ((struct sockaddr*)&a_one, result);
+
+  result = find_ip_in_subnet_list(
+    g_ceph_context,
+    &one,
+    "10.2.0.0/16",
+    "eth1");
+  ASSERT_EQ((struct sockaddr*)&a_two, result);
+
+  // match by eth name
+  result = find_ip_in_subnet_list(
+    g_ceph_context,
+    &one,
+    "10.0.0.0/8",
+    "eth0");
+  ASSERT_EQ((struct sockaddr*)&a_one, result);
+
+  result = find_ip_in_subnet_list(
+    g_ceph_context,
+    &one,
+    "10.0.0.0/8",
+    "eth1");
+  ASSERT_EQ((struct sockaddr*)&a_two, result);
+}