Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 62 additions & 19 deletions api/net/router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,41 @@ namespace net {
using Addr = typename IPV::addr;
using Netmask = typename IPV::addr;

Addr dest_net()
{ return dest_net_; }
Addr net() const noexcept
{ return net_; }

Netmask netmask()
Netmask netmask() const noexcept
{ return netmask_; }

Addr gateway()
Addr gateway() const noexcept
{ return gateway_; }

int cost()
int cost() const noexcept
{ return cost_; }

Stack_ptr interface()
Stack_ptr interface() const noexcept
{ return iface_; };

Route(Addr dest_net, Netmask mask, Addr gateway, Stack& iface, int cost)
: dest_net_{dest_net}, netmask_{mask}, gateway_{gateway}, iface_{&iface}, cost_{cost}
Stack_ptr match(typename IPV::addr dest) const noexcept
{ return (dest & netmask_) == net_ ? iface_ : nullptr; }

bool operator<(const Route& b) const
{ return cost() < b.cost(); }

bool operator==(const Route& b) const
{
return net_ == b.net() and
netmask_ == b.netmask() and
cost_ == b.cost() and
iface_ == b.interface();
}

Route(Addr net, Netmask mask, Addr gateway, Stack& iface, int cost)
: net_{net}, netmask_{mask}, gateway_{gateway}, iface_{&iface}, cost_{cost}
{}

private:
Addr dest_net_;
Addr net_;
Netmask netmask_;
Addr gateway_;
Stack_ptr iface_;
Expand Down Expand Up @@ -82,33 +96,62 @@ namespace net {
{ return Forward_delg(*this, forward); }


/** Get the interface route for a certain IP **/
virtual Stack_ptr get_interface(typename IPV::addr dest) {
/** Get any interface route for a certain IP **/
Route<IPV>* get_first_route(typename IPV::addr dest) {

for (auto&& route : routing_table_) {
if ((dest & route.netmask()) == route.dest_net())
return route.interface();
Stack_ptr match = route.match(dest);
if (match) return &route;
}

return nullptr;
};

/** Get any interface route for a certain IP **/
Stack_ptr get_first_interface(typename IPV::addr dest) {
auto route = get_first_route(dest);
if (route) return route->interface();
return nullptr;
};

/** Check if there exists a route for a given IP **/
bool route_check(typename IPV::addr dest){
return get_interface(dest);
return get_first_interface(dest) != nullptr;
}


/**
* Get all routes for a certain IP
* @todo : Optimize!
**/
Routing_table get_all_routes(typename IPV::addr dest) {

Routing_table t;
std::copy_if(routing_table_.begin(),
routing_table_.end(),
std::back_inserter(t), [dest](const Route<IPV>& route) {
return route.match(dest);
});
return t;
}

/**
* Get cheapest route for a certain IP
* @todo : Optimize!
**/
Route<IPV>* get_cheapest_route(typename IPV::addr dest) {
Routing_table all = get_all_routes(dest);
std::sort(all.begin(), all.end());
if (not all.empty()) return &all.front();
return nullptr;
};


/** Construct a router over a set of interfaces **/
Router(Interfaces& ifaces, Routing_table&& tbl = {})
Router(Interfaces& ifaces, Routing_table tbl = {})
: networks_{ifaces}, routing_table_{tbl}
{ }

void set_routing_table(Routing_table&& tbl) {
routing_table_ = std::forward(tbl);
};

void set_routing_table(Routing_table tbl) {
routing_table_ = tbl;
};
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ set(TEST_SOURCES
${TEST}/net/unit/tcp_packet_test.cpp
${TEST}/net/unit/tcp_socket.cpp
${TEST}/net/unit/tcp_write_queue.cpp
${TEST}/net/unit/router.cpp
${TEST}/posix/unit/fd_map_test.cpp
${TEST}/posix/unit/inet_test.cpp
${TEST}/util/unit/base64.cpp
Expand Down
19 changes: 19 additions & 0 deletions test/net/integration/router/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 2.8.9)

# IncludeOS install location
if (NOT DEFINED ENV{INCLUDEOS_PREFIX})
set(ENV{INCLUDEOS_PREFIX} /usr/local)
endif()

set(CMAKE_TOOLCHAIN_FILE $ENV{INCLUDEOS_PREFIX}/includeos/i686-elf-toolchain.cmake)

project (test_udp)

set(SERVICE_NAME "Routing test service")
set(BINARY "test_router")
set(MAX_MEM 128)
set(SOURCES service.cpp)
set(DRIVERS virtionet) #vmxnet3

# include service build script
include($ENV{INCLUDEOS_PREFIX}/includeos/service.cmake)
25 changes: 25 additions & 0 deletions test/net/integration/router/diagram.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
+
+--------------------+ Network Namespace 0 | Network Namespace 1
| | |
| IncludeOS | |
| router | |
| | |
| eth0 eth1 | |
+-------------------+ +--+--------------+--+ +-------------------+ +------------+ | +------------+
| | ^ | | | | | | | |
| Bridge43 (Linux) +-------+ +------>+ Bridge44 (Linux) +------>+ veth0 +------------>+ veth1 |
| TAP device | | TAP device | | (NO IP) | | | 10.42.42.2 |
| 10.0.0.1 | | (NO IP) | | | | | |
| | | | +------------+ | +-----+------+
+--------+----------+ +-------------------+ | |
^ | |
| | |
| | |
$ nc 10.42.42.2 -u 4242 +---+ | +--> $ nc -u -l 10.42.42.2 4242
|
|
|
|
|
+

123 changes: 123 additions & 0 deletions test/net/integration/router/service.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed 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.

// #define DEBUG // Debug supression
// #define NO_INFO

#include <os>
#include <kernel/irq_manager.hpp>
#include <list>
#include <net/inet4>
#include <net/router.hpp>
#include <timers>
#include <profile>

#define USE_STACK_SAMPLING

using namespace net;
using namespace std::chrono;

std::unique_ptr<Router<IP4> > router;

bool route_checker(IP4::addr addr) {
INFO("Route checker", "asked for route to IP %s", addr.to_string().c_str());

bool have_route = router->route_check(addr);

INFO("Route checker", "The router says %i", have_route);

if (have_route)
INFO2("* Responding YES");
else
INFO2("* Responding NO");

return have_route;
}

void ip_forward (Inet<IP4>& stack, IP4::IP_packet_ptr pckt) {

// Packet could have been erroneously moved prior to this call
if (not pckt)
return;

Inet<IP4>* route = router->get_first_interface(pckt->dst());

if (not route){
INFO("ip_fwd", "No route found for %s dropping\n", pckt->dst().to_string().c_str());
return;
}

if (route == &stack) {
INFO("ip_fwd", "* Oh, this packet was for me, sow why was it forwarded here? \n");
return;
}

debug("ip_fwd %s transmitting packet to %s", ifname, route->ifname().c_str());
route->ip_obj().ship(std::move(pckt));
}


void Service::start(const std::string&)
{
auto& inet = Inet4::stack<0>();
inet.network_config({ 10, 0, 0, 42 }, // IP
{ 255, 255, 0, 0 }, // Netmask
{ 10, 0, 0, 1 } ); // Gateway

INFO("Router","Interface 1 IP: %s\n", inet.ip_addr().str().c_str());

auto& inet2 = Inet4::stack<1>();
inet2.network_config({ 10, 42, 42, 43 }, // IP
{ 255, 255, 255, 0 }, // Netmask
{ 10, 42, 42, 2 } ); // Gateway

INFO("Router","Interface2 IP: %s\n", inet2.ip_addr().str().c_str());


// IP Forwarding
inet.ip_obj().set_packet_forwarding(ip_forward);
inet2.ip_obj().set_packet_forwarding(ip_forward);

// ARP Route checker
inet.set_route_checker(route_checker);
inet2.set_route_checker(route_checker);


/** Some times it's nice to add dest. to arp-cache to avoid having it respond to arp */
// inet2.cache_link_ip({10,42,42,2}, {0x10,0x11, 0x12, 0x13, 0x14, 0x15});
// inet2.cache_link_ip({10,42,42,2}, {0x1e,0x5f,0x30,0x98,0x19,0x8b});
// inet2.cache_link_ip({10,42,42,2}, {0xc0,0x00, 0x10, 0x00, 0x00, 0x02});

// Routing table
Router<IP4>::Routing_table routing_table{
{{10, 42, 42, 0 }, { 255, 255, 255, 0}, {10, 42, 42, 2}, inet2 , 1 },
{{10, 0, 0, 0 }, { 255, 255, 255, 0}, {10, 0, 0, 1}, inet , 1 }
};

router = std::make_unique<Router<IP4>>(Super_stack::inet().ip4_stacks(), routing_table);

INFO("Router", "Routing enabled - routing table:");

for (auto r : routing_table)
INFO2("* %s/%i -> %s / %s, cost %i", r.net().str().c_str(),
__builtin_popcount(r.netmask().whole),
r.interface()->ifname().c_str(),
r.gateway().str().c_str(),
r.cost());
printf("\n");
INFO("Router","Service ready");
}
70 changes: 70 additions & 0 deletions test/net/integration/router/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#! /bin/bash
source_net=10.0.0.0/24
source_bridge=bridge43

dest_net=10.42.42.0/24
dest_bridge=bridge44
dest_gateway=10.42.42.2


export NSNAME="server1"
shopt -s expand_aliases
alias server1="sudo ip netns exec $NSNAME"

setup() {

# TODO: it's probably not nice to install test deps here
sudo apt install -y iperf3

# Make sure the default bridge exists
$INCLUDEOS_PREFIX/includeos/scripts/create_bridge.sh

# Create veth link
sudo ip link add veth_src type veth peer name veth_dest

# Bring up source end
sudo ip link set veth_src up

# Add network namespace
sudo ip netns add $NSNAME

# Add destination to namespace
sudo ip link set veth_dest netns $NSNAME

# Bring up destination end, with IP, inside namespace
server1 ip addr add $dest_gateway/24 dev veth_dest
server1 ip link set veth_dest up
server1 ip link set lo up

# Create a second bridge and bring it up, no IP
sudo brctl addbr $dest_bridge
sudo ip link set dev $dest_bridge up

# Add source end to bridge44
sudo brctl addif $dest_bridge veth_src

# Route all traffic to the isolated network via bridge43
sudo ip route add $dest_net dev $source_bridge

# Route all traffic from server1 back to root namespace, via veth_dest
server1 sudo ip route add $source_net via $dest_gateway

}

undo(){
echo ">>> Deleting $dest_bridge"
sudo ip link set $dest_bridge down
sudo brctl delbr $dest_bridge
echo ">>> Deleting namespace and veth pair"
sudo ip netns del $NSNAME
echo ">>> Deleting route to namespace"
sudo ip route del $dest_net dev $source_bridge
}


if [ "$1" == "--clean" ]
then
undo
else
setup
fi
Loading