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 <yujie....@gmail.com>
Authored: Sun Apr 13 15:52:38 2014 -0700
Committer: Jie Yu <yujie....@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());
+}

Reply via email to