You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2014/05/15 03:21:37 UTC

[7/9] git commit: Added API for managing ICMP packet filters.

Added API for managing ICMP packet filters.

Review: https://reviews.apache.org/r/20295


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/6d068b21
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/6d068b21
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/6d068b21

Branch: refs/heads/master
Commit: 6d068b218f50e4de3d2b35d52bb90f66562e34cf
Parents: c3fe024
Author: Jie Yu <yu...@gmail.com>
Authored: Mon Apr 28 09:45:11 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed May 14 17:38:43 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am                   |   2 +
 src/linux/routing/filter/icmp.cpp | 273 +++++++++++++++++++++++++++++++++
 src/linux/routing/filter/icmp.hpp | 125 +++++++++++++++
 src/tests/routing_tests.cpp       | 199 ++++++++++++++++++++++++
 4 files changed, 599 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/6d068b21/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 42acff1..c18ddf0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -270,6 +270,7 @@ if WITH_NETWORK_ISOLATOR
   libmesos_no_3rdparty_la_SOURCES +=					\
 	linux/routing/utils.cpp						\
 	linux/routing/link/link.cpp					\
+	linux/routing/filter/icmp.cpp					\
 	linux/routing/queueing/handle.cpp				\
 	linux/routing/queueing/ingress.cpp
 
@@ -278,6 +279,7 @@ if WITH_NETWORK_ISOLATOR
 	linux/routing/utils.hpp						\
 	linux/routing/filter/action.hpp					\
 	linux/routing/filter/filter.hpp					\
+	linux/routing/filter/icmp.hpp					\
 	linux/routing/filter/internal.hpp				\
 	linux/routing/filter/priority.hpp				\
 	linux/routing/link/internal.hpp					\

