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)));
+}