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