http://git-wip-us.apache.org/repos/asf/mesos/blob/6d068b21/src/linux/routing/filter/icmp.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/icmp.cpp b/src/linux/routing/filter/icmp.cpp
new file mode 100644
index 0000000..31a7332
--- /dev/null
+++ b/src/linux/routing/filter/icmp.cpp
@@ -0,0 +1,273 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 <stdint.h>
+
+#include <arpa/inet.h>
+
+#include <linux/if_ether.h>
+
+#include <netlink/errno.h>
+
+#include <netlink/route/tc.h>
+
+#include <netlink/route/cls/u32.h>
+
+#include <stout/error.hpp>
+#include <stout/none.hpp>
+
+#include "linux/routing/internal.hpp"
+
+#include "linux/routing/filter/action.hpp"
+#include "linux/routing/filter/filter.hpp"
+#include "linux/routing/filter/icmp.hpp"
+#include "linux/routing/filter/internal.hpp"
+#include "linux/routing/filter/priority.hpp"
+
+#include "linux/routing/queueing/handle.hpp"
+
+using std::string;
+using std::vector;
+
+namespace routing {
+namespace filter {
+
+/////////////////////////////////////////////////
+// Classifier specific {en}decoding functions.
+/////////////////////////////////////////////////
+
+namespace internal {
+
+// Encodes the ICMP classifier into the libnl filter 'cls'. Each type
+// of classifier needs to implement this function.
+template <>
+Try<Nothing> encode<icmp::Classifier>(
+    const Netlink<struct rtnl_cls>& cls,
+    const icmp::Classifier& classifier)
+{
+  // ICMP packets are one type of IP packets.
+  rtnl_cls_set_protocol(cls.get(), ETH_P_IP);
+
+  int err = rtnl_tc_set_kind(TC_CAST(cls.get()), "u32");
+  if (err != 0) {
+    return Error(
+        "Failed to set the kind of the classifier: " +
+        string(nl_geterror(err)));
+  }
+
+  // To avoid confusion, we only use u32 selectors which are used to
+  // match arbitrary 32-bit content in a packet.
+
+  // Format of an IP packet at offset 8. The IP protocol field is at
+  // offset 9. ICMP has protocol = 1.
+  //        +--------+--------+--------+--------+
+  //        |   X    | Proto. |   X    |   X    |
+  //        +--------+--------+--------+--------+
+  // Offset:    8        9        10       11
+  uint32_t protocol = 0x00010000;
+  uint32_t mask = 0x00ff0000; // Ignore offset 8, 10, 11.
+
+  // To match ICMP packets (protocol = 1).
+  err = rtnl_u32_add_key(
+      cls.get(),
+      htonl(protocol),
+      htonl(mask),
+      8, // Offset from which to start matching.
+      0);
+
+  if (err != 0) {
+    return Error(
+        "Failed to add selector for IP protocol: " +
+        string(nl_geterror(err)));
+  }
+
+  if (classifier.destinationIP().isSome()) {
+    // To match those IP packets that have the given destination IP.
+    err = rtnl_u32_add_key(
+        cls.get(),
+        htonl(classifier.destinationIP().get().address()),
+        htonl(0xffffffff),
+        16, // Offset from which to start matching.
+        0);
+
+    if (err != 0) {
+      return Error(
+          "Failed to add selector for destination IP address: " +
+          string(nl_geterror(err)));
+    }
+  }
+
+  return Nothing();
+}
+
+
+// Decodes the ICMP classifier from the libnl filter 'cls'. Each type
+// of classifier needs to implement this function. Returns None if the
+// libnl filter is not an ICMP packet filter.
+template <>
+Result<icmp::Classifier> decode<icmp::Classifier>(
+    const Netlink<struct rtnl_cls>& cls)
+{
+  if (rtnl_cls_get_protocol(cls.get()) != ETH_P_IP ||
+      rtnl_tc_get_kind(TC_CAST(cls.get())) != string("u32")) {
+    return None();
+  }
+
+  // Raw values.
+  Option<uint32_t> protocol;
+  Option<net::IP> destinationIP;
+
+  // There are at most 0xff keys.
+  for (uint8_t i = 0; i <= 0xff; i++) {
+    uint32_t value;
+    uint32_t mask;
+    int offset;
+    int offsetmask;
+
+    // Decode a selector from the libnl filter 'cls'.
+    int err = rtnl_u32_get_key(
+        cls.get(),
+        i,
+        &value,
+        &mask,
+        &offset,
+        &offsetmask);
+
+    if (err != 0) {
+      if (err == -NLE_INVAL) {
+        // This is the case where cls does not have a u32 selector. In
+        // that case, we just return None.
+        return None();
+      } else if (err == -NLE_RANGE) {
+        break;
+      } else {
+        return Error(
+            "Failed to decode a u32 selector: " +
+            string(nl_geterror(err)));
+      }
+    }
+
+    // The function "rtnl_u32_get_key" sets value and mask in network
+    // order. Convert them back to host order.
+    value = ntohl(value);
+    mask = ntohl(mask);
+
+    // IP protocol field.
+    if (offset == 8 && value == 0x00010000 && mask == 0x00ff0000) {
+      protocol = value;
+    }
+
+    // Destination IP address.
+    if (offset == 16 && mask == 0xffffffff) {
+      destinationIP = net::IP(value);
+    }
+  }
+
+  if (protocol.isSome()) {
+    return icmp::Classifier(destinationIP);
+  }
+
+  return None();
+}
+
+} // namespace internal {
+
+/////////////////////////////////////////////////
+// Public interfaces.
+/////////////////////////////////////////////////
+
+namespace icmp {
+
+Try<bool> exists(
+    const string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier)
+{
+  return internal::exists(link, parent, classifier);
+}
+
+
+Try<bool> create(
+    const string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier,
+    const Option<Priority>& priority,
+    const action::Redirect& redirect)
+{
+  return internal::create(
+      link,
+      Filter<Classifier>(
+          parent,
+          classifier,
+          priority,
+          redirect));
+}
+
+
+Try<bool> create(
+    const string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier,
+    const Option<Priority>& priority,
+    const action::Mirror& mirror)
+{
+  return internal::create(
+      link,
+      Filter<Classifier>(
+          parent,
+          classifier,
+          priority,
+          mirror));
+}
+
+
+Try<bool> remove(
+    const string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier)
+{
+  return internal::remove(link, parent, classifier);
+}
+
+
+Try<bool> update(
+    const string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier,
+    const action::Mirror& mirror)
+{
+  return internal::update(
+      link,
+      Filter<Classifier>(
+          parent,
+          classifier,
+          None(),
+          mirror));
+}
+
+
+Result<vector<Classifier> > classifiers(
+    const string& link,
+    const queueing::Handle& parent)
+{
+  return internal::classifiers<Classifier>(link, parent);
+}
+
+} // namespace icmp {
+} // namespace filter {
+} // namespace routing {

