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:31 UTC
[1/9] git commit: Allowed waiting for a link to be removed.
Repository: mesos
Updated Branches:
refs/heads/master 6987a9e05 -> 4f97402d7
Allowed waiting for a link to be removed.
Review: https://reviews.apache.org/r/21141
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4f97402d
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4f97402d
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4f97402d
Branch: refs/heads/master
Commit: 4f97402d70f0f0936396cf5443ce545cf8e1c958
Parents: 19f643a
Author: Jie Yu <yu...@gmail.com>
Authored: Tue May 6 17:14:25 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed May 14 17:38:43 2014 -0700
----------------------------------------------------------------------
src/linux/routing/link/link.cpp | 70 ++++++++++++++++++++++++++++++++++++
src/linux/routing/link/link.hpp | 9 +++++
src/tests/routing_tests.cpp | 22 ++++++++++++
3 files changed, 101 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/4f97402d/src/linux/routing/link/link.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/link/link.cpp b/src/linux/routing/link/link.cpp
index 62996bd..039e308 100644
--- a/src/linux/routing/link/link.cpp
+++ b/src/linux/routing/link/link.cpp
@@ -32,7 +32,13 @@
#include <netlink/route/link/veth.h>
+#include <process/delay.hpp>
+#include <process/pid.hpp>
+#include <process/process.hpp>
+
#include <stout/error.hpp>
+#include <stout/duration.hpp>
+#include <stout/lambda.hpp>
#include <stout/none.hpp>
#include <stout/os.hpp>
@@ -41,6 +47,8 @@
#include "linux/routing/link/internal.hpp"
#include "linux/routing/link/link.hpp"
+using namespace process;
+
using std::string;
namespace routing {
@@ -109,6 +117,68 @@ Try<bool> remove(const string& _link)
}
+namespace internal {
+
+// A process that checks if a link has been removed.
+class ExistenceChecker : public Process<ExistenceChecker>
+{
+public:
+ ExistenceChecker(const string& _link) : link(_link) {}
+
+ virtual ~ExistenceChecker() {}
+
+ // Returns a future which gets set when the link has been removed.
+ Future<Nothing> future() { return promise.future(); }
+
+protected:
+ virtual void initialize()
+ {
+ // Stop when no one cares.
+ promise.future().onDiscard(lambda::bind(
+ static_cast<void (*)(const UPID&, bool)>(terminate), self(), true));
+
+ check();
+ }
+
+ virtual void finalize()
+ {
+ promise.discard();
+ }
+
+private:
+ void check()
+ {
+ Try<bool> exists = link::exists(link);
+ if (exists.isError()) {
+ promise.fail(exists.error());
+ terminate(self());
+ return;
+ } else if (!exists.get()) {
+ promise.set(Nothing());
+ terminate(self());
+ return;
+ }
+
+ // Perform the check again.
+ delay(Milliseconds(100), self(), &Self::check);
+ }
+
+ const string link;
+ Promise<Nothing> promise;
+};
+
+} // namespace internal {
+
+
+Future<Nothing> removed(const string& link)
+{
+ internal::ExistenceChecker* checker = new internal::ExistenceChecker(link);
+ Future<Nothing> future = checker->future();
+ spawn(checker, true);
+ return future;
+}
+
+
Result<int> index(const string& _link)
{
Result<Netlink<struct rtnl_link> > link = internal::get(_link);
http://git-wip-us.apache.org/repos/asf/mesos/blob/4f97402d/src/linux/routing/link/link.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/link/link.hpp b/src/linux/routing/link/link.hpp
index ef982e3..4264a1e 100644
--- a/src/linux/routing/link/link.hpp
+++ b/src/linux/routing/link/link.hpp
@@ -25,8 +25,11 @@
#include <string>
+#include <process/future.hpp>
+
#include <stout/hashmap.hpp>
#include <stout/net.hpp>
+#include <stout/nothing.hpp>
#include <stout/option.hpp>
#include <stout/result.hpp>
#include <stout/try.hpp>
@@ -53,6 +56,12 @@ Try<bool> create(
Try<bool> remove(const std::string& link);
+// Waits for the link to be removed. The returned future will be set
+// once the link has been removed. The user can discard the returned
+// future to cancel the operation.
+process::Future<Nothing> removed(const std::string& link);
+
+
// Returns the interface index of the link. Returns None if the link
// is not found.
Result<int> index(const std::string& link);
http://git-wip-us.apache.org/repos/asf/mesos/blob/4f97402d/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
index aca88dc..1658f9a 100644
--- a/src/tests/routing_tests.cpp
+++ b/src/tests/routing_tests.cpp
@@ -26,6 +26,9 @@
#include <gtest/gtest.h>
+#include <process/clock.hpp>
+#include <process/gtest.hpp>
+
#include <stout/foreach.hpp>
#include <stout/gtest.hpp>
#include <stout/hashmap.hpp>
@@ -43,6 +46,8 @@
#include "linux/routing/queueing/handle.hpp"
#include "linux/routing/queueing/ingress.hpp"
+using namespace process;
+
using namespace routing;
using namespace routing::filter;
using namespace routing::queueing;
@@ -270,6 +275,23 @@ TEST_F(RoutingVethTest, ROOT_LinkCreatePid)
#endif
+TEST_F(RoutingVethTest, ROOT_LinkWait)
+{
+ AWAIT_READY(link::removed(TEST_VETH_LINK));
+
+ 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));
+
+ Future<Nothing> removed = link::removed(TEST_VETH_LINK);
+ EXPECT_TRUE(removed.isPending());
+
+ ASSERT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+ AWAIT_READY(removed);
+}
+
+
TEST_F(RoutingVethTest, ROOT_LinkSetUp)
{
ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
[5/9] git commit: Added internal filter and queueing APIs.
Posted by ji...@apache.org.
Added internal filter and queueing APIs.
Review: https://reviews.apache.org/r/20781
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/c3fe0246
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/c3fe0246
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/c3fe0246
Branch: refs/heads/master
Commit: c3fe02466fe733c3b7fd99458ef637bf348eac10
Parents: b99402f
Author: Jie Yu <yu...@gmail.com>
Authored: Mon Apr 28 09:44:07 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed May 14 17:38:43 2014 -0700
----------------------------------------------------------------------
src/Makefile.am | 13 +-
src/linux/routing/filter/action.hpp | 76 ++++
src/linux/routing/filter/filter.hpp | 108 +++++
src/linux/routing/filter/internal.hpp | 649 +++++++++++++++++++++++++++
src/linux/routing/filter/priority.hpp | 66 +++
src/linux/routing/queueing/handle.cpp | 30 ++
src/linux/routing/queueing/handle.hpp | 74 +++
src/linux/routing/queueing/ingress.cpp | 126 ++++++
src/linux/routing/queueing/ingress.hpp | 53 +++
src/linux/routing/queueing/internal.hpp | 272 +++++++++++
10 files changed, 1465 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index debc77a..42acff1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -269,13 +269,22 @@ endif
if WITH_NETWORK_ISOLATOR
libmesos_no_3rdparty_la_SOURCES += \
linux/routing/utils.cpp \
- linux/routing/link/link.cpp
+ linux/routing/link/link.cpp \
+ linux/routing/queueing/handle.cpp \
+ linux/routing/queueing/ingress.cpp
libmesos_no_3rdparty_la_SOURCES += \
linux/routing/internal.hpp \
linux/routing/utils.hpp \
+ linux/routing/filter/action.hpp \
+ linux/routing/filter/filter.hpp \
+ linux/routing/filter/internal.hpp \
+ linux/routing/filter/priority.hpp \
linux/routing/link/internal.hpp \
- linux/routing/link/link.hpp
+ linux/routing/link/link.hpp \
+ linux/routing/queueing/handle.hpp \
+ linux/routing/queueing/ingress.hpp \
+ linux/routing/queueing/internal.hpp
endif
libmesos_no_3rdparty_la_SOURCES += common/attributes.hpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/filter/action.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/action.hpp b/src/linux/routing/filter/action.hpp
new file mode 100644
index 0000000..8cc6594
--- /dev/null
+++ b/src/linux/routing/filter/action.hpp
@@ -0,0 +1,76 @@
+/**
+ * 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_ACTION_HPP__
+#define __LINUX_ROUTING_FILTER_ACTION_HPP__
+
+#include <set>
+#include <string>
+
+namespace routing {
+namespace action {
+
+// Base class for filter actions.
+class Action
+{
+public:
+ virtual ~Action() {}
+
+protected:
+ // Hide the default constructor.
+ Action() {}
+};
+
+
+// Represents an action that redirects a packet to a given link.
+// Currently, kernel only supports redirecting to the egress of a
+// given link.
+class Redirect : public Action
+{
+public:
+ explicit Redirect(const std::string& _link)
+ : link_(_link) {}
+
+ const std::string& link() const { return link_; }
+
+private:
+ // The link to which the packet will be redirected.
+ std::string link_;
+};
+
+
+// Represents an action that mirrors a packet to a set of links.
+// Currently, kernel only supports mirroring to the egress of each
+// link.
+class Mirror : public Action
+{
+public:
+ explicit Mirror(const std::set<std::string>& _links)
+ : links_(_links) {}
+
+ const std::set<std::string>& links() const { return links_; }
+
+private:
+ // The set of links to which the packet will be mirrored.
+ std::set<std::string> links_;
+};
+
+} // namespace action {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_FILTER_ACTION_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/filter/filter.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/filter.hpp b/src/linux/routing/filter/filter.hpp
new file mode 100644
index 0000000..40e21db
--- /dev/null
+++ b/src/linux/routing/filter/filter.hpp
@@ -0,0 +1,108 @@
+/**
+ * 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_FILTER_HPP__
+#define __LINUX_ROUTING_FILTER_FILTER_HPP__
+
+#include <vector>
+
+#include <process/shared.hpp>
+
+#include <stout/option.hpp>
+
+#include "linux/routing/filter/action.hpp"
+#include "linux/routing/filter/priority.hpp"
+
+#include "linux/routing/queueing/handle.hpp"
+
+namespace routing {
+namespace filter {
+
+// Our representation of a filter. Each filter is attached to a
+// 'parent' (either a queueing discipline or queueing class), and
+// contains a 'classifier' which defines how packets will be matched,
+// a 'priority' which defines the order in which filters will be
+// applied, and a series of 'actions' that will be taken when a packet
+// satisfies the conditions specified in the classifier. If the
+// priority is not specified, the kernel will assign a priority to the
+// filter.
+// TODO(jieyu): Currently, this data structure is not directly exposed
+// to the user because libnl does not support getting actions of a
+// filter. Expose this data structure once libnl fixes the issue.
+template <typename Classifier>
+class Filter
+{
+public:
+ // Creates a filter with no action.
+ Filter(const queueing::Handle& _parent,
+ const Classifier& _classifier,
+ const Option<Priority>& _priority)
+ : parent_(_parent),
+ classifier_(_classifier),
+ priority_(_priority) {}
+
+ // TODO(jieyu): Support arbitrary number of actions.
+ template <typename Action>
+ Filter(const queueing::Handle& _parent,
+ const Classifier& _classifier,
+ const Option<Priority>& _priority,
+ const Action& action)
+ : parent_(_parent),
+ classifier_(_classifier),
+ priority_(_priority)
+ {
+ attach(action);
+ }
+
+ // Attaches an action to this filter.
+ template <typename A>
+ void attach(const A& action)
+ {
+ actions_.push_back(process::Shared<action::Action>(new A(action)));
+ }
+
+ const queueing::Handle parent() const { return parent_; }
+ const Classifier& classifier() const { return classifier_; }
+ const Option<Priority>& priority() const { return priority_; }
+
+ // Returns all the actions attached to this filter.
+ const std::vector<process::Shared<action::Action> >& actions() const
+ {
+ return actions_;
+ }
+
+private:
+ // Each filter is attached to a queueing object (either a queueing
+ // discipline or a queueing class).
+ queueing::Handle parent_;
+
+ // The filter specific classifier.
+ Classifier classifier_;
+
+ // The priority of this filter.
+ Option<Priority> priority_;
+
+ // The set of actions attached to this filer. Note that we use
+ // Shared here to make Filter copyable.
+ std::vector<process::Shared<action::Action> > actions_;
+};
+
+} // namespace filter {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_FILTER_FILTER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/filter/internal.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/internal.hpp b/src/linux/routing/filter/internal.hpp
new file mode 100644
index 0000000..cd3279a
--- /dev/null
+++ b/src/linux/routing/filter/internal.hpp
@@ -0,0 +1,649 @@
+/**
+ * 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_INTERNAL_HPP__
+#define __LINUX_ROUTING_FILTER_INTERNAL_HPP__
+
+#include <stdint.h>
+
+#include <netlink/cache.h>
+#include <netlink/errno.h>
+#include <netlink/object.h>
+#include <netlink/socket.h>
+
+#include <netlink/route/classifier.h>
+#include <netlink/route/link.h>
+#include <netlink/route/tc.h>
+
+#include <netlink/route/act/mirred.h>
+
+#include <netlink/route/cls/basic.h>
+#include <netlink/route/cls/u32.h>
+
+#include <string>
+#include <vector>
+
+#include <process/shared.hpp>
+
+#include <stout/error.hpp>
+#include <stout/foreach.hpp>
+#include <stout/none.hpp>
+#include <stout/nothing.hpp>
+#include <stout/option.hpp>
+#include <stout/result.hpp>
+#include <stout/try.hpp>
+
+#include "linux/routing/internal.hpp"
+
+#include "linux/routing/filter/action.hpp"
+#include "linux/routing/filter/filter.hpp"
+#include "linux/routing/filter/priority.hpp"
+
+#include "linux/routing/link/internal.hpp"
+
+#include "linux/routing/queueing/handle.hpp"
+
+namespace routing {
+namespace filter {
+namespace internal {
+
+/////////////////////////////////////////////////
+// Helpers for {en}decoding.
+/////////////////////////////////////////////////
+
+// Forward declaration. Each type of classifier needs to implement
+// this function to encode itself into the libnl filter (rtnl_cls).
+template <typename Classifier>
+Try<Nothing> encode(
+ const Netlink<struct rtnl_cls>& cls,
+ const Classifier& classifier);
+
+
+// Forward declaration. Each type of classifier needs to implement
+// this function to decode itself from the libnl filter (rtnl_cls).
+// Returns None if the libnl filter does not match the type of the
+// classifier.
+template <typename Classifier>
+Result<Classifier> decode(const Netlink<struct rtnl_cls>& cls);
+
+
+// Attaches a redirect action to the libnl filter (rtnl_cls).
+inline Try<Nothing> attach(
+ const Netlink<struct rtnl_cls>& cls,
+ const action::Redirect& redirect)
+{
+ Result<Netlink<struct rtnl_link> > link =
+ link::internal::get(redirect.link());
+
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return Error("Link '" + redirect.link() + "' is not found");
+ }
+
+ // TODO(jieyu): Note that currently, we don't use Netlink for 'act'
+ // because libnl has a refcount issue for rtnl_act. Clean this up
+ // once the bug is fixed in libnl.
+ struct rtnl_act* act = rtnl_act_alloc();
+ if (act == NULL) {
+ return Error("Failed to allocate a libnl action (rtnl_act)");
+ }
+
+ // Set the kind of the action to 'mirred'. The kind 'mirred' stands
+ // for mirror or redirect actions.
+ int err = rtnl_tc_set_kind(TC_CAST(act), "mirred");
+ if (err != 0) {
+ rtnl_act_put(act);
+ return Error(
+ "Failed to set the kind of the action: " +
+ std::string(nl_geterror(err)));
+ }
+
+ rtnl_mirred_set_ifindex(act, rtnl_link_get_ifindex(link.get().get()));
+ rtnl_mirred_set_action(act, TCA_EGRESS_REDIR);
+ rtnl_mirred_set_policy(act, TC_ACT_STOLEN);
+
+ const std::string kind = rtnl_tc_get_kind(TC_CAST(cls.get()));
+ if (kind == "basic") {
+ err = rtnl_basic_add_action(cls.get(), act);
+ if (err != 0) {
+ rtnl_act_put(act);
+ return Error(std::string(nl_geterror(err)));
+ }
+ } else if (kind == "u32") {
+ err = rtnl_u32_add_action(cls.get(), act);
+ if (err != 0) {
+ rtnl_act_put(act);
+ return Error(std::string(nl_geterror(err)));
+ }
+ } else {
+ rtnl_act_put(act);
+ return Error("Unsupported classifier kind: " + kind);
+ }
+
+ return Nothing();
+}
+
+
+// Attaches a mirror action to the libnl filter (rtnl_cls).
+inline Try<Nothing> attach(
+ const Netlink<struct rtnl_cls>& cls,
+ const action::Mirror& mirror)
+{
+ foreach (const std::string& _link, mirror.links()) {
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return Error("Link '" + _link + "' is not found");
+ }
+
+ // TODO(jieyu): Note that currently, we don't use Netlink for
+ // 'act' because libnl has a refcount issue for rtnl_act. Clean
+ // this up once libnl fixes the bug.
+ struct rtnl_act* act = rtnl_act_alloc();
+ if (act == NULL) {
+ return Error("Failed to allocate a libnl action (rtnl_act)");
+ }
+
+ int err = rtnl_tc_set_kind(TC_CAST(act), "mirred");
+ if (err != 0) {
+ rtnl_act_put(act);
+ return Error(
+ "Failed to set the kind of the action: " +
+ std::string(nl_geterror(err)));
+ }
+
+ rtnl_mirred_set_ifindex(act, rtnl_link_get_ifindex(link.get().get()));
+ rtnl_mirred_set_action(act, TCA_EGRESS_MIRROR);
+ rtnl_mirred_set_policy(act, TC_ACT_PIPE);
+
+ const std::string kind = rtnl_tc_get_kind(TC_CAST(cls.get()));
+ if (kind == "basic") {
+ err = rtnl_basic_add_action(cls.get(), act);
+ if (err != 0) {
+ rtnl_act_put(act);
+ return Error(std::string(nl_geterror(err)));
+ }
+ } else if (kind == "u32") {
+ err = rtnl_u32_add_action(cls.get(), act);
+ if (err != 0) {
+ rtnl_act_put(act);
+ return Error(std::string(nl_geterror(err)));
+ }
+ } else {
+ rtnl_act_put(act);
+ return Error("Unsupported classifier kind: " + kind);
+ }
+ }
+
+ return Nothing();
+}
+
+
+// Attaches an action to the libnl filter (rtnl_cls). This function
+// essentially delegates the call to the corresponding attach function
+// depending on the type of the action.
+inline Try<Nothing> attach(
+ const Netlink<struct rtnl_cls>& cls,
+ const process::Shared<action::Action>& action)
+{
+ const action::Redirect* redirect =
+ dynamic_cast<const action::Redirect*>(action.get());
+ if (redirect != NULL) {
+ return attach(cls, *redirect);
+ }
+
+ const action::Mirror* mirror =
+ dynamic_cast<const action::Mirror*>(action.get());
+ if (mirror != NULL) {
+ return attach(cls, *mirror);
+ }
+
+ return Error("Unsupported action type");
+}
+
+
+// Encodes a filter (in our representation) to a libnl filter
+// (rtnl_cls). We use template here so that it works for any type of
+// classifier.
+template <typename Classifier>
+Try<Netlink<struct rtnl_cls> > encodeFilter(
+ const Netlink<struct rtnl_link>& link,
+ const Filter<Classifier>& filter)
+{
+ struct rtnl_cls* c = rtnl_cls_alloc();
+ if (c == NULL) {
+ return Error("Failed to allocate a libnl filter (rtnl_cls)");
+ }
+
+ Netlink<struct rtnl_cls> cls(c);
+
+ rtnl_tc_set_link(TC_CAST(cls.get()), link.get());
+ rtnl_tc_set_parent(TC_CAST(cls.get()), filter.parent().get());
+
+ // Encode the priority.
+ if (filter.priority().isSome()) {
+ rtnl_cls_set_prio(cls.get(), filter.priority().get().get());
+ }
+
+ // Encode the classifier using the classifier specific function.
+ Try<Nothing> encoding = encode(cls, filter.classifier());
+ if (encoding.isError()) {
+ return Error("Failed to encode the classifier " + encoding.error());
+ }
+
+ // Stop the packet from being passed to the next filter if a match
+ // is found. This is only applied to u32 filters (which can match
+ // any 32-bit value in a packet), and is not needed for other types
+ // fo fitlers.
+ // TODO(jieyu): Consider setting flowid.
+ if (rtnl_tc_get_kind(TC_CAST(cls.get())) == std::string("u32")) {
+ int err = rtnl_u32_set_cls_terminal(cls.get());
+ if (err != 0) {
+ return Error(
+ "Failed to mark the libnl filter as a terminal" +
+ std::string(nl_geterror(err)));
+ }
+ }
+
+ // Attach actions to the libnl filter.
+ foreach (const process::Shared<action::Action>& action, filter.actions()) {
+ Try<Nothing> attaching = attach(cls, action);
+ if (attaching.isError()) {
+ return Error("Failed to attach an action " + attaching.error());
+ }
+ }
+
+ return cls;
+}
+
+
+// Decodes a libnl filter (rtnl_cls) to our filter representation.
+// Returns None if the libnl filter does not match the specified
+// classifier type. We use template here so that it works for any type
+// of classifier.
+template <typename Classifier>
+Result<Filter<Classifier> > decodeFilter(const Netlink<struct rtnl_cls>& cls)
+{
+ // If the handle of the libnl filer is 0, it means that it is an
+ // internal filter, therefore is definitly not created by us.
+ if (rtnl_tc_get_handle(TC_CAST(cls.get())) == 0) {
+ return None();
+ }
+
+ // Decode the parent.
+ queueing::Handle parent(rtnl_tc_get_parent(TC_CAST(cls.get())));
+
+ // Decode the priority. If the priority is not specified by the
+ // user, kernel will assign a priority to the filter. So we should
+ // always have a valid priority here.
+ Priority priority(rtnl_cls_get_prio(cls.get()));
+
+ // Decode the classifier using a classifier specific function.
+ Result<Classifier> classifier = decode<Classifier>(cls);
+ if (classifier.isError()) {
+ return Error("Failed to decode the classifier: " + classifier.error());
+ } else if (classifier.isNone()) {
+ return None();
+ }
+
+ // TODO(jieyu): Decode all the actions attached to the filter.
+ // Currently, libnl does not support that (but will support that in
+ // the future).
+ return Filter<Classifier>(parent, classifier.get(), priority);
+}
+
+/////////////////////////////////////////////////
+// Helpers for internal APIs.
+/////////////////////////////////////////////////
+
+// Returns all the libnl filters (rtnl_cls) attached to the given
+// parent on the link.
+inline Try<std::vector<Netlink<struct rtnl_cls> > > getClses(
+ const Netlink<struct rtnl_link>& link,
+ const queueing::Handle& parent)
+{
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ // Dump all the libnl filters (i.e., rtnl_cls) attached to the given
+ // parent on the link.
+ struct nl_cache* c = NULL;
+ int err = rtnl_cls_alloc_cache(
+ sock.get().get(),
+ rtnl_link_get_ifindex(link.get()),
+ parent.get(),
+ &c);
+
+ if (err != 0) {
+ return Error(
+ "Failed to get filter info from kernel: " +
+ std::string(nl_geterror(err)));
+ }
+
+ Netlink<struct nl_cache> cache(c);
+
+ std::vector<Netlink<struct rtnl_cls> > results;
+
+ for (struct nl_object* o = nl_cache_get_first(cache.get());
+ o != NULL; o = nl_cache_get_next(o)) {
+ nl_object_get(o); // Increment the reference counter.
+ results.push_back(Netlink<struct rtnl_cls>((struct rtnl_cls*) o));
+ }
+
+ return results;
+}
+
+
+// Returns the libnl filter (rtnl_cls) attached to the given parent
+// that matches the specified classifier on the link. Returns None if
+// no match has been found. We use template here so that it works for
+// any type of classifier.
+template <typename Classifier>
+Result<Netlink<struct rtnl_cls> > getCls(
+ const Netlink<struct rtnl_link>& link,
+ const queueing::Handle& parent,
+ const Classifier& classifier)
+{
+ Try<std::vector<Netlink<struct rtnl_cls> > > clses = getClses(link, parent);
+ if (clses.isError()) {
+ return Error(clses.error());
+ }
+
+ foreach (const Netlink<struct rtnl_cls>& cls, clses.get()) {
+ // The decode function will return None if 'cls' does not match
+ // the classifier type. In that case, we just move on to the next
+ // libnl filter.
+ Result<Filter<Classifier> > filter = decodeFilter<Classifier>(cls);
+ if (filter.isError()) {
+ return Error("Failed to decode: " + filter.error());
+ } else if (filter.isSome() && filter.get().classifier() == classifier) {
+ return cls;
+ }
+ }
+
+ return None();
+}
+
+/////////////////////////////////////////////////
+// Internal filter APIs.
+/////////////////////////////////////////////////
+
+// Returns true if there exists a filter attached to the given parent
+// that matches the specified classifier on the link. We use template
+// here so that it works for any type of classifier.
+template <typename Classifier>
+Try<bool> exists(
+ const std::string& _link,
+ const queueing::Handle& parent,
+ const Classifier& classifier)
+{
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ Result<Netlink<struct rtnl_cls> > cls =
+ getCls(link.get(), parent, classifier);
+
+ if (cls.isError()) {
+ return Error(cls.error());
+ }
+ return cls.isSome();
+}
+
+
+// Creates a new filter on the link. Returns false if a filter
+// attached to the same parent with the same classifier already
+// exists. We use template here so that it works for any type of
+// classifier.
+template <typename Classifier>
+Try<bool> create(const std::string& _link, const Filter<Classifier>& filter)
+{
+ // TODO(jieyu): Currently, we're not able to guarantee the atomicity
+ // between the existence check and the following add operation. So
+ // if two threads try to create the same filter, both of them may
+ // succeed and end up with two filters in the kernel.
+ Try<bool> _exists = exists(_link, filter.parent(), filter.classifier());
+ if (_exists.isError()) {
+ return Error("Check filter existence failed: " + _exists.error());
+ } else if (_exists.get()) {
+ // The filter already exists.
+ return false;
+ }
+
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return Error("Link '" + _link + "' is not found");
+ }
+
+ Try<Netlink<struct rtnl_cls> > cls = encodeFilter(link.get(), filter);
+ if (cls.isError()) {
+ return Error("Failed to encode the filter: " + cls.error());
+ }
+
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ int err = rtnl_cls_add(
+ sock.get().get(),
+ cls.get().get(),
+ NLM_F_CREATE | NLM_F_EXCL);
+
+ if (err != 0) {
+ if (err == -NLE_EXIST) {
+ return false;
+ } else {
+ return Error(std::string(nl_geterror(err)));
+ }
+ }
+
+ return true;
+}
+
+
+// Removes the filter attached to the given parent that matches the
+// specified classifier from the link. Returns false if such a filter
+// is not found. We use template here so that it works for any type of
+// classifier.
+template <typename Classifier>
+Try<bool> remove(
+ const std::string& _link,
+ const queueing::Handle& parent,
+ const Classifier& classifier)
+{
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ Result<Netlink<struct rtnl_cls> > cls =
+ getCls(link.get(), parent, classifier);
+
+ if (cls.isError()) {
+ return Error(cls.error());
+ } else if (cls.isNone()) {
+ return false;
+ }
+
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ int err = rtnl_cls_delete(sock.get().get(), cls.get().get(), 0);
+ if (err != 0) {
+ // TODO(jieyu): Interpret the error code and return false if it
+ // indicates that the filter is not found.
+ return Error(std::string(nl_geterror(err)));
+ }
+
+ return true;
+}
+
+
+// Updates the action of the filter attached to the given parent that
+// matches the specified classifier on the link. Returns false if such
+// a filter is not found. We use template here so that it works for
+// any type of classifier.
+template <typename Classifier>
+Try<bool> update(const std::string& _link, const Filter<Classifier>& filter)
+{
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ // Get the old libnl classifier (to-be-updated) from kernel.
+ Result<Netlink<struct rtnl_cls> > oldCls =
+ getCls(link.get(), filter.parent(), filter.classifier());
+
+ if (oldCls.isError()) {
+ return Error(oldCls.error());
+ } else if (oldCls.isNone()) {
+ return false;
+ }
+
+ // The kernel does not allow us to update the priority. So if the
+ // user specifies a priority, we will check to make sure they match.
+ if (filter.priority().isSome() &&
+ filter.priority().get().get() != rtnl_cls_get_prio(oldCls.get().get())) {
+ return Error(
+ "The priorities do not match. The old priority is " +
+ stringify(rtnl_cls_get_prio(oldCls.get().get())) +
+ " and the new priority is " +
+ stringify(filter.priority().get().get()));
+ }
+
+ Try<Netlink<struct rtnl_cls> > newCls = encodeFilter(link.get(), filter);
+ if (newCls.isError()) {
+ return Error("Failed to encode the new filter: " + newCls.error());
+ }
+
+ // Set the handle of the new filter to match that of the old one.
+ rtnl_tc_set_handle(
+ TC_CAST(newCls.get().get()),
+ rtnl_tc_get_handle(TC_CAST(oldCls.get().get())));
+
+ // Set the priority of the new filter to match that of the old one.
+ rtnl_cls_set_prio(
+ newCls.get().get(),
+ rtnl_cls_get_prio(oldCls.get().get()));
+
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ int err = rtnl_cls_change(sock.get().get(), newCls.get().get(), 0);
+ if (err != 0) {
+ if (err == -NLE_OBJ_NOTFOUND) {
+ return false;
+ } else {
+ return Error(std::string(nl_geterror(err)));
+ }
+ }
+
+ return true;
+}
+
+
+// Returns all the filters attached to the given parent on the link.
+// Returns None if the link or the parent is not found. We use
+// template here so that it works for any type of classifier.
+template <typename Classifier>
+Result<std::vector<Filter<Classifier> > > filters(
+ const std::string& _link,
+ const queueing::Handle& parent)
+{
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return None();
+ }
+
+ Try<std::vector<Netlink<struct rtnl_cls> > > clses =
+ getClses(link.get(), parent);
+
+ if (clses.isError()) {
+ return Error(clses.error());
+ }
+
+ std::vector<Filter<Classifier> > results;
+
+ foreach (const Netlink<struct rtnl_cls>& cls, clses.get()) {
+ // The decode function will return None if 'cls' does not match
+ // the classifier type. In that case, we just move on to the next
+ // libnl filter.
+ Result<Filter<Classifier> > filter = decodeFilter<Classifier>(cls);
+ if (filter.isError()) {
+ return Error(filter.error());
+ } else if (filter.isSome()) {
+ results.push_back(filter.get());
+ }
+ }
+
+ return results;
+}
+
+
+// Returns all the classifiers attached to the given parent on the
+// link. Returns None if the link or the parent is not found. We use
+// template here so that it works for any type of classifier.
+template <typename Classifier>
+Result<std::vector<Classifier> > classifiers(
+ const std::string& link,
+ const queueing::Handle& parent)
+{
+ Result<std::vector<Filter<Classifier> > > _filters =
+ filters<Classifier>(link, parent);
+
+ if (_filters.isError()) {
+ return Error(_filters.error());
+ } else if (_filters.isNone()) {
+ return None();
+ }
+
+ std::vector<Classifier> results;
+
+ foreach (const Filter<Classifier>& filter, _filters.get()) {
+ results.push_back(filter.classifier());
+ }
+
+ return results;
+}
+
+} // namespace internal {
+} // namespace filter {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_FILTER_INTERNAL_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/filter/priority.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/priority.hpp b/src/linux/routing/filter/priority.hpp
new file mode 100644
index 0000000..699ad17
--- /dev/null
+++ b/src/linux/routing/filter/priority.hpp
@@ -0,0 +1,66 @@
+/**
+ * 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_PRIORITY_HPP__
+#define __LINUX_ROUTING_FILTER_PRIORITY_HPP__
+
+#include <stdint.h>
+
+namespace routing {
+namespace filter {
+
+// Each filter on a link has a priority (a 16-bit integer). The
+// priority defines the order in which filters will be applied to each
+// packet. The lower the number, the higher the priority. In order to
+// deal with filters of different types, we split the priority into
+// two 8-bit parts: a primary part and a secondary part. When
+// comparing two priorities, their primary parts will be compared
+// first. If they have the same primary part, their secondary parts
+// will then be compared. Typically, the primary part is used to
+// define the preference order between different types of filters. For
+// example, a user may want ICMP packet filters to be always applied
+// first than IP packet filters. In that case, the user can assign
+// ICMP packet filters a higher priority in the primary part. If the
+// user wants to further define orders between filters of the same
+// type, they can use the secondary part.
+class Priority
+{
+public:
+ explicit Priority(uint16_t priority)
+ {
+ primary = (uint8_t) (priority >> 8);
+ secondary = (uint8_t) priority;
+ }
+
+ Priority(uint8_t _primary, uint8_t _secondary)
+ : primary(_primary), secondary(_secondary) {}
+
+ uint16_t get() const
+ {
+ return (((uint16_t) primary) << 8) + secondary;
+ }
+
+private:
+ uint8_t primary;
+ uint8_t secondary;
+};
+
+} // namespace filter {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_FILTER_PRIORITY_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/queueing/handle.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/queueing/handle.cpp b/src/linux/routing/queueing/handle.cpp
new file mode 100644
index 0000000..cd34fc4
--- /dev/null
+++ b/src/linux/routing/queueing/handle.cpp
@@ -0,0 +1,30 @@
+/**
+ * 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 <netlink/route/tc.h>
+
+#include "linux/routing/queueing/handle.hpp"
+
+namespace routing {
+namespace queueing {
+
+const Handle INGRESS_ROOT = Handle(TC_H_INGRESS);
+const Handle EGRESS_ROOT = Handle(TC_H_ROOT);
+
+} // namespace queueing {
+} // namespace routing {
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/queueing/handle.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/queueing/handle.hpp b/src/linux/routing/queueing/handle.hpp
new file mode 100644
index 0000000..2725d07
--- /dev/null
+++ b/src/linux/routing/queueing/handle.hpp
@@ -0,0 +1,74 @@
+/**
+ * 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_QUEUEING_HANDLE_HPP__
+#define __LINUX_ROUTING_QUEUEING_HANDLE_HPP__
+
+#include <stdint.h>
+
+namespace routing {
+namespace queueing {
+
+// Represents a handle for a queueing object (either a queueing
+// discipline or a queueing class). It can be specified by combining a
+// primary number and a secondary number (modeled after traffic
+// control object handle used in kernel).
+class Handle
+{
+public:
+ explicit Handle(uint32_t _handle) : handle(_handle) {}
+
+ Handle(uint16_t primary, uint16_t secondary)
+ {
+ handle = (((uint32_t) primary) << 16) + secondary;
+ }
+
+ uint32_t get() const { return handle; }
+
+private:
+ uint32_t handle;
+};
+
+
+// Packets flowing from the device driver to the network stack are
+// called ingress traffic, and packets flowing from the network stack
+// to the device driver are called egress traffic (shown below).
+//
+// +---------+
+// | Network |
+// | Stack |
+// |---------|
+// | eth0 |
+// +---------+
+// ^ |
+// Ingress | | Egress
+// | |
+// -------+ +------>
+
+
+// The parent of the root ingress queueing discipline.
+extern const Handle INGRESS_ROOT;
+
+
+// The parent of the root egress queueing discipline.
+extern const Handle EGRESS_ROOT;
+
+} // namespace queueing {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_QUEUEING_HANDLE_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/queueing/ingress.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/queueing/ingress.cpp b/src/linux/routing/queueing/ingress.cpp
new file mode 100644
index 0000000..e696950
--- /dev/null
+++ b/src/linux/routing/queueing/ingress.cpp
@@ -0,0 +1,126 @@
+/**
+ * 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 <netlink/errno.h>
+
+#include <netlink/route/qdisc.h>
+#include <netlink/route/tc.h>
+
+#include <stout/error.hpp>
+#include <stout/none.hpp>
+#include <stout/nothing.hpp>
+#include <stout/result.hpp>
+
+#include "linux/routing/queueing/handle.hpp"
+#include "linux/routing/queueing/ingress.hpp"
+#include "linux/routing/queueing/internal.hpp"
+
+using std::string;
+
+namespace routing {
+namespace queueing {
+
+namespace ingress {
+
+// The ingress queueing discipline is not exposed to the user.
+struct Discipline
+{
+ bool operator == (const Discipline& that) const
+ {
+ return true;
+ }
+};
+
+} // namespace ingress {
+
+/////////////////////////////////////////////////
+// Type specific {en}decoding functions.
+/////////////////////////////////////////////////
+
+namespace internal {
+
+// Encodes an ingress queueing discipline into the libnl queueing
+// discipline 'qdisc'. Each type of queueing discipline needs to
+// implement this function.
+template <>
+Try<Nothing> encode<ingress::Discipline>(
+ const Netlink<struct rtnl_qdisc>& qdisc,
+ const ingress::Discipline& discipline)
+{
+ int err = rtnl_tc_set_kind(TC_CAST(qdisc.get()), "ingress");
+ if (err != 0) {
+ return Error(
+ "Failed to set the kind of the queueing discipline: " +
+ string(nl_geterror(err)));
+ }
+
+ rtnl_tc_set_parent(TC_CAST(qdisc.get()), INGRESS_ROOT.get());
+ rtnl_tc_set_handle(TC_CAST(qdisc.get()), ingress::HANDLE.get());
+
+ return Nothing();
+}
+
+
+// Decodes the ingress queue discipline from the libnl queueing
+// discipline 'qdisc'. Each type of queueing discipline needs to
+// implement this function. Returns None if the libnl queueing
+// discipline is not an ingress queueing discipline.
+template <>
+Result<ingress::Discipline> decode<ingress::Discipline>(
+ const Netlink<struct rtnl_qdisc>& qdisc)
+{
+ if (rtnl_tc_get_kind(TC_CAST(qdisc.get())) != string("ingress") ||
+ rtnl_tc_get_parent(TC_CAST(qdisc.get())) != INGRESS_ROOT.get() ||
+ rtnl_tc_get_handle(TC_CAST(qdisc.get())) != ingress::HANDLE.get()) {
+ return None();
+ }
+
+ return ingress::Discipline();
+}
+
+} // namespace internal {
+
+/////////////////////////////////////////////////
+// Public interfaces.
+/////////////////////////////////////////////////
+
+namespace ingress {
+
+const Handle HANDLE = Handle(0xffff, 0);
+
+
+Try<bool> exists(const string& link)
+{
+ return internal::exists(link, Discipline());
+}
+
+
+Try<bool> create(const string& link)
+{
+ return internal::create(link, Discipline());
+}
+
+
+Try<bool> remove(const string& link)
+{
+ return internal::remove(link, Discipline());
+}
+
+} // namespace ingress {
+} // namespace queueing {
+} // namespace routing {
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/queueing/ingress.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/queueing/ingress.hpp b/src/linux/routing/queueing/ingress.hpp
new file mode 100644
index 0000000..b323a7f
--- /dev/null
+++ b/src/linux/routing/queueing/ingress.hpp
@@ -0,0 +1,53 @@
+/**
+ * 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_QUEUEING_INGRESS_HPP__
+#define __LINUX_ROUTING_QUEUEING_INGRESS_HPP__
+
+#include <string>
+
+#include <stout/try.hpp>
+
+#include "linux/routing/queueing/handle.hpp"
+
+namespace routing {
+namespace queueing {
+namespace ingress {
+
+// The handle of the ingress queueing discipline is fixed.
+extern const Handle HANDLE;
+
+
+// Returns true if there exists an ingress qdisc on the link.
+Try<bool> exists(const std::string& link);
+
+
+// Creates a new ingress qdisc on the link. Returns false if an
+// ingress qdisc already exists on the link.
+Try<bool> create(const std::string& link);
+
+
+// Removes the ingress qdisc on the link. Return false if the ingress
+// qdisc is not found.
+Try<bool> remove(const std::string& link);
+
+} // namespace ingress {
+} // namespace queueing {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_QUEUEING_INGRESS_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/c3fe0246/src/linux/routing/queueing/internal.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/queueing/internal.hpp b/src/linux/routing/queueing/internal.hpp
new file mode 100644
index 0000000..4d8bd55
--- /dev/null
+++ b/src/linux/routing/queueing/internal.hpp
@@ -0,0 +1,272 @@
+/**
+ * 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_QUEUEING_INTERNAL_HPP__
+#define __LINUX_ROUTING_QUEUEING_INTERNAL_HPP__
+
+#include <netlink/cache.h>
+#include <netlink/errno.h>
+#include <netlink/object.h>
+#include <netlink/socket.h>
+
+#include <netlink/route/link.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/tc.h>
+
+#include <string>
+#include <vector>
+
+#include <stout/error.hpp>
+#include <stout/foreach.hpp>
+#include <stout/none.hpp>
+#include <stout/nothing.hpp>
+#include <stout/result.hpp>
+#include <stout/try.hpp>
+
+#include "linux/routing/internal.hpp"
+
+#include "linux/routing/link/internal.hpp"
+
+#include "linux/routing/queueing/handle.hpp"
+
+namespace routing {
+namespace queueing {
+namespace internal {
+
+/////////////////////////////////////////////////
+// Helpers for {en}decoding.
+/////////////////////////////////////////////////
+
+// Forward declaration. Each type of queueing discipline needs to
+// implement this function to encode itself into the libnl queueing
+// discipline (rtnl_qdisc).
+template <typename Discipline>
+Try<Nothing> encode(
+ const Netlink<struct rtnl_qdisc>& qdisc,
+ const Discipline& discipline);
+
+
+// Forward declaration. Each type of queueing discipline needs to
+// implement this function to decode itself from the libnl queueing
+// discipline (rtnl_qdisc). Returns None if the libnl queueing
+// discipline does not match the specified queueing discipline type.
+template <typename Discipline>
+Result<Discipline> decode(const Netlink<struct rtnl_qdisc>& qdisc);
+
+
+// Encodes a queueing discipline (in our representation) to a libnl
+// queueing discipline (rtnl_qdisc). We use template here so that it
+// works for any type of queueing discipline.
+template <typename Discipline>
+Try<Netlink<struct rtnl_qdisc> > encode(
+ const Netlink<struct rtnl_link>& link,
+ const Discipline& discipline)
+{
+ struct rtnl_qdisc* q = rtnl_qdisc_alloc();
+ if (q == NULL) {
+ return Error("Failed to allocate a libnl qdisc");
+ }
+
+ Netlink<struct rtnl_qdisc> qdisc(q);
+
+ rtnl_tc_set_link(TC_CAST(qdisc.get()), link.get());
+
+ // Perform queue discipline specific encoding.
+ Try<Nothing> encoding = encode(qdisc, discipline);
+ if (encoding.isError()) {
+ return Error(
+ "Failed to encode the queueing discipline: " +
+ encoding.error());
+ }
+
+ return qdisc;
+}
+
+/////////////////////////////////////////////////
+// Helpers for internal APIs.
+/////////////////////////////////////////////////
+
+// Returns all the libnl queue discipline (rtnl_qdisc) on the link.
+inline Try<std::vector<Netlink<struct rtnl_qdisc> > > getQdiscs(
+ const Netlink<struct rtnl_link>& link)
+{
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ // Dump all the queueing discipline from kernel.
+ struct nl_cache* c = NULL;
+ int err = rtnl_qdisc_alloc_cache(sock.get().get(), &c);
+ if (err != 0) {
+ return Error(
+ "Failed to get queueing discipline info from kernel: " +
+ std::string(nl_geterror(err)));
+ }
+
+ Netlink<struct nl_cache> cache(c);
+
+ std::vector<Netlink<struct rtnl_qdisc> > results;
+
+ for (struct nl_object* o = nl_cache_get_first(cache.get());
+ o != NULL; o = nl_cache_get_next(o)) {
+ nl_object_get(o); // Increment the reference counter.
+ results.push_back(Netlink<struct rtnl_qdisc>((struct rtnl_qdisc*) o));
+ }
+
+ return results;
+}
+
+
+// Returns the libnl queueing discipline (rtnl_qdisc) that matches the
+// specified queueing discipline on the link. Return None if no match
+// has been found. We use template here so that it works for any type
+// of queueing discipline.
+template <typename Discipline>
+Result<Netlink<struct rtnl_qdisc> > getQdisc(
+ const Netlink<struct rtnl_link>& link,
+ const Discipline& discipline)
+{
+ Try<std::vector<Netlink<struct rtnl_qdisc> > > qdiscs = getQdiscs(link);
+ if (qdiscs.isError()) {
+ return Error(qdiscs.error());
+ }
+
+ foreach (const Netlink<struct rtnl_qdisc>& qdisc, qdiscs.get()) {
+ // The decode function will return None if 'qdisc' does not match
+ // the specified queueing discipline. In that case, we just move
+ // on to the next libnl queueing discipline.
+ Result<Discipline> result = decode<Discipline>(qdisc);
+ if (result.isError()) {
+ return Error("Failed to decode: " + result.error());
+ } else if (result.isSome() && result.get() == discipline) {
+ return qdisc;
+ }
+ }
+
+ return None();
+}
+
+/////////////////////////////////////////////////
+// Internal queueing APIs.
+/////////////////////////////////////////////////
+
+// Returns true if the specified queueing discipline exists on the
+// link. We use template here so that it works for any type of
+// queueing discipline.
+template <typename Discipline>
+Try<bool> exists(const std::string& _link, const Discipline& discipline)
+{
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ Result<Netlink<struct rtnl_qdisc> > qdisc = getQdisc(link.get(), discipline);
+ if (qdisc.isError()) {
+ return Error(qdisc.error());
+ }
+ return qdisc.isSome();
+}
+
+
+// Creates a new queueing discipline on the link. Returns false if the
+// same queueing discipline already exists on the link. We use
+// template here so that it works for any type of queueing discipline.
+template <typename Discipline>
+Try<bool> create(const std::string& _link, const Discipline& discipline)
+{
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return Error("Link '" + _link + "' is not found");
+ }
+
+ Try<Netlink<struct rtnl_qdisc> > qdisc = encode(link.get(), discipline);
+ if (qdisc.isError()) {
+ return Error("Failed to encode the queueing discipline: " + qdisc.error());
+ }
+
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ // The flag NLM_F_EXCL tells libnl that if the qdisc already exists,
+ // this function should return error.
+ int err = rtnl_qdisc_add(
+ sock.get().get(),
+ qdisc.get().get(),
+ NLM_F_CREATE | NLM_F_EXCL);
+
+ if (err != 0) {
+ if (err == -NLE_EXIST) {
+ return false;
+ }
+ return Error(
+ "Failed to add a queueing discipline to the link: " +
+ std::string(nl_geterror(err)));
+ }
+
+ return true;
+}
+
+
+// Removes the specified queueing discipline on the link. Return false
+// if the queueing discipline is not found. We use template here so
+// that it works for any type of queueing discipline.
+template <typename Discipline>
+Try<bool> remove(const std::string& _link, const Discipline& discipline)
+{
+ Result<Netlink<struct rtnl_link> > link = link::internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ Result<Netlink<struct rtnl_qdisc> > qdisc = getQdisc(link.get(), discipline);
+ if (qdisc.isError()) {
+ return Error(qdisc.error());
+ } else if (qdisc.isNone()) {
+ return false;
+ }
+
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ int err = rtnl_qdisc_delete(sock.get().get(), qdisc.get().get());
+ if (err != 0) {
+ // TODO(jieyu): Interpret the error code and return false if it
+ // indicates that the queueing discipline is not found.
+ return Error(std::string(nl_geterror(err)));
+ }
+
+ return true;
+}
+
+} // namespace internal {
+} // namespace queueing {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_QUEUEING_INTERNAL_HPP__
[4/9] git commit: Added API for managing links.
Posted by ji...@apache.org.
Added API for managing links.
Review: https://reviews.apache.org/r/20292
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/27e458e8
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/27e458e8
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/27e458e8
Branch: refs/heads/master
Commit: 27e458e88f6e9b6d05ba5d86a55427270916f123
Parents: 6987a9e
Author: Jie Yu <yu...@gmail.com>
Authored: Sun Apr 13 16:01:50 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed May 14 17:38:43 2014 -0700
----------------------------------------------------------------------
configure.ac | 45 ++++++
src/Makefile.am | 18 +++
src/linux/routing/internal.hpp | 97 ++++++++++++
src/linux/routing/link/internal.hpp | 163 ++++++++++++++++++++
src/linux/routing/link/link.cpp | 255 +++++++++++++++++++++++++++++++
src/linux/routing/link/link.hpp | 86 +++++++++++
src/tests/routing_tests.cpp | 249 ++++++++++++++++++++++++++++++
7 files changed, 913 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/27e458e8/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 69acaa1..1ebd196 100644
--- a/configure.ac
+++ b/configure.ac
@@ -163,6 +163,11 @@ AC_ARG_WITH([cxx11],
[builds Mesos without C++11 support (deprecated)]),
[], [with_cxx11=yes])
+AC_ARG_WITH([network-isolator],
+ AS_HELP_STRING([--with-network-isolator],
+ [builds the network isolator]),
+ [], [with_network_isolator=no])
+
# TODO(benh): Support --without-included-protobuf,
# --without-included-glog, etc. Doing this for protobuf is
# considerably more tricky because we need to make sure that 'protoc'
@@ -785,6 +790,46 @@ We need libsasl2 for authentication!
])])
+# Perform necessary configuration for network isolator.
+if test "x$with_network_isolator" = "xyes"; then
+ # Check for OS support.
+ AS_IF([test "$OS_NAME" = "linux"],
+ [],
+ [AC_MSG_ERROR([cannot build network isolator
+-------------------------------------------------------------------
+Network isolator is only supported on Linux!
+-------------------------------------------------------------------
+ ])])
+
+ # Check for libnl.
+ AC_CHECK_LIB([nl-3], [nl_connect], [],
+ [AC_MSG_ERROR([cannot find libnl-3
+-------------------------------------------------------------------
+We need libnl-3 for network isolator!
+-------------------------------------------------------------------
+ ])])
+
+ AC_CHECK_LIB([nl-route-3], [rtnl_cls_alloc], [],
+ [AC_MSG_ERROR([cannot find libnl-route-3
+-------------------------------------------------------------------
+We need libnl-route-3 for network isolator!
+-------------------------------------------------------------------
+ ])])
+
+ # TODO(jieyu): Automatically detect the location where the libnl
+ # headers are installed.
+ LIBNL_CFLAGS=-I/usr/include/libnl3
+
+ AC_SUBST([LIBNL_CFLAGS])
+
+ AC_DEFINE([WITH_NETWORK_ISOLATOR])
+fi
+
+
+AM_CONDITIONAL([WITH_NETWORK_ISOLATOR],
+ [test "x$with_network_isolator" = "xyes"])
+
+
AM_CONDITIONAL([GIT_REPO], [test -d ${srcdir}"/.git"])
http://git-wip-us.apache.org/repos/asf/mesos/blob/27e458e8/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 12374c4..ab5df98 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -95,6 +95,10 @@ if WITH_BUNDLED_ZOOKEEPER
MESOS_CPPFLAGS += -I../$(ZOOKEEPER)/generated
endif
+if WITH_NETWORK_ISOLATOR
+ MESOS_CPPFLAGS += $(LIBNL_CFLAGS)
+endif
+
# README: we build the Mesos library out of a collection of
# convenience libraries (that is, libraries that do not get installed
# but we can use as building blocks to vary compile flags as necessary
@@ -262,6 +266,16 @@ else
EXTRA_DIST += linux/fs.cpp
endif
+if WITH_NETWORK_ISOLATOR
+ libmesos_no_3rdparty_la_SOURCES += \
+ linux/routing/link/link.cpp
+
+ libmesos_no_3rdparty_la_SOURCES += \
+ linux/routing/internal.hpp \
+ linux/routing/link/internal.hpp \
+ linux/routing/link/link.hpp
+endif
+
libmesos_no_3rdparty_la_SOURCES += common/attributes.hpp \
common/build.hpp common/date_utils.hpp common/factory.hpp \
common/protobuf_utils.hpp \
@@ -940,6 +954,10 @@ if OS_LINUX
mesos_tests_SOURCES += tests/fs_tests.cpp
endif
+if WITH_NETWORK_ISOLATOR
+ mesos_tests_SOURCES += tests/routing_tests.cpp
+endif
+
if HAS_JAVA
mesos_tests_SOURCES += tests/zookeeper.cpp \
tests/zookeeper_test_server.cpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/27e458e8/src/linux/routing/internal.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/internal.hpp b/src/linux/routing/internal.hpp
new file mode 100644
index 0000000..fa1a6ee
--- /dev/null
+++ b/src/linux/routing/internal.hpp
@@ -0,0 +1,97 @@
+/**
+ * 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_INTERNAL_HPP__
+#define __LINUX_ROUTING_INTERNAL_HPP__
+
+#include <netlink/cache.h>
+#include <netlink/netlink.h>
+#include <netlink/socket.h>
+
+#include <netlink/route/classifier.h>
+#include <netlink/route/link.h>
+#include <netlink/route/qdisc.h>
+
+#include <stout/error.hpp>
+#include <stout/memory.hpp>
+#include <stout/try.hpp>
+
+namespace routing {
+
+// A helper class for managing netlink objects (e.g., rtnl_link,
+// nl_sock, etc.). It manages the life cycle of a netlink object. It
+// is copyable and assignable, and multiple copies share the same
+// underlying netlink object. A netlink object specific cleanup
+// function will be invoked when the last copy of this wrapper is
+// being deleted (similar to Future<T>). We use this class to simplify
+// our code, especially for error handling.
+template <typename T>
+class Netlink
+{
+public:
+ explicit Netlink(T* object) : data(new Data(object)) {}
+
+ T* get() const { return data->object; }
+
+private:
+ struct Data
+ {
+ explicit Data(T* _object) : object(_object) {}
+
+ ~Data()
+ {
+ if (object != NULL) {
+ cleanup(object);
+ }
+ }
+
+ T* object;
+ };
+
+ memory::shared_ptr<Data> data;
+};
+
+
+// Customized deallocation functions for netlink objects.
+inline void cleanup(struct nl_cache* cache) { nl_cache_free(cache); }
+inline void cleanup(struct nl_sock* sock) { nl_socket_free(sock); }
+inline void cleanup(struct rtnl_cls* cls) { rtnl_cls_put(cls); }
+inline void cleanup(struct rtnl_link* link) { rtnl_link_put(link); }
+inline void cleanup(struct rtnl_qdisc* qdisc) { rtnl_qdisc_put(qdisc); }
+
+
+// Returns a netlink socket for communicating with the kernel. This
+// socket is needed for most of the operations.
+inline Try<Netlink<struct nl_sock> > socket()
+{
+ struct nl_sock* s = nl_socket_alloc();
+ if (s == NULL) {
+ return Error("Failed to allocate netlink socket");
+ }
+
+ Netlink<struct nl_sock> sock(s);
+ if (nl_connect(sock.get(), NETLINK_ROUTE) != 0) {
+ return Error("Failed to connect to routing netlink protocol");
+ }
+
+ return sock;
+}
+
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_INTERNAL_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/27e458e8/src/linux/routing/link/internal.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/link/internal.hpp b/src/linux/routing/link/internal.hpp
new file mode 100644
index 0000000..0d46e9a
--- /dev/null
+++ b/src/linux/routing/link/internal.hpp
@@ -0,0 +1,163 @@
+/**
+ * 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_LINK_INTERNAL_HPP__
+#define __LINUX_ROUTING_LINK_INTERNAL_HPP__
+
+#include <errno.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <linux/if.h> // Must be included after sys/socket.h.
+
+#include <netlink/cache.h>
+#include <netlink/errno.h>
+#include <netlink/socket.h>
+
+#include <netlink/route/link.h>
+
+#include <string>
+
+#include <stout/error.hpp>
+#include <stout/none.hpp>
+#include <stout/os.hpp>
+#include <stout/result.hpp>
+#include <stout/try.hpp>
+
+#include "linux/routing/internal.hpp"
+
+namespace routing {
+namespace link {
+namespace internal {
+
+// Returns the netlink link object associated with a given link by its
+// name. Returns None if the link is not found.
+inline Result<Netlink<struct rtnl_link> > get(const std::string& link)
+{
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ // Dump all the netlink link objects from kernel. Note that the flag
+ // AF_UNSPEC means all available families.
+ struct nl_cache* c = NULL;
+ int err = rtnl_link_alloc_cache(sock.get().get(), AF_UNSPEC, &c);
+ if (err != 0) {
+ return Error(nl_geterror(err));
+ }
+
+ Netlink<struct nl_cache> cache(c);
+ struct rtnl_link* l = rtnl_link_get_by_name(cache.get(), link.c_str());
+ if (l == NULL) {
+ return None();
+ }
+
+ return Netlink<struct rtnl_link>(l);
+}
+
+
+// Returns the netlink link object associated with a given link by its
+// interface index. Returns None if the link is not found.
+inline Result<Netlink<struct rtnl_link> > get(int index)
+{
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ // Dump all the netlink link objects from kernel. Note that the flag
+ // AF_UNSPEC means all available families.
+ struct nl_cache* c = NULL;
+ int err = rtnl_link_alloc_cache(sock.get().get(), AF_UNSPEC, &c);
+ if (err != 0) {
+ return Error(nl_geterror(err));
+ }
+
+ Netlink<struct nl_cache> cache(c);
+ struct rtnl_link* l = rtnl_link_get(cache.get(), index);
+ if (l == NULL) {
+ return None();
+ }
+
+ return Netlink<struct rtnl_link>(l);
+}
+
+
+// Tests if the flags are set on the link. Returns None if the link is
+// not found.
+inline Result<bool> test(const std::string& _link, unsigned int flags)
+{
+ Result<Netlink<struct rtnl_link> > link = get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return None();
+ }
+
+ return flags == (rtnl_link_get_flags(link.get().get()) & flags);
+}
+
+
+// Sets the flags on the link. Returns false if the link is not found.
+inline Try<bool> set(const std::string& _link, unsigned int flags)
+{
+ Result<Netlink<struct rtnl_link> > link = get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ // TODO(jieyu): We use ioctl to set the flags because the interfaces
+ // in libnl have some issues with virtual devices.
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+
+ // Get the existing flags and take a bit-wise OR.
+ ifr.ifr_flags = (rtnl_link_get_flags(link.get().get()) | flags);
+
+ strncpy(ifr.ifr_name, _link.c_str(), IFNAMSIZ);
+
+ int fd = ::socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ return ErrnoError();
+ }
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
+ if (errno == ENODEV) {
+ os::close(fd);
+ return false;
+ } else {
+ // Save the error string as os::close may overwrite errno.
+ std::string message = strerror(errno);
+ os::close(fd);
+ return Error(message);
+ }
+ }
+
+ return true;
+}
+
+} // namespace internal {
+} // namespace link {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_LINK_INTERNAL_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/27e458e8/src/linux/routing/link/link.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/link/link.cpp b/src/linux/routing/link/link.cpp
new file mode 100644
index 0000000..97d03bf
--- /dev/null
+++ b/src/linux/routing/link/link.cpp
@@ -0,0 +1,255 @@
+/**
+ * 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 <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <linux/if.h> // Must be included after sys/socket.h.
+
+#include <netlink/errno.h>
+#include <netlink/socket.h>
+
+#include <netlink/route/link.h>
+
+#include <netlink/route/link/veth.h>
+
+#include <stout/error.hpp>
+#include <stout/none.hpp>
+#include <stout/os.hpp>
+
+#include "linux/routing/internal.hpp"
+
+#include "linux/routing/link/internal.hpp"
+#include "linux/routing/link/link.hpp"
+
+using std::string;
+
+namespace routing {
+namespace link {
+
+Try<bool> exists(const string& _link)
+{
+ Result<Netlink<struct rtnl_link> > link = internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ }
+ return link.isSome();
+}
+
+
+Try<bool> create(
+ const string& veth,
+ const string& peer,
+ const Option<pid_t>& pid)
+{
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ int err = rtnl_link_veth_add(
+ sock.get().get(),
+ veth.c_str(),
+ peer.c_str(),
+ (pid.isNone() ? getpid() : pid.get()));
+
+ if (err != 0) {
+ if (err == -NLE_EXIST) {
+ return false;
+ }
+ return Error(nl_geterror(err));
+ }
+
+ return true;
+}
+
+
+Try<bool> remove(const string& _link)
+{
+ Result<Netlink<struct rtnl_link> > link = internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ int err = rtnl_link_delete(sock.get().get(), link.get().get());
+ if (err != 0) {
+ if (err == -NLE_OBJ_NOTFOUND) {
+ return false;
+ }
+ return Error(nl_geterror(err));
+ }
+
+ return true;
+}
+
+
+Result<int> index(const string& _link)
+{
+ Result<Netlink<struct rtnl_link> > link = internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return None();
+ }
+
+ return rtnl_link_get_ifindex(link.get().get());
+}
+
+
+Result<string> name(int index)
+{
+ Result<Netlink<struct rtnl_link> > link = internal::get(index);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return None();
+ }
+
+ return rtnl_link_get_name(link.get().get());
+}
+
+
+Result<bool> isUp(const string& link)
+{
+ return internal::test(link, IFF_UP);
+}
+
+
+Try<bool> setUp(const string& link)
+{
+ return internal::set(link, IFF_UP);
+}
+
+
+Try<bool> setMAC(const string& link, const net::MAC& mac)
+{
+ // TODO(jieyu): We use ioctl to set the MAC address because the
+ // interfaces in libnl have some issues with virtual devices.
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+
+ strncpy(ifr.ifr_name, link.c_str(), IFNAMSIZ);
+
+ int fd = ::socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ return ErrnoError();
+ }
+
+ // Since loopback interface has sa_family ARPHRD_LOOPBACK, we need
+ // to get the MAC address of the link first to decide what value the
+ // sa_family should be.
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
+ if (errno == ENODEV) {
+ os::close(fd);
+ return false;
+ } else {
+ // Save the error string as os::close may overwrite errno.
+ const string message = strerror(errno);
+ os::close(fd);
+ return Error(message);
+ }
+ }
+
+ ifr.ifr_hwaddr.sa_data[0] = mac[0];
+ ifr.ifr_hwaddr.sa_data[1] = mac[1];
+ ifr.ifr_hwaddr.sa_data[2] = mac[2];
+ ifr.ifr_hwaddr.sa_data[3] = mac[3];
+ ifr.ifr_hwaddr.sa_data[4] = mac[4];
+ ifr.ifr_hwaddr.sa_data[5] = mac[5];
+
+ if (ioctl(fd, SIOCSIFHWADDR, &ifr) == -1) {
+ if (errno == ENODEV) {
+ os::close(fd);
+ return false;
+ } else {
+ // Save the error string as os::close may overwrite errno.
+ const string message = strerror(errno);
+ os::close(fd);
+ return Error(message);
+ }
+ }
+
+ os::close(fd);
+ return true;
+}
+
+
+Result<hashmap<string, uint64_t> > statistics(const string& _link)
+{
+ Result<Netlink<struct rtnl_link> > link = internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return None();
+ }
+
+ rtnl_link_stat_id_t stats[] = {
+ // Statistics related to receiving.
+ RTNL_LINK_RX_PACKETS,
+ RTNL_LINK_RX_BYTES,
+ RTNL_LINK_RX_ERRORS,
+ RTNL_LINK_RX_DROPPED,
+ RTNL_LINK_RX_COMPRESSED,
+ RTNL_LINK_RX_FIFO_ERR,
+ RTNL_LINK_RX_LEN_ERR,
+ RTNL_LINK_RX_OVER_ERR,
+ RTNL_LINK_RX_CRC_ERR,
+ RTNL_LINK_RX_FRAME_ERR,
+ RTNL_LINK_RX_MISSED_ERR,
+ RTNL_LINK_MULTICAST,
+
+ // Statistics related to sending.
+ RTNL_LINK_TX_PACKETS,
+ RTNL_LINK_TX_BYTES,
+ RTNL_LINK_TX_ERRORS,
+ RTNL_LINK_TX_DROPPED,
+ RTNL_LINK_TX_COMPRESSED,
+ RTNL_LINK_TX_FIFO_ERR,
+ RTNL_LINK_TX_ABORT_ERR,
+ RTNL_LINK_TX_CARRIER_ERR,
+ RTNL_LINK_TX_HBEAT_ERR,
+ RTNL_LINK_TX_WIN_ERR,
+ RTNL_LINK_COLLISIONS,
+ };
+
+ hashmap<string, uint64_t> results;
+
+ char buf[32];
+ size_t size = sizeof(stats) / sizeof(stats[0]);
+
+ for (size_t i = 0; i < size; i++) {
+ rtnl_link_stat2str(stats[i], buf, 32);
+ results[buf] = rtnl_link_get_stat(link.get().get(), stats[i]);
+ }
+
+ return results;
+}
+
+} // namespace link {
+} // namespace routing {
http://git-wip-us.apache.org/repos/asf/mesos/blob/27e458e8/src/linux/routing/link/link.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/link/link.hpp b/src/linux/routing/link/link.hpp
new file mode 100644
index 0000000..fe19ff2
--- /dev/null
+++ b/src/linux/routing/link/link.hpp
@@ -0,0 +1,86 @@
+/**
+ * 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_LINK_LINK_HPP__
+#define __LINUX_ROUTING_LINK_LINK_HPP__
+
+#include <stdint.h>
+
+#include <sys/types.h>
+
+#include <string>
+
+#include <stout/hashmap.hpp>
+#include <stout/net.hpp>
+#include <stout/option.hpp>
+#include <stout/result.hpp>
+#include <stout/try.hpp>
+
+namespace routing {
+namespace link {
+
+// Returns true if the link exists.
+Try<bool> exists(const std::string& link);
+
+
+// Creates a pair of virtual network links. The peer link is put in
+// the network namespace represented by 'pid' upon creation if
+// specified. If 'pid' is None, the peer link will be put into
+// caller's network namespace. Returns false if the virtual network
+// links (with the same name) already exist.
+Try<bool> create(
+ const std::string& veth,
+ const std::string& peer,
+ const Option<pid_t>& pid);
+
+
+// Removes a link. Returns false if the link is not found.
+Try<bool> remove(const std::string& link);
+
+
+// Returns the interface index of the link. Returns None if the link
+// is not found.
+Result<int> index(const std::string& link);
+
+
+// Returns the name of the link from its interface index. Returns None
+// if the link with the given interface index is not found.
+Result<std::string> name(int index);
+
+
+// Returns true if the link is up. Returns None if the link is not
+// found.
+Result<bool> isUp(const std::string& link);
+
+
+// Sets the link up (IFF_UP). Returns false if the link is not found.
+Try<bool> setUp(const std::string& link);
+
+
+// Sets the MAC address of the link. Returns false if the link is not
+// found.
+Try<bool> setMAC(const std::string& link, const net::MAC& mac);
+
+
+// Returns the statistics of the link.
+Result<hashmap<std::string, uint64_t> > statistics(const std::string& link);
+
+} // namespace link
+} // namespace routing
+
+#endif // __LINUX_ROUTING_LINK_LINK_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/27e458e8/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
new file mode 100644
index 0000000..8dfc23e
--- /dev/null
+++ b/src/tests/routing_tests.cpp
@@ -0,0 +1,249 @@
+/**
+ * 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 <signal.h>
+#include <unistd.h>
+
+#include <linux/version.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <gtest/gtest.h>
+
+#include <stout/foreach.hpp>
+#include <stout/gtest.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/net.hpp>
+
+#include "linux/routing/link/link.hpp"
+
+using namespace routing;
+
+using std::set;
+using std::string;
+
+
+static const string TEST_VETH_LINK = "veth-test";
+static const string TEST_PEER_LINK = "veth-peer";
+
+
+class RoutingTest : public ::testing::Test {};
+
+
+// Tests that require setting up virtual ethernet on host.
+class RoutingVethTest : public RoutingTest
+{
+protected:
+ virtual void SetUp()
+ {
+ // Clean up the test links, in case it wasn't cleaned up properly
+ // from previous tests.
+ link::remove(TEST_VETH_LINK);
+
+ ASSERT_SOME_FALSE(link::exists(TEST_VETH_LINK));
+ ASSERT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+ }
+
+ virtual void TearDown()
+ {
+ link::remove(TEST_VETH_LINK);
+ }
+};
+
+
+TEST_F(RoutingTest, LinkIndex)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ EXPECT_SOME_NE(0, link::index(link));
+ }
+
+ EXPECT_NONE(link::index("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkName)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ EXPECT_SOME_NE(0, link::index(link));
+ EXPECT_SOME_EQ(link, link::name(link::index(link).get()));
+ }
+}
+
+
+TEST_F(RoutingTest, LinkStatistics)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ Result<hashmap<string, uint64_t> > statistics = link::statistics(link);
+
+ ASSERT_SOME(statistics);
+ EXPECT_TRUE(statistics.get().contains("rx_packets"));
+ EXPECT_TRUE(statistics.get().contains("rx_bytes"));
+ EXPECT_TRUE(statistics.get().contains("tx_packets"));
+ EXPECT_TRUE(statistics.get().contains("tx_bytes"));
+ }
+
+ EXPECT_NONE(link::statistics("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkExists)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ EXPECT_SOME_TRUE(link::exists(link));
+ }
+
+ EXPECT_SOME_FALSE(link::exists("not-exist"));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkCreate)
+{
+ 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));
+
+ EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+ EXPECT_SOME_NE(0, link::index(TEST_PEER_LINK));
+
+ // Test the case where the veth (with the same name) already exists.
+ EXPECT_SOME_FALSE(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkRemove)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(link::remove(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(link::remove(TEST_PEER_LINK));
+}
+
+
+// Entry point of the child process (used in clone()).
+static int child(void*)
+{
+ // Wait to be killed.
+ while (true) {
+ sleep(1);
+ }
+
+ // Should not reach here.
+ ABORT("Child process should not reach here");
+
+ return -1;
+}
+
+
+// Network namespace is not available until Linux 2.6.24.
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+TEST_F(RoutingVethTest, ROOT_LinkCreatePid)
+{
+ // Stack used in the child process.
+ unsigned long long stack[32];
+
+ pid_t pid = ::clone(child, &stack[31], CLONE_NEWNET | SIGCHLD, NULL);
+ ASSERT_NE(-1, pid);
+
+ // In parent process.
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, pid));
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+
+ // The peer should not exist in parent network namespace.
+ EXPECT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+
+ // TODO(jieyu): Enter the child network namespace and make sure that
+ // the TEST_PEER_LINK is there.
+
+ EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+
+ // Kill the child process.
+ ASSERT_NE(-1, kill(pid, SIGKILL));
+
+ // Wait for the child process.
+ int status;
+ EXPECT_NE(-1, waitpid((pid_t) -1, &status, 0));
+ ASSERT_TRUE(WIFSIGNALED(status));
+ EXPECT_EQ(SIGKILL, WTERMSIG(status));
+}
+#endif
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetUp)
+{
+ 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));
+
+ EXPECT_SOME_FALSE(link::isUp(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::setUp(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::isUp(TEST_VETH_LINK));
+
+ EXPECT_SOME_FALSE(link::isUp(TEST_PEER_LINK));
+ EXPECT_SOME_TRUE(link::setUp(TEST_PEER_LINK));
+ EXPECT_SOME_TRUE(link::isUp(TEST_PEER_LINK));
+
+ EXPECT_NONE(link::isUp("non-exist"));
+ EXPECT_SOME_FALSE(link::setUp("non-exist"));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetMAC)
+{
+ 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));
+
+ uint8_t bytes[6] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
+
+ EXPECT_SOME_TRUE(link::setMAC(TEST_VETH_LINK, net::MAC(bytes)));
+ EXPECT_SOME_TRUE(link::setMAC(TEST_PEER_LINK, net::MAC(bytes)));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+
+ ASSERT_SOME(mac);
+ EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+ mac = net::mac(TEST_PEER_LINK);
+
+ ASSERT_SOME(mac);
+ EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+ EXPECT_SOME_FALSE(link::setMAC("non-exist", net::MAC(bytes)));
+
+ // Kernel will reject a multicast MAC address.
+ uint8_t multicast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ EXPECT_ERROR(link::setMAC(TEST_VETH_LINK, net::MAC(multicast)));
+}
[2/9] git commit: Added API for managing IP packet filters.
Posted by ji...@apache.org.
Added API for managing IP packet filters.
Review: https://reviews.apache.org/r/20297
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/d6bcfa9b
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/d6bcfa9b
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/d6bcfa9b
Branch: refs/heads/master
Commit: d6bcfa9bde6e7e93815eaaf437e26192ab5dff89
Parents: 336420c
Author: Jie Yu <yu...@gmail.com>
Authored: Sun Apr 13 15:52:38 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/ip.cpp | 456 +++++++++++++++++++++++++++++++++++
src/linux/routing/filter/ip.hpp | 161 +++++++++++++
src/tests/routing_tests.cpp | 345 ++++++++++++++++++++++++++
4 files changed, 964 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/d6bcfa9b/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index ddbd82b..ce97f1b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -272,6 +272,7 @@ if WITH_NETWORK_ISOLATOR
linux/routing/link/link.cpp \
linux/routing/filter/arp.cpp \
linux/routing/filter/icmp.cpp \
+ linux/routing/filter/ip.cpp \
linux/routing/queueing/handle.cpp \
linux/routing/queueing/ingress.cpp
@@ -283,6 +284,7 @@ if WITH_NETWORK_ISOLATOR
linux/routing/filter/filter.hpp \
linux/routing/filter/icmp.hpp \
linux/routing/filter/internal.hpp \
+ linux/routing/filter/ip.hpp \
linux/routing/filter/priority.hpp \
linux/routing/link/internal.hpp \
linux/routing/link/link.hpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/d6bcfa9b/src/linux/routing/filter/ip.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/ip.cpp b/src/linux/routing/filter/ip.cpp
new file mode 100644
index 0000000..24f3d52
--- /dev/null
+++ b/src/linux/routing/filter/ip.cpp
@@ -0,0 +1,456 @@
+/**
+ * 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 <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/internal.hpp"
+#include "linux/routing/filter/ip.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 IP classifier into the libnl filter 'cls'. Each type of
+// classifier needs to implement this function.
+template <>
+Try<Nothing> encode<ip::Classifier>(
+ const Netlink<struct rtnl_cls>& cls,
+ const ip::Classifier& classifier)
+{
+ 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)));
+ }
+
+ // TODO(jieyu): Do we need to check the protocol (e.g., TCP/UDP)?
+
+ if (classifier.destinationMAC().isSome()) {
+ // Since we set the protocol of this classifier to be ETH_P_IP
+ // above, all IP packets that contain 802.1Q tag (i.e., VLAN tag)
+ // will not match this classifier (those packets have protocol
+ // ETH_P_8021Q). Therefore, the offset of the start of the MAC
+ // destination address is at -14 (0xfffffff2).
+ net::MAC mac = classifier.destinationMAC().get();
+
+ // To avoid confusion, we only use u32 selectors which are used to
+ // match arbitrary 32-bit content in a packet.
+
+ // We need two u32 selectors as MAC address contains 6 bytes.
+ uint32_t value[2];
+
+ // Format of an IP packet at offset -16.
+ // +--------+--------+--------+--------+
+ // | X | X | mac[0] | mac[1] |
+ // +--------+--------+--------+--------+
+ // Offset: -16 -15 -14 -13
+ value[0] = (((uint32_t) mac[0]) << 8) + ((uint32_t) mac[1]);
+
+ // Format of an IP packet at offset -12:
+ // +--------+--------+--------+--------+
+ // | mac[2] | mac[3] | mac[4] | mac[5] |
+ // +--------+--------+--------+--------+
+ // Offset: -12 -11 -10 -09
+ value[1] = (((uint32_t) mac[2]) << 24) +
+ (((uint32_t) mac[3]) << 16) +
+ (((uint32_t) mac[4]) << 8) +
+ ((uint32_t) mac[5]);
+
+ // To match the first two bytes of the MAC address.
+ err = rtnl_u32_add_key(
+ cls.get(),
+ htonl(value[0]),
+ htonl(0x0000ffff), // Ignore offset -16 and -15.
+ -16, // Offset from which to start matching.
+ 0);
+
+ if (err != 0) {
+ return Error(
+ "Failed to add selector for destination MAC address: " +
+ string(nl_geterror(err)));
+ }
+
+ // To match the last four bytes of the MAC address.
+ err = rtnl_u32_add_key(
+ cls.get(),
+ htonl(value[1]),
+ htonl(0xffffffff),
+ -12, // Offset from which to start matching.
+ 0);
+
+ if (err != 0) {
+ return Error(
+ "Failed to add selector for destination MAC address: " +
+ 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,
+ 0);
+
+ if (err != 0) {
+ return Error(
+ "Failed to add selector for destination IP address: " +
+ string(nl_geterror(err)));
+ }
+ }
+
+ // TODO(jieyu): Here, we assume that the IP packet does not contain
+ // IP options. As a result, we can hard code the offsets of the
+ // source port and the destination ports to be 20 and 22
+ // respectively. Users can choose to add a high priority filter to
+ // filter all the IP packets that have IP options.
+ if (classifier.sourcePorts().isSome()) {
+ // Format of an IP packet at offset 20:
+ // +--------+--------+--------+--------+
+ // | Source Port | X | X |
+ // +--------+--------+--------+--------+
+ // Offset: 20 21 22 23
+ uint32_t value = ((uint32_t) classifier.sourcePorts().get().begin()) << 16;
+ uint32_t mask = ((uint32_t) classifier.sourcePorts().get().mask()) << 16;
+
+ // To match IP packets that have the given source ports.
+ err = rtnl_u32_add_key(
+ cls.get(),
+ htonl(value),
+ htonl(mask),
+ 20, // Offset to which to start matching.
+ 0);
+
+ if (err != 0) {
+ return Error(
+ "Failed to add selector for source ports: " +
+ string(nl_geterror(err)));
+ }
+ }
+
+ if (classifier.destinationPorts().isSome()) {
+ // Format of an IP packet at offset 20:
+ // +--------+--------+--------+--------+
+ // | X | X | Dest. Port |
+ // +--------+--------+--------+--------+
+ // Offset: 20 21 22 23
+ uint32_t value = (uint32_t) classifier.destinationPorts().get().begin();
+ uint32_t mask = (uint32_t) classifier.destinationPorts().get().mask();
+
+ // To match IP packets that have the given destination ports.
+ err = rtnl_u32_add_key(
+ cls.get(),
+ htonl(value),
+ htonl(mask),
+ 20,
+ 0);
+
+ if (err != 0) {
+ return Error(
+ "Failed to add selector for destination ports: " +
+ string(nl_geterror(err)));
+ }
+ }
+
+ return Nothing();
+}
+
+
+// Decodes the IP classifier from the libnl filter 'cls'. Each type of
+// classifier needs to implement this function. Returns None if the
+// libnl filter is not an IP packet filter.
+template <>
+Result<ip::Classifier> decode<ip::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<uint32_t> valueDestinationMAC1;
+ Option<uint32_t> valueDestinationMAC2;
+ Option<uint32_t> valueDestinationIP;
+ Option<uint32_t> valueSourcePorts;
+ Option<uint32_t> valueSourcePortsMask;
+ Option<uint32_t> valueDestinationPorts;
+ Option<uint32_t> valueDestinationPortsMask;
+
+ // There are at most 0xff keys.
+ for (uint8_t i = 0; i <= 0xff; i++) {
+ uint32_t value;
+ uint32_t mask;
+ int offset;
+ int offsetmask;
+
+ 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 classifier: " +
+ 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 && mask == 0x00ff0000) {
+ protocol = value;
+ }
+
+ // First two bytes of the destination MAC address.
+ if (offset == -16 && mask == 0x0000ffff) {
+ valueDestinationMAC1 = value;
+ }
+
+ // Last four bytes of the MAC address.
+ if (offset == -12 && mask == 0xffffffff) {
+ valueDestinationMAC2 = value;
+ }
+
+ // Destination IP address.
+ if (offset == 16 && mask == 0xffffffff) {
+ valueDestinationIP = value;
+ }
+
+ // Source or destination ports, depending on the mask.
+ if (offset == 20) {
+ if ((mask | 0xffff0000) == 0xffff0000) {
+ valueSourcePorts = value;
+ valueSourcePortsMask = mask;
+ } else if ((mask | 0x0000ffff) == 0x0000ffff) {
+ valueDestinationPorts = value;
+ valueDestinationPortsMask = mask;
+ }
+ }
+ }
+
+ // IP packet filters do not check IP protocol field.
+ if (protocol.isSome()) {
+ return None();
+ }
+
+ // Sanity checks.
+ if (valueDestinationMAC1.isSome() && valueDestinationMAC2.isNone()) {
+ return Error("Missing the last 4 bytes of the destination MAC address");
+ }
+
+ if (valueDestinationMAC1.isNone() && valueDestinationMAC2.isSome()) {
+ return Error("Missing the first 2 bytes of the destination MAC address");
+ }
+
+ if (valueSourcePorts.isSome() && valueSourcePortsMask.isNone()) {
+ return Error("Missing source ports mask");
+ }
+
+ if (valueSourcePorts.isNone() && valueSourcePortsMask.isSome()) {
+ return Error("Missing source ports value");
+ }
+
+ if (valueDestinationPorts.isSome() && valueDestinationPortsMask.isNone()) {
+ return Error("Missing destination ports mask");
+ }
+
+ if (valueDestinationPorts.isNone() && valueDestinationPortsMask.isSome()) {
+ return Error("Missing destination ports value");
+ }
+
+ // Pack the values into the classifier.
+ Option<net::MAC> destinationMAC;
+ Option<net::IP> destinationIP;
+ Option<ip::PortRange> sourcePorts;
+ Option<ip::PortRange> destinationPorts;
+
+ if (valueDestinationMAC1.isSome() && valueDestinationMAC2.isSome()) {
+ uint8_t bytes[6];
+
+ bytes[0] = (uint8_t) (valueDestinationMAC1.get() >> 8);
+ bytes[1] = (uint8_t) valueDestinationMAC1.get();
+ bytes[2] = (uint8_t) (valueDestinationMAC2.get() >> 24);
+ bytes[3] = (uint8_t) (valueDestinationMAC2.get() >> 16);
+ bytes[4] = (uint8_t) (valueDestinationMAC2.get() >> 8);
+ bytes[5] = (uint8_t) valueDestinationMAC2.get();
+
+ destinationMAC = net::MAC(bytes);
+ }
+
+ if (valueDestinationIP.isSome()) {
+ destinationIP = net::IP(valueDestinationIP.get());
+ }
+
+ if (valueSourcePorts.isSome() && valueSourcePortsMask.isSome()) {
+ uint16_t begin = (uint16_t) (valueSourcePorts.get() >> 16);
+ uint16_t mask = (uint16_t) (valueSourcePortsMask.get() >> 16);
+
+ Try<ip::PortRange> ports = ip::PortRange::fromBeginMask(begin, mask);
+ if (ports.isError()) {
+ return Error("Invalid source ports: " + ports.error());
+ }
+
+ sourcePorts = ports.get();
+ }
+
+ if (valueDestinationPorts.isSome() && valueDestinationPortsMask.isSome()) {
+ uint16_t begin = (uint16_t) valueDestinationPorts.get();
+ uint16_t mask = (uint16_t) valueDestinationPortsMask.get();
+
+ Try<ip::PortRange> ports = ip::PortRange::fromBeginMask(begin, mask);
+ if (ports.isError()) {
+ return Error("Invalid destination ports: " + ports.error());
+ }
+
+ destinationPorts = ports.get();
+ }
+
+ return ip::Classifier(
+ destinationMAC,
+ destinationIP,
+ sourcePorts,
+ destinationPorts);
+}
+
+} // namespace internal {
+
+/////////////////////////////////////////////////
+// Public interfaces.
+/////////////////////////////////////////////////
+
+namespace ip {
+
+Try<PortRange> PortRange::fromBeginEnd(uint16_t begin, uint16_t end)
+{
+ if (begin > end) {
+ return Error("'begin' is larger than 'end'");
+ }
+
+ uint16_t size = end - begin + 1;
+
+ // Test if the size is a power of 2.
+ if ((size & (size - 1)) != 0) {
+ return Error("The size " + stringify(size) + " is not a power of 2");
+ }
+
+ // Test if begin is aligned.
+ if (begin % size != 0) {
+ return Error("'begin' is not size aligned");
+ }
+
+ return PortRange(begin, end);
+}
+
+
+Try<PortRange> PortRange::fromBeginMask(uint16_t begin, uint16_t mask)
+{
+ uint16_t size = ~mask + 1;
+ return fromBeginEnd(begin, begin + size - 1);
+}
+
+
+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> remove(
+ const string& link,
+ const queueing::Handle& parent,
+ const Classifier& classifier)
+{
+ return internal::remove(link, parent, classifier);
+}
+
+
+Result<vector<Classifier> > classifiers(
+ const string& link,
+ const queueing::Handle& parent)
+{
+ return internal::classifiers<Classifier>(link, parent);
+}
+
+} // namespace ip {
+} // namespace filter {
+} // namespace routing {
http://git-wip-us.apache.org/repos/asf/mesos/blob/d6bcfa9b/src/linux/routing/filter/ip.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/ip.hpp b/src/linux/routing/filter/ip.hpp
new file mode 100644
index 0000000..fc95e0c
--- /dev/null
+++ b/src/linux/routing/filter/ip.hpp
@@ -0,0 +1,161 @@
+/**
+ * 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_IP_HPP__
+#define __LINUX_ROUTING_FILTER_IP_HPP__
+
+#include <stdint.h>
+
+#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 ip {
+
+// Represents a port range that can be used by a single u32 matcher.
+// The port range [begin, end] (both begin and end are inclusive)
+// should have size = 2^n (n=0,1,2,...) and its begin is size aligned
+// (i.e., begin % size == 0).
+class PortRange
+{
+public:
+ // Creates a port range from the specified begin and end. Returns
+ // error if it does not meet the above requirements. All values are
+ // in host order.
+ static Try<PortRange> fromBeginEnd(uint16_t begin, uint16_t end);
+
+ // Creates a port range from the specified begin and mask. Returns
+ // error if it does not meet the above requirements. All values are
+ // in host order.
+ static Try<PortRange> fromBeginMask(uint16_t begin, uint16_t mask);
+
+ // Returns the begin (in host order) of this port range.
+ uint16_t begin() const { return begin_; }
+
+ // Returns the end (in host order) of this port range.
+ uint16_t end() const { return end_; }
+
+ // Returns the mask (in host order) of this port range.
+ uint16_t mask() const { return ~(end_ - begin_); }
+
+ bool operator == (const PortRange& that) const
+ {
+ return begin_ == that.begin_ && end_ == that.end_;
+ }
+
+private:
+ PortRange(uint16_t _begin, uint16_t _end)
+ : begin_(_begin), end_(_end) {}
+
+ uint16_t begin_; // In host order.
+ uint16_t end_; // In host order.
+};
+
+
+class Classifier
+{
+public:
+ Classifier(
+ const Option<net::MAC>& _destinationMAC,
+ const Option<net::IP>& _destinationIP,
+ const Option<PortRange>& _sourcePorts,
+ const Option<PortRange>& _destinationPorts)
+ : destinationMAC_(_destinationMAC),
+ destinationIP_(_destinationIP),
+ sourcePorts_(_sourcePorts),
+ destinationPorts_(_destinationPorts) {}
+
+ bool operator == (const Classifier& that) const
+ {
+ return (destinationMAC_ == that.destinationMAC_ &&
+ destinationIP_ == that.destinationIP_ &&
+ destinationPorts_ == that.destinationPorts_ &&
+ sourcePorts_ == that.sourcePorts_);
+ }
+
+ const Option<net::MAC>& destinationMAC() const { return destinationMAC_; }
+ const Option<net::IP>& destinationIP() const { return destinationIP_; }
+ const Option<PortRange>& sourcePorts() const { return sourcePorts_; }
+
+ const Option<PortRange>& destinationPorts() const
+ {
+ return destinationPorts_;
+ }
+
+private:
+ Option<net::MAC> destinationMAC_;
+ Option<net::IP> destinationIP_;
+ Option<PortRange> sourcePorts_;
+ Option<PortRange> destinationPorts_;
+};
+
+
+// Returns true if an IP packet filter attached to the given parent
+// that matches the specified classifier exists on the link.
+Try<bool> exists(
+ const std::string& link,
+ const queueing::Handle& parent,
+ const Classifier& classifier);
+
+
+// Creates an IP packet filter attached to the given parent on the
+// link which will redirect all the IP packets that satisfy the
+// conditions specified by the classifier to the target link. Returns
+// false if an IP packet filter attached to the given parent with the
+// same classifier already exists.
+Try<bool> create(
+ const std::string& link,
+ const queueing::Handle& parent,
+ const Classifier& classifier,
+ const Option<Priority>& priority,
+ const action::Redirect& redirect);
+
+
+// Removes the IP 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);
+
+
+// Returns the classifiers of all the IP 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 ip {
+} // namespace filter {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_FILTER_IP_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/d6bcfa9b/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
index cc3d6da..5650fca 100644
--- a/src/tests/routing_tests.cpp
+++ b/src/tests/routing_tests.cpp
@@ -35,6 +35,7 @@
#include "linux/routing/filter/arp.hpp"
#include "linux/routing/filter/icmp.hpp"
+#include "linux/routing/filter/ip.hpp"
#include "linux/routing/link/link.hpp"
@@ -93,6 +94,40 @@ protected:
};
+TEST_F(RoutingTest, PortRange)
+{
+ Try<ip::PortRange> ports = ip::PortRange::fromBeginEnd(1, 0);
+ EXPECT_ERROR(ports);
+
+ ports = ip::PortRange::fromBeginEnd(4, 11);
+ EXPECT_ERROR(ports);
+
+ ports = ip::PortRange::fromBeginEnd(4, 7);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(4u, ports.get().begin());
+ EXPECT_EQ(7u, ports.get().end());
+ EXPECT_EQ(0xfffc, ports.get().mask());
+
+ ports = ip::PortRange::fromBeginEnd(10, 10);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(10u, ports.get().begin());
+ EXPECT_EQ(10u, ports.get().end());
+ EXPECT_EQ(0xffff, ports.get().mask());
+
+ ports = ip::PortRange::fromBeginMask(20, 0xffff);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(20u, ports.get().begin());
+ EXPECT_EQ(20u, ports.get().end());
+ EXPECT_EQ(0xffff, ports.get().mask());
+
+ ports = ip::PortRange::fromBeginMask(1024, 0xfff8);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(1024u, ports.get().begin());
+ EXPECT_EQ(1031u, ports.get().end());
+ EXPECT_EQ(0xfff8, ports.get().mask());
+}
+
+
TEST_F(RoutingTest, LinkIndex)
{
Try<set<string> > links = net::links();
@@ -587,3 +622,313 @@ TEST_F(RoutingVethTest, ROOT_ICMPFilterUpdate)
ingress::HANDLE,
icmp::Classifier(ip)));
}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate)
+{
+ 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));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts);
+
+ Try<ip::PortRange> destinationPorts =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts);
+
+ ip::Classifier classifier =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts.get(),
+ destinationPorts.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(1u, classifiers.get().size());
+ EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC());
+ EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP());
+
+ EXPECT_SOME_EQ(
+ sourcePorts.get(),
+ classifiers.get().front().sourcePorts());
+
+ EXPECT_SOME_EQ(
+ destinationPorts.get(),
+ classifiers.get().front().destinationPorts());
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate2)
+{
+ 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(0x12345678);
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ip::Classifier(None(), ip, None(), None()),
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ip::Classifier(None(), ip, None(), None())));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(1u, classifiers.get().size());
+ EXPECT_NONE(classifiers.get().front().destinationMAC());
+ EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP());
+ EXPECT_NONE(classifiers.get().front().sourcePorts());
+ EXPECT_NONE(classifiers.get().front().destinationPorts());
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateDuplicated)
+{
+ 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));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts);
+
+ Try<ip::PortRange> destinationPorts =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts);
+
+ ip::Classifier classifier =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts.get(),
+ destinationPorts.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+ EXPECT_SOME_FALSE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateMultiple)
+{
+ 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));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts1 =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts1);
+
+ Try<ip::PortRange> destinationPorts1 =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts1);
+
+ Try<ip::PortRange> sourcePorts2 =
+ ip::PortRange::fromBeginEnd(3024, 3025);
+
+ ASSERT_SOME(sourcePorts2);
+
+ Try<ip::PortRange> destinationPorts2 =
+ ip::PortRange::fromBeginEnd(4000, 4003);
+
+ ASSERT_SOME(destinationPorts2);
+
+ ip::Classifier classifier1 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts1.get(),
+ destinationPorts1.get());
+
+ ip::Classifier classifier2 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts2.get(),
+ destinationPorts2.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier1,
+ Priority(2, 1),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier2,
+ Priority(2, 2),
+ action::Redirect(TEST_PEER_LINK)));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(2u, classifiers.get().size());
+
+ EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC());
+ EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP());
+
+ EXPECT_SOME_EQ(
+ sourcePorts1.get(),
+ classifiers.get().front().sourcePorts());
+
+ EXPECT_SOME_EQ(
+ destinationPorts1.get(),
+ classifiers.get().front().destinationPorts());
+
+ EXPECT_SOME_EQ(mac.get(), classifiers.get().back().destinationMAC());
+ EXPECT_SOME_EQ(ip, classifiers.get().back().destinationIP());
+
+ EXPECT_SOME_EQ(
+ sourcePorts2.get(),
+ classifiers.get().back().sourcePorts());
+
+ EXPECT_SOME_EQ(
+ destinationPorts2.get(),
+ classifiers.get().back().destinationPorts());
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterRemove)
+{
+ 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));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts1 =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts1);
+
+ Try<ip::PortRange> destinationPorts1 =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts1);
+
+ Try<ip::PortRange> sourcePorts2 =
+ ip::PortRange::fromBeginEnd(3024, 3025);
+
+ ASSERT_SOME(sourcePorts2);
+
+ Try<ip::PortRange> destinationPorts2 =
+ ip::PortRange::fromBeginEnd(4000, 4003);
+
+ ASSERT_SOME(destinationPorts2);
+
+ ip::Classifier classifier1 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts1.get(),
+ destinationPorts1.get());
+
+ ip::Classifier classifier2 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts2.get(),
+ destinationPorts2.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier1,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier2,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+ EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+
+ EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+ EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ EXPECT_EQ(0u, classifiers.get().size());
+}
[8/9] git commit: Added API for getting the default gateway.
Posted by ji...@apache.org.
Added API for getting the default gateway.
Review: https://reviews.apache.org/r/20347
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/19f643aa
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/19f643aa
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/19f643aa
Branch: refs/heads/master
Commit: 19f643aa5b84f3542cd92b0d84bef543c55b9b52
Parents: d6bcfa9
Author: Jie Yu <yu...@gmail.com>
Authored: Mon Apr 14 18:23:45 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed May 14 17:38:43 2014 -0700
----------------------------------------------------------------------
src/Makefile.am | 2 +
src/linux/routing/route.cpp | 136 +++++++++++++++++++++++++++++++++++++++
src/linux/routing/route.hpp | 65 +++++++++++++++++++
src/tests/routing_tests.cpp | 11 ++++
4 files changed, 214 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/19f643aa/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index ce97f1b..ae576c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -268,6 +268,7 @@ endif
if WITH_NETWORK_ISOLATOR
libmesos_no_3rdparty_la_SOURCES += \
+ linux/routing/route.cpp \
linux/routing/utils.cpp \
linux/routing/link/link.cpp \
linux/routing/filter/arp.cpp \
@@ -278,6 +279,7 @@ if WITH_NETWORK_ISOLATOR
libmesos_no_3rdparty_la_SOURCES += \
linux/routing/internal.hpp \
+ linux/routing/route.hpp \
linux/routing/utils.hpp \
linux/routing/filter/action.hpp \
linux/routing/filter/arp.hpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/19f643aa/src/linux/routing/route.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/route.cpp b/src/linux/routing/route.cpp
new file mode 100644
index 0000000..6e3ec0f
--- /dev/null
+++ b/src/linux/routing/route.cpp
@@ -0,0 +1,136 @@
+/**
+ * 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 <linux/rtnetlink.h>
+
+#include <netinet/in.h>
+
+#include <netlink/addr.h>
+#include <netlink/cache.h>
+#include <netlink/errno.h>
+#include <netlink/object.h>
+#include <netlink/socket.h>
+
+#include <netlink/route/route.h>
+
+#include <glog/logging.h>
+
+#include <stout/error.hpp>
+#include <stout/foreach.hpp>
+#include <stout/none.hpp>
+#include <stout/stringify.hpp>
+
+#include "linux/routing/internal.hpp"
+#include "linux/routing/route.hpp"
+
+#include "linux/routing/link/link.hpp"
+
+using std::string;
+using std::vector;
+
+namespace routing {
+namespace route {
+
+Try<vector<Rule> > table()
+{
+ Try<Netlink<struct nl_sock> > sock = routing::socket();
+ if (sock.isError()) {
+ return Error(sock.error());
+ }
+
+ // Dump all the routes (for IPv4) from kernel.
+ struct nl_cache* c = NULL;
+ int err = rtnl_route_alloc_cache(sock.get().get(), AF_INET, 0, &c);
+ if (err != 0) {
+ return Error(nl_geterror(err));
+ }
+
+ Netlink<struct nl_cache> cache(c);
+
+ vector<Rule> results;
+
+ // Scan the routes and look for entries in the main routing table.
+ for (struct nl_object* o = nl_cache_get_first(cache.get());
+ o != NULL; o = nl_cache_get_next(o)) {
+ struct rtnl_route* route = (struct rtnl_route*) o;
+
+ // TODO(jieyu): Currently, we assume each route in the routing
+ // table has only one hop (which is true in most environments).
+ if (rtnl_route_get_table(route) == RT_TABLE_MAIN &&
+ rtnl_route_get_nnexthops(route) == 1) {
+ CHECK_EQ(AF_INET, rtnl_route_get_family(route));
+
+ // Get the destination IP if exists.
+ Option<net::IP> destination;
+ struct nl_addr* dst = rtnl_route_get_dst(route);
+ if (dst != NULL && nl_addr_get_len(dst) != 0) {
+ struct in_addr* addr = (struct in_addr*) nl_addr_get_binary_addr(dst);
+
+ // Calculate the netmask based on the prefix length.
+ CHECK_GE(32u, nl_addr_get_prefixlen(dst));
+ uint32_t netmask = 0xffffffff << (32 - nl_addr_get_prefixlen(dst));
+
+ destination = net::IP(ntohl(addr->s_addr), netmask);
+ }
+
+ // Get the default gateway if exists.
+ Option<net::IP> gateway;
+ struct rtnl_nexthop* hop = rtnl_route_nexthop_n(route, 0);
+ struct nl_addr* gw = rtnl_route_nh_get_gateway(CHECK_NOTNULL(hop));
+ if (gw != NULL && nl_addr_get_len(gw) != 0) {
+ struct in_addr* addr = (struct in_addr*) nl_addr_get_binary_addr(gw);
+ gateway = net::IP(ntohl(addr->s_addr));
+ }
+
+ // Get the destination link.
+ int index = rtnl_route_nh_get_ifindex(hop);
+ Result<string> link = link::name(index);
+ if (link.isError()) {
+ return Error("Failed to get the link name: " + link.error());
+ } else if (link.isNone()) {
+ return Error("Link of index " + stringify(index) + " is not found");
+ }
+
+ results.push_back(Rule(destination, gateway, link.get()));
+ }
+ }
+
+ return results;
+}
+
+
+Result<net::IP> defaultGateway()
+{
+ Try<vector<Rule> > rules = table();
+ if (rules.isError()) {
+ return Error("Failed to get the routing table: " + rules.error());
+ }
+
+ foreach (const Rule& rule, rules.get()) {
+ if (rule.destination().isNone() && rule.gateway().isSome()) {
+ return rule.gateway().get();
+ }
+ }
+
+ return None();
+}
+
+} // namespace route
+} // namespace routing
http://git-wip-us.apache.org/repos/asf/mesos/blob/19f643aa/src/linux/routing/route.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/route.hpp b/src/linux/routing/route.hpp
new file mode 100644
index 0000000..435b981
--- /dev/null
+++ b/src/linux/routing/route.hpp
@@ -0,0 +1,65 @@
+/**
+ * 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_ROUTE_HPP__
+#define __LINUX_ROUTING_ROUTE_HPP__
+
+#include <string>
+#include <vector>
+
+#include <stout/net.hpp>
+#include <stout/option.hpp>
+#include <stout/result.hpp>
+#include <stout/try.hpp>
+
+namespace routing {
+namespace route {
+
+// Represents a rule in the routing table (for IPv4).
+class Rule
+{
+public:
+ Rule(const Option<net::IP>& _destination,
+ const Option<net::IP>& _gateway,
+ const std::string& _link)
+ : destination_(_destination),
+ gateway_(_gateway),
+ link_(_link) {}
+
+ const Option<net::IP>& destination() const { return destination_; }
+ const Option<net::IP>& gateway() const { return gateway_; }
+ const std::string& link() const { return link_; }
+
+private:
+ Option<net::IP> destination_;
+ Option<net::IP> gateway_;
+ std::string link_;
+};
+
+
+// Returns the main routing table of this host.
+Try<std::vector<Rule> > table();
+
+
+// Returns the default gateway of this host.
+Result<net::IP> defaultGateway();
+
+} // namespace route
+} // namespace routing
+
+#endif // __LINUX_ROUTING_ROUTE_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/19f643aa/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
index 5650fca..aca88dc 100644
--- a/src/tests/routing_tests.cpp
+++ b/src/tests/routing_tests.cpp
@@ -31,6 +31,7 @@
#include <stout/hashmap.hpp>
#include <stout/net.hpp>
+#include "linux/routing/route.hpp"
#include "linux/routing/utils.hpp"
#include "linux/routing/filter/arp.hpp"
@@ -128,6 +129,16 @@ TEST_F(RoutingTest, PortRange)
}
+TEST_F(RoutingTest, RouteTable)
+{
+ Try<vector<route::Rule> > table = route::table();
+ EXPECT_SOME(table);
+
+ Result<net::IP> gateway = route::defaultGateway();
+ EXPECT_FALSE(gateway.isError());
+}
+
+
TEST_F(RoutingTest, LinkIndex)
{
Try<set<string> > links = net::links();
[9/9] git commit: Added API for managing ARP packet filters.
Posted by ji...@apache.org.
Added API for managing ARP packet filters.
Review: https://reviews.apache.org/r/20296
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/336420c6
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/336420c6
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/336420c6
Branch: refs/heads/master
Commit: 336420c66516d09211ecbebf3310b45517e35577
Parents: 6d068b2
Author: Jie Yu <yu...@gmail.com>
Authored: Sun Apr 13 11:42: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/arp.cpp | 164 ++++++++++++++++++++++++++++++++++
src/linux/routing/filter/arp.hpp | 84 +++++++++++++++++
src/tests/routing_tests.cpp | 106 ++++++++++++++++++++++
4 files changed, 356 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/336420c6/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index c18ddf0..ddbd82b 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/arp.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/internal.hpp \
linux/routing/utils.hpp \
linux/routing/filter/action.hpp \
+ linux/routing/filter/arp.hpp \
linux/routing/filter/filter.hpp \
linux/routing/filter/icmp.hpp \
linux/routing/filter/internal.hpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/336420c6/src/linux/routing/filter/arp.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/arp.cpp b/src/linux/routing/filter/arp.cpp
new file mode 100644
index 0000000..3f37a83
--- /dev/null
+++ b/src/linux/routing/filter/arp.cpp
@@ -0,0 +1,164 @@
+/**
+ * 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 <linux/if_ether.h>
+
+#include <netlink/errno.h>
+
+#include <netlink/route/tc.h>
+
+#include <netlink/route/cls/basic.h>
+
+#include <stout/error.hpp>
+#include <stout/none.hpp>
+
+#include "linux/routing/internal.hpp"
+
+#include "linux/routing/filter/action.hpp"
+#include "linux/routing/filter/arp.hpp"
+#include "linux/routing/filter/filter.hpp"
+#include "linux/routing/filter/internal.hpp"
+#include "linux/routing/filter/priority.hpp"
+
+#include "linux/routing/queueing/handle.hpp"
+
+using std::string;
+
+namespace routing {
+namespace filter {
+namespace arp {
+
+// The classifier for ARP packet filters contains nothing and is not
+// exposed to the user as we don't care about the content in the ARP
+// packet.
+struct Classifier
+{
+ bool operator == (const Classifier& that) const
+ {
+ return true;
+ }
+};
+
+} // namespace arp {
+
+/////////////////////////////////////////////////
+// Filter specific pack/unpack functions.
+/////////////////////////////////////////////////
+
+namespace internal {
+
+// Encodes the ARP classifier into the libnl filter 'cls'. Each type
+// of classifier needs to implement this function.
+template <>
+Try<Nothing> encode<arp::Classifier>(
+ const Netlink<struct rtnl_cls>& cls,
+ const arp::Classifier& classifier)
+{
+ rtnl_cls_set_protocol(cls.get(), ETH_P_ARP);
+
+ int err = rtnl_tc_set_kind(TC_CAST(cls.get()), "basic");
+ if (err != 0) {
+ return Error(
+ "Failed to set the kind of the classifier: " +
+ string(nl_geterror(err)));
+ }
+
+ return Nothing();
+}
+
+// Decodes the ARP classifier from the libnl filter 'cls'. Each type
+// of classifier needs to implement this function. Returns None if the
+// libnl filter is not an ARP packet filter.
+template <>
+Result<arp::Classifier> decode<arp::Classifier>(
+ const Netlink<struct rtnl_cls>& cls)
+{
+ if (rtnl_cls_get_protocol(cls.get()) == ETH_P_ARP &&
+ rtnl_tc_get_kind(TC_CAST(cls.get())) == string("basic")) {
+ return arp::Classifier();
+ }
+
+ return None();
+}
+
+} // namespace internal {
+
+
+namespace arp {
+
+Try<bool> exists(const string& link, const queueing::Handle& parent)
+{
+ return internal::exists(link, parent, Classifier());
+}
+
+
+Try<bool> create(
+ const string& link,
+ const queueing::Handle& parent,
+ 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 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)
+{
+ return internal::remove(link, parent, Classifier());
+}
+
+
+Try<bool> update(
+ const string& link,
+ const queueing::Handle& parent,
+ const action::Mirror& mirror)
+{
+ return internal::update(
+ link,
+ Filter<Classifier>(
+ parent,
+ Classifier(),
+ None(),
+ mirror));
+}
+
+} // namespace arp {
+} // namespace filter {
+} // namespace routing {
http://git-wip-us.apache.org/repos/asf/mesos/blob/336420c6/src/linux/routing/filter/arp.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/filter/arp.hpp b/src/linux/routing/filter/arp.hpp
new file mode 100644
index 0000000..fa0ea6f
--- /dev/null
+++ b/src/linux/routing/filter/arp.hpp
@@ -0,0 +1,84 @@
+/**
+ * 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_ARP_HPP__
+#define __LINUX_ROUTING_FILTER_ARP_HPP__
+
+#include <string>
+
+#include <stout/option.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 arp {
+
+// Returns true if an ARP packet filter attached to the given parent
+// exists on the link.
+Try<bool> exists(const std::string& link, const queueing::Handle& parent);
+
+
+// Creates an ARP packet filter attached to the given parent on the
+// link which will redirect all ARP packets to the target link.
+// Returns false if an ARP packet filter attached to the given parent
+// already exists on the link. The user can choose to specify an
+// optional priority for the filter.
+Try<bool> create(
+ const std::string& link,
+ const queueing::Handle& parent,
+ const Option<Priority>& priority,
+ const action::Redirect& redirect);
+
+
+// Creates an ARP packet filter attached to the given parent on the
+// link which will mirror all ARP packets to a set of links (specified
+// in the mirror action). Returns false if an ARP packet filter
+// attached to the given parent already exists on the link. The user
+// can choose to specify an optional priority for the filter.
+Try<bool> create(
+ const std::string& link,
+ const queueing::Handle& parent,
+ const Option<Priority>& priority,
+ const action::Mirror& mirror);
+
+
+// Removes the ARP packet filter attached to the parent from the link.
+// Returns false if no ARP packet filter attached to the given parent
+// is found on the link.
+Try<bool> remove(const std::string& link, const queueing::Handle& parent);
+
+
+// Updates the action of the APR packet filter attached to the given
+// parent on the link. Returns false if no ARP packet filter attached
+// to the parent is found on the link.
+Try<bool> update(
+ const std::string& link,
+ const queueing::Handle& parent,
+ const action::Mirror& mirror);
+
+} // namespace arp {
+} // namespace filter {
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_FILTER_ARP_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/336420c6/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
index de6d9e3..cc3d6da 100644
--- a/src/tests/routing_tests.cpp
+++ b/src/tests/routing_tests.cpp
@@ -33,6 +33,7 @@
#include "linux/routing/utils.hpp"
+#include "linux/routing/filter/arp.hpp"
#include "linux/routing/filter/icmp.hpp"
#include "linux/routing/link/link.hpp"
@@ -292,6 +293,111 @@ TEST_F(RoutingVethTest, ROOT_LinkMTU)
}
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreate)
+{
+ 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(arp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(arp::exists(TEST_VETH_LINK, ingress::HANDLE));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreateDuplicated)
+{
+ 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(arp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ None(),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(arp::exists(TEST_VETH_LINK, ingress::HANDLE));
+
+ EXPECT_SOME_FALSE(arp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterRemove)
+{
+ 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(arp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ None(),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(arp::exists(TEST_VETH_LINK, ingress::HANDLE));
+ EXPECT_SOME_TRUE(arp::remove(TEST_VETH_LINK, ingress::HANDLE));
+ EXPECT_SOME_FALSE(arp::exists(TEST_VETH_LINK, ingress::HANDLE));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterUpdate)
+{
+ 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_FALSE(arp::update(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(arp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(arp::exists(TEST_VETH_LINK, ingress::HANDLE));
+
+ EXPECT_SOME_TRUE(arp::update(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(arp::exists(TEST_VETH_LINK, ingress::HANDLE));
+}
+
+
TEST_F(RoutingVethTest, ROOT_ICMPFilterCreate)
{
ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
[7/9] git commit: Added API for managing ICMP packet filters.
Posted by ji...@apache.org.
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)));
+}
[6/9] git commit: Allowed to get/set MTU for a link.
Posted by ji...@apache.org.
Allowed to get/set MTU for a link.
Review: https://reviews.apache.org/r/21288
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/d50f487e
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/d50f487e
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/d50f487e
Branch: refs/heads/master
Commit: d50f487e3bf010d7bb20695903e2a0b61c83c957
Parents: 27e458e
Author: Jie Yu <yu...@gmail.com>
Authored: Fri May 9 14:45:48 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed May 14 17:38:43 2014 -0700
----------------------------------------------------------------------
src/linux/routing/link/link.cpp | 51 ++++++++++++++++++++++++++++++++++++
src/linux/routing/link/link.hpp | 10 +++++++
src/tests/routing_tests.cpp | 18 +++++++++++++
3 files changed, 79 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/d50f487e/src/linux/routing/link/link.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/link/link.cpp b/src/linux/routing/link/link.cpp
index 97d03bf..62996bd 100644
--- a/src/linux/routing/link/link.cpp
+++ b/src/linux/routing/link/link.cpp
@@ -200,6 +200,57 @@ Try<bool> setMAC(const string& link, const net::MAC& mac)
}
+Result<unsigned int> mtu(const string& _link)
+{
+ Result<Netlink<struct rtnl_link> > link = internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return None();
+ }
+
+ return rtnl_link_get_mtu(link.get().get());
+}
+
+
+Try<bool> setMTU(const string& _link, unsigned int mtu)
+{
+ Result<Netlink<struct rtnl_link> > link = internal::get(_link);
+ if (link.isError()) {
+ return Error(link.error());
+ } else if (link.isNone()) {
+ return false;
+ }
+
+ // TODO(jieyu): We use ioctl to set the MTU because libnl has some
+ // issues with rtnl_link_change.
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+
+ strncpy(ifr.ifr_name, _link.c_str(), IFNAMSIZ);
+ ifr.ifr_mtu = mtu;
+
+ int fd = ::socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ return ErrnoError();
+ }
+
+ if (ioctl(fd, SIOCSIFMTU, &ifr) == -1) {
+ if (errno == ENODEV) {
+ os::close(fd);
+ return false;
+ }
+
+ // Save the error string as os::close may overwrite errno.
+ const string message = strerror(errno);
+ os::close(fd);
+ return Error(message);
+ }
+
+ return true;
+}
+
+
Result<hashmap<string, uint64_t> > statistics(const string& _link)
{
Result<Netlink<struct rtnl_link> > link = internal::get(_link);
http://git-wip-us.apache.org/repos/asf/mesos/blob/d50f487e/src/linux/routing/link/link.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/link/link.hpp b/src/linux/routing/link/link.hpp
index fe19ff2..ef982e3 100644
--- a/src/linux/routing/link/link.hpp
+++ b/src/linux/routing/link/link.hpp
@@ -77,6 +77,16 @@ Try<bool> setUp(const std::string& link);
Try<bool> setMAC(const std::string& link, const net::MAC& mac);
+// Returns the Maximum Transmission Unit (MTU) of the link. Returns
+// None if the link is not found.
+Result<unsigned int> mtu(const std::string& link);
+
+
+// Sets the Maximum Transmission Unit (MTU) of the link. Returns false
+// if the link is not found.
+Try<bool> setMTU(const std::string& link, unsigned int mtu);
+
+
// Returns the statistics of the link.
Result<hashmap<std::string, uint64_t> > statistics(const std::string& link);
http://git-wip-us.apache.org/repos/asf/mesos/blob/d50f487e/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
index 8dfc23e..c95dcec 100644
--- a/src/tests/routing_tests.cpp
+++ b/src/tests/routing_tests.cpp
@@ -247,3 +247,21 @@ TEST_F(RoutingVethTest, ROOT_LinkSetMAC)
EXPECT_ERROR(link::setMAC(TEST_VETH_LINK, net::MAC(multicast)));
}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkMTU)
+{
+ 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));
+
+ EXPECT_SOME_TRUE(link::setMTU(TEST_VETH_LINK, 10000));
+
+ Result<unsigned int> mtu = link::mtu(TEST_VETH_LINK);
+ ASSERT_SOME(mtu);
+ EXPECT_EQ(10000u, mtu.get());
+
+ EXPECT_NONE(link::mtu("not-exist"));
+ EXPECT_SOME_FALSE(link::setMTU("not-exist", 1500));
+}
[3/9] git commit: Added runtime check to the Linux routing library.
Posted by ji...@apache.org.
Added runtime check to the Linux routing library.
Review: https://reviews.apache.org/r/21269
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/b99402f6
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/b99402f6
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/b99402f6
Branch: refs/heads/master
Commit: b99402f6a1c1b7474500d36e461bfda878ea9612
Parents: d50f487
Author: Jie Yu <yu...@gmail.com>
Authored: Fri May 9 10:33:53 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed May 14 17:38:43 2014 -0700
----------------------------------------------------------------------
src/Makefile.am | 2 ++
src/linux/routing/internal.hpp | 7 ++++++
src/linux/routing/utils.cpp | 46 +++++++++++++++++++++++++++++++++++++
src/linux/routing/utils.hpp | 35 ++++++++++++++++++++++++++++
src/tests/routing_tests.cpp | 19 ++++++++++++++-
5 files changed, 108 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/b99402f6/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index ab5df98..debc77a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -268,10 +268,12 @@ endif
if WITH_NETWORK_ISOLATOR
libmesos_no_3rdparty_la_SOURCES += \
+ linux/routing/utils.cpp \
linux/routing/link/link.cpp
libmesos_no_3rdparty_la_SOURCES += \
linux/routing/internal.hpp \
+ linux/routing/utils.hpp \
linux/routing/link/internal.hpp \
linux/routing/link/link.hpp
endif
http://git-wip-us.apache.org/repos/asf/mesos/blob/b99402f6/src/linux/routing/internal.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/internal.hpp b/src/linux/routing/internal.hpp
index fa1a6ee..be117da 100644
--- a/src/linux/routing/internal.hpp
+++ b/src/linux/routing/internal.hpp
@@ -31,6 +31,8 @@
#include <stout/memory.hpp>
#include <stout/try.hpp>
+#include "linux/routing/utils.hpp"
+
namespace routing {
// A helper class for managing netlink objects (e.g., rtnl_link,
@@ -79,6 +81,11 @@ inline void cleanup(struct rtnl_qdisc* qdisc) { rtnl_qdisc_put(qdisc); }
// socket is needed for most of the operations.
inline Try<Netlink<struct nl_sock> > socket()
{
+ Try<Nothing> checking = check();
+ if (checking.isError()) {
+ return Error(checking.error());
+ }
+
struct nl_sock* s = nl_socket_alloc();
if (s == NULL) {
return Error("Failed to allocate netlink socket");
http://git-wip-us.apache.org/repos/asf/mesos/blob/b99402f6/src/linux/routing/utils.cpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/utils.cpp b/src/linux/routing/utils.cpp
new file mode 100644
index 0000000..80b62d0
--- /dev/null
+++ b/src/linux/routing/utils.cpp
@@ -0,0 +1,46 @@
+/**
+ * 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 <netlink/utils.h>
+
+#include "linux/routing/utils.hpp"
+
+namespace routing {
+
+Try<Nothing> check()
+{
+ // As advised in libnl, we use numeric values, instead of defined
+ // macros (which creates compile time dependency), to check
+ // capabilities.
+
+ // Check NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE.
+ if (nl_has_capability(2) == 0) {
+ return Error(
+ "Capability ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE is not available");
+ }
+
+ // Check NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE.
+ if (nl_has_capability(3) == 0) {
+ return Error(
+ "Capability ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE is not available");
+ }
+
+ return Nothing();
+}
+
+} // namespace routing {
http://git-wip-us.apache.org/repos/asf/mesos/blob/b99402f6/src/linux/routing/utils.hpp
----------------------------------------------------------------------
diff --git a/src/linux/routing/utils.hpp b/src/linux/routing/utils.hpp
new file mode 100644
index 0000000..a3d914a
--- /dev/null
+++ b/src/linux/routing/utils.hpp
@@ -0,0 +1,35 @@
+/**
+ * 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_UTILS_HPP__
+#define __LINUX_ROUTING_UTILS_HPP__
+
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+
+namespace routing {
+
+// Checks the capabilities of the underlying libraries. Returns error
+// if the underlying libraries are not new enough to fully support the
+// functionalities in this library. This check is typically performed
+// before any library function is invoked.
+Try<Nothing> check();
+
+} // namespace routing {
+
+#endif // __LINUX_ROUTING_UTILS_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/b99402f6/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
index c95dcec..7ba826f 100644
--- a/src/tests/routing_tests.cpp
+++ b/src/tests/routing_tests.cpp
@@ -31,10 +31,13 @@
#include <stout/hashmap.hpp>
#include <stout/net.hpp>
+#include "linux/routing/utils.hpp"
+
#include "linux/routing/link/link.hpp"
using namespace routing;
+using std::endl;
using std::set;
using std::string;
@@ -43,7 +46,19 @@ static const string TEST_VETH_LINK = "veth-test";
static const string TEST_PEER_LINK = "veth-peer";
-class RoutingTest : public ::testing::Test {};
+class RoutingTest : public ::testing::Test
+{
+protected:
+ virtual void SetUp()
+ {
+ ASSERT_SOME(routing::check())
+ << "-------------------------------------------------------------\n"
+ << "We cannot run any routing tests because your libnl\n"
+ << "library is not new enough. You can either install a\n"
+ << "new libnl library, or disable this test case\n"
+ << "-------------------------------------------------------------";
+ }
+};
// Tests that require setting up virtual ethernet on host.
@@ -52,6 +67,8 @@ class RoutingVethTest : public RoutingTest
protected:
virtual void SetUp()
{
+ RoutingTest::SetUp();
+
// Clean up the test links, in case it wasn't cleaned up properly
// from previous tests.
link::remove(TEST_VETH_LINK);