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