http://git-wip-us.apache.org/repos/asf/mesos/blob/6d068b21/src/linux/routing/filter/icmp.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/icmp.hpp b/src/linux/routing/filter/icmp.hpp
new file mode 100644
index 0000000..dc075a7
--- /dev/null
+++ b/src/linux/routing/filter/icmp.hpp
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 __LINUX_ROUTING_FILTER_ICMP_HPP__
+#define __LINUX_ROUTING_FILTER_ICMP_HPP__
+
+#include <string>
+#include <vector>
+
+#include <stout/net.hpp>
+#include <stout/option.hpp>
+#include <stout/result.hpp>
+#include <stout/try.hpp>
+
+#include "linux/routing/filter/action.hpp"
+#include "linux/routing/filter/filter.hpp"
+#include "linux/routing/filter/priority.hpp"
+
+#include "linux/routing/queueing/handle.hpp"
+
+namespace routing {
+namespace filter {
+namespace icmp {
+
+class Classifier
+{
+public:
+  explicit Classifier(const Option<net::IP>& _destinationIP)
+    : destinationIP_(_destinationIP) {}
+
+  bool operator == (const Classifier& that) const
+  {
+    return destinationIP_ == that.destinationIP_;
+  }
+
+  const Option<net::IP>& destinationIP() const { return destinationIP_; }
+
+private:
+  Option<net::IP> destinationIP_;
+};
+
+
+// Returns true if there exists an ICMP packet filter attached to the
+// given parent on the link which matches the specified classifier.
+Try<bool> exists(
+    const std::string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier);
+
+
+// Creates an ICMP packet filter attached to the given parent on the
+// link which will redirect all the ICMP packets that satisfy the
+// conditions specified by the classifier to the target link. Returns
+// false if an ICMP packet filter attached to the given parent with
+// the same classifier already exists. The user can choose to specify
+// an optional priority for the filter.
+Try<bool> create(
+    const std::string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier,
+    const Option<Priority>& priority,
+    const action::Redirect& redirect);
+
+
+// Creates an ICMP packet filter attached to the given parent on the
+// link which will mirror all the ICMP packets that satisfy the
+// conditions specified by the classifier to a set of links (specified
+// in the mirror action). Returns false if an ICMP packet filter
+// attached to the given parent with the same classifier already
+// exists. The user can choose to specify an optional priority for the
+// filter.
+Try<bool> create(
+    const std::string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier,
+    const Option<Priority>& priority,
+    const action::Mirror& mirror);
+
+
+// Removes the ICMP packet filter attached to the given parent that
+// matches the specified classifier from the link. Returns false if
+// such a filter is not found.
+Try<bool> remove(
+    const std::string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier);
+
+
+// Updates the action of the ICMP packet filter attached to the given
+// parent that matches the specified classifier on the link. Returns
+// false if such a filter is not found.
+Try<bool> update(
+    const std::string& link,
+    const queueing::Handle& parent,
+    const Classifier& classifier,
+    const action::Mirror& mirror);
+
+
+// Returns the classifiers of all the ICMP packet filters attached to
+// the given parent on the link. Returns None if the link or the
+// parent is not found.
+Result<std::vector<Classifier> > classifiers(
+    const std::string& link,
+    const queueing::Handle& parent);
+
+} // namespace icmp {
+} // namespace filter {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_FILTER_ICMP_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/6d068b21/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
index 7ba826f..de6d9e3 100644
--- a/src/tests/routing_tests.cpp
+++ b/src/tests/routing_tests.cpp
@@ -33,13 +33,21 @@
 
 #include "linux/routing/utils.hpp"
 
+#include "linux/routing/filter/icmp.hpp"
+
 #include "linux/routing/link/link.hpp"
 
+#include "linux/routing/queueing/handle.hpp"
+#include "linux/routing/queueing/ingress.hpp"
+
 using namespace routing;
+using namespace routing::filter;
+using namespace routing::queueing;
 
 using std::endl;
 using std::set;
 using std::string;
+using std::vector;
 
 
 static const string TEST_VETH_LINK = "veth-test";
@@ -282,3 +290,194 @@ TEST_F(RoutingVethTest, ROOT_LinkMTU)
   EXPECT_NONE(link::mtu("not-exist"));
   EXPECT_SOME_FALSE(link::setMTU("not-exist", 1500));
 }
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip)));
+
+  Result<vector<icmp::Classifier> > classifiers =
+    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(1u, classifiers.get().size());
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP());
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateDuplicated)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Mirror(links)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateMultiple)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip1 = net::IP(0x01020304); // 1.2.3.4
+  net::IP ip2 = net::IP(0x05060708); // 5.6.7.8
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip1),
+      Priority(1, 1),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip2),
+      Priority(1, 2),
+      action::Redirect(TEST_PEER_LINK)));
+
+  Result<vector<icmp::Classifier> > classifiers =
+    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(2u, classifiers.get().size());
+  EXPECT_SOME_EQ(ip1, classifiers.get().front().destinationIP());
+  EXPECT_SOME_EQ(ip2, classifiers.get().back().destinationIP());
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterRemove)
+{
+  ASSERT_SOME(link::create(
+      TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_TRUE(icmp::remove(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterUpdate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_FALSE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip)));
+}