Skip to content

Commit a4cacea

Browse files
authored
Merge pull request #1209 from alfred-bratterud/dev
Router: more functionality and tests
2 parents 1799b7c + a6944f8 commit a4cacea

9 files changed

Lines changed: 485 additions & 19 deletions

File tree

api/net/router.hpp

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,41 @@ namespace net {
3131
using Addr = typename IPV::addr;
3232
using Netmask = typename IPV::addr;
3333

34-
Addr dest_net()
35-
{ return dest_net_; }
34+
Addr net() const noexcept
35+
{ return net_; }
3636

37-
Netmask netmask()
37+
Netmask netmask() const noexcept
3838
{ return netmask_; }
3939

40-
Addr gateway()
40+
Addr gateway() const noexcept
4141
{ return gateway_; }
4242

43-
int cost()
43+
int cost() const noexcept
4444
{ return cost_; }
4545

46-
Stack_ptr interface()
46+
Stack_ptr interface() const noexcept
4747
{ return iface_; };
4848

49-
Route(Addr dest_net, Netmask mask, Addr gateway, Stack& iface, int cost)
50-
: dest_net_{dest_net}, netmask_{mask}, gateway_{gateway}, iface_{&iface}, cost_{cost}
49+
Stack_ptr match(typename IPV::addr dest) const noexcept
50+
{ return (dest & netmask_) == net_ ? iface_ : nullptr; }
51+
52+
bool operator<(const Route& b) const
53+
{ return cost() < b.cost(); }
54+
55+
bool operator==(const Route& b) const
56+
{
57+
return net_ == b.net() and
58+
netmask_ == b.netmask() and
59+
cost_ == b.cost() and
60+
iface_ == b.interface();
61+
}
62+
63+
Route(Addr net, Netmask mask, Addr gateway, Stack& iface, int cost)
64+
: net_{net}, netmask_{mask}, gateway_{gateway}, iface_{&iface}, cost_{cost}
5165
{}
5266

5367
private:
54-
Addr dest_net_;
68+
Addr net_;
5569
Netmask netmask_;
5670
Addr gateway_;
5771
Stack_ptr iface_;
@@ -82,33 +96,62 @@ namespace net {
8296
{ return Forward_delg(*this, forward); }
8397

8498

85-
/** Get the interface route for a certain IP **/
86-
virtual Stack_ptr get_interface(typename IPV::addr dest) {
99+
/** Get any interface route for a certain IP **/
100+
Route<IPV>* get_first_route(typename IPV::addr dest) {
87101

88102
for (auto&& route : routing_table_) {
89-
if ((dest & route.netmask()) == route.dest_net())
90-
return route.interface();
103+
Stack_ptr match = route.match(dest);
104+
if (match) return &route;
91105
}
92106

93107
return nullptr;
108+
};
94109

110+
/** Get any interface route for a certain IP **/
111+
Stack_ptr get_first_interface(typename IPV::addr dest) {
112+
auto route = get_first_route(dest);
113+
if (route) return route->interface();
114+
return nullptr;
95115
};
96116

117+
/** Check if there exists a route for a given IP **/
97118
bool route_check(typename IPV::addr dest){
98-
return get_interface(dest);
119+
return get_first_interface(dest) != nullptr;
99120
}
100121

101122

123+
/**
124+
* Get all routes for a certain IP
125+
* @todo : Optimize!
126+
**/
127+
Routing_table get_all_routes(typename IPV::addr dest) {
128+
129+
Routing_table t;
130+
std::copy_if(routing_table_.begin(),
131+
routing_table_.end(),
132+
std::back_inserter(t), [dest](const Route<IPV>& route) {
133+
return route.match(dest);
134+
});
135+
return t;
136+
}
137+
138+
/**
139+
* Get cheapest route for a certain IP
140+
* @todo : Optimize!
141+
**/
142+
Route<IPV>* get_cheapest_route(typename IPV::addr dest) {
143+
Routing_table all = get_all_routes(dest);
144+
std::sort(all.begin(), all.end());
145+
if (not all.empty()) return &all.front();
146+
return nullptr;
147+
};
148+
102149

103150
/** Construct a router over a set of interfaces **/
104-
Router(Interfaces& ifaces, Routing_table&& tbl = {})
151+
Router(Interfaces& ifaces, Routing_table tbl = {})
105152
: networks_{ifaces}, routing_table_{tbl}
106153
{ }
107154

108-
void set_routing_table(Routing_table&& tbl) {
109-
routing_table_ = std::forward(tbl);
110-
};
111-
112155
void set_routing_table(Routing_table tbl) {
113156
routing_table_ = tbl;
114157
};

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ set(TEST_SOURCES
6666
${TEST}/net/unit/tcp_packet_test.cpp
6767
${TEST}/net/unit/tcp_socket.cpp
6868
${TEST}/net/unit/tcp_write_queue.cpp
69+
${TEST}/net/unit/router.cpp
6970
${TEST}/posix/unit/fd_map_test.cpp
7071
${TEST}/posix/unit/inet_test.cpp
7172
${TEST}/util/unit/base64.cpp
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required(VERSION 2.8.9)
2+
3+
# IncludeOS install location
4+
if (NOT DEFINED ENV{INCLUDEOS_PREFIX})
5+
set(ENV{INCLUDEOS_PREFIX} /usr/local)
6+
endif()
7+
8+
set(CMAKE_TOOLCHAIN_FILE $ENV{INCLUDEOS_PREFIX}/includeos/i686-elf-toolchain.cmake)
9+
10+
project (test_udp)
11+
12+
set(SERVICE_NAME "Routing test service")
13+
set(BINARY "test_router")
14+
set(MAX_MEM 128)
15+
set(SOURCES service.cpp)
16+
set(DRIVERS virtionet) #vmxnet3
17+
18+
# include service build script
19+
include($ENV{INCLUDEOS_PREFIX}/includeos/service.cmake)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
+
2+
+--------------------+ Network Namespace 0 | Network Namespace 1
3+
| | |
4+
| IncludeOS | |
5+
| router | |
6+
| | |
7+
| eth0 eth1 | |
8+
+-------------------+ +--+--------------+--+ +-------------------+ +------------+ | +------------+
9+
| | ^ | | | | | | | |
10+
| Bridge43 (Linux) +-------+ +------>+ Bridge44 (Linux) +------>+ veth0 +------------>+ veth1 |
11+
| TAP device | | TAP device | | (NO IP) | | | 10.42.42.2 |
12+
| 10.0.0.1 | | (NO IP) | | | | | |
13+
| | | | +------------+ | +-----+------+
14+
+--------+----------+ +-------------------+ | |
15+
^ | |
16+
| | |
17+
| | |
18+
$ nc 10.42.42.2 -u 4242 +---+ | +--> $ nc -u -l 10.42.42.2 4242
19+
|
20+
|
21+
|
22+
|
23+
|
24+
+
25+
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// This file is a part of the IncludeOS unikernel - www.includeos.org
2+
//
3+
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
4+
// and Alfred Bratterud
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
// #define DEBUG // Debug supression
19+
// #define NO_INFO
20+
21+
#include <os>
22+
#include <kernel/irq_manager.hpp>
23+
#include <list>
24+
#include <net/inet4>
25+
#include <net/router.hpp>
26+
#include <timers>
27+
#include <profile>
28+
29+
#define USE_STACK_SAMPLING
30+
31+
using namespace net;
32+
using namespace std::chrono;
33+
34+
std::unique_ptr<Router<IP4> > router;
35+
36+
bool route_checker(IP4::addr addr) {
37+
INFO("Route checker", "asked for route to IP %s", addr.to_string().c_str());
38+
39+
bool have_route = router->route_check(addr);
40+
41+
INFO("Route checker", "The router says %i", have_route);
42+
43+
if (have_route)
44+
INFO2("* Responding YES");
45+
else
46+
INFO2("* Responding NO");
47+
48+
return have_route;
49+
}
50+
51+
void ip_forward (Inet<IP4>& stack, IP4::IP_packet_ptr pckt) {
52+
53+
// Packet could have been erroneously moved prior to this call
54+
if (not pckt)
55+
return;
56+
57+
Inet<IP4>* route = router->get_first_interface(pckt->dst());
58+
59+
if (not route){
60+
INFO("ip_fwd", "No route found for %s dropping\n", pckt->dst().to_string().c_str());
61+
return;
62+
}
63+
64+
if (route == &stack) {
65+
INFO("ip_fwd", "* Oh, this packet was for me, sow why was it forwarded here? \n");
66+
return;
67+
}
68+
69+
debug("ip_fwd %s transmitting packet to %s", ifname, route->ifname().c_str());
70+
route->ip_obj().ship(std::move(pckt));
71+
}
72+
73+
74+
void Service::start(const std::string&)
75+
{
76+
auto& inet = Inet4::stack<0>();
77+
inet.network_config({ 10, 0, 0, 42 }, // IP
78+
{ 255, 255, 0, 0 }, // Netmask
79+
{ 10, 0, 0, 1 } ); // Gateway
80+
81+
INFO("Router","Interface 1 IP: %s\n", inet.ip_addr().str().c_str());
82+
83+
auto& inet2 = Inet4::stack<1>();
84+
inet2.network_config({ 10, 42, 42, 43 }, // IP
85+
{ 255, 255, 255, 0 }, // Netmask
86+
{ 10, 42, 42, 2 } ); // Gateway
87+
88+
INFO("Router","Interface2 IP: %s\n", inet2.ip_addr().str().c_str());
89+
90+
91+
// IP Forwarding
92+
inet.ip_obj().set_packet_forwarding(ip_forward);
93+
inet2.ip_obj().set_packet_forwarding(ip_forward);
94+
95+
// ARP Route checker
96+
inet.set_route_checker(route_checker);
97+
inet2.set_route_checker(route_checker);
98+
99+
100+
/** Some times it's nice to add dest. to arp-cache to avoid having it respond to arp */
101+
// inet2.cache_link_ip({10,42,42,2}, {0x10,0x11, 0x12, 0x13, 0x14, 0x15});
102+
// inet2.cache_link_ip({10,42,42,2}, {0x1e,0x5f,0x30,0x98,0x19,0x8b});
103+
// inet2.cache_link_ip({10,42,42,2}, {0xc0,0x00, 0x10, 0x00, 0x00, 0x02});
104+
105+
// Routing table
106+
Router<IP4>::Routing_table routing_table{
107+
{{10, 42, 42, 0 }, { 255, 255, 255, 0}, {10, 42, 42, 2}, inet2 , 1 },
108+
{{10, 0, 0, 0 }, { 255, 255, 255, 0}, {10, 0, 0, 1}, inet , 1 }
109+
};
110+
111+
router = std::make_unique<Router<IP4>>(Super_stack::inet().ip4_stacks(), routing_table);
112+
113+
INFO("Router", "Routing enabled - routing table:");
114+
115+
for (auto r : routing_table)
116+
INFO2("* %s/%i -> %s / %s, cost %i", r.net().str().c_str(),
117+
__builtin_popcount(r.netmask().whole),
118+
r.interface()->ifname().c_str(),
119+
r.gateway().str().c_str(),
120+
r.cost());
121+
printf("\n");
122+
INFO("Router","Service ready");
123+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#! /bin/bash
2+
source_net=10.0.0.0/24
3+
source_bridge=bridge43
4+
5+
dest_net=10.42.42.0/24
6+
dest_bridge=bridge44
7+
dest_gateway=10.42.42.2
8+
9+
10+
export NSNAME="server1"
11+
shopt -s expand_aliases
12+
alias server1="sudo ip netns exec $NSNAME"
13+
14+
setup() {
15+
16+
# TODO: it's probably not nice to install test deps here
17+
sudo apt install -y iperf3
18+
19+
# Make sure the default bridge exists
20+
$INCLUDEOS_PREFIX/includeos/scripts/create_bridge.sh
21+
22+
# Create veth link
23+
sudo ip link add veth_src type veth peer name veth_dest
24+
25+
# Bring up source end
26+
sudo ip link set veth_src up
27+
28+
# Add network namespace
29+
sudo ip netns add $NSNAME
30+
31+
# Add destination to namespace
32+
sudo ip link set veth_dest netns $NSNAME
33+
34+
# Bring up destination end, with IP, inside namespace
35+
server1 ip addr add $dest_gateway/24 dev veth_dest
36+
server1 ip link set veth_dest up
37+
server1 ip link set lo up
38+
39+
# Create a second bridge and bring it up, no IP
40+
sudo brctl addbr $dest_bridge
41+
sudo ip link set dev $dest_bridge up
42+
43+
# Add source end to bridge44
44+
sudo brctl addif $dest_bridge veth_src
45+
46+
# Route all traffic to the isolated network via bridge43
47+
sudo ip route add $dest_net dev $source_bridge
48+
49+
# Route all traffic from server1 back to root namespace, via veth_dest
50+
server1 sudo ip route add $source_net via $dest_gateway
51+
52+
}
53+
54+
undo(){
55+
echo ">>> Deleting $dest_bridge"
56+
sudo ip link set $dest_bridge down
57+
sudo brctl delbr $dest_bridge
58+
echo ">>> Deleting namespace and veth pair"
59+
sudo ip netns del $NSNAME
60+
echo ">>> Deleting route to namespace"
61+
sudo ip route del $dest_net dev $source_bridge
62+
}
63+
64+
65+
if [ "$1" == "--clean" ]
66+
then
67+
undo
68+
else
69+
setup
70+
fi

0 commit comments

Comments
 (0)