Skip to content

Commit a4bd1fc

Browse files
committed
test: added router integration test (intrusive)
1 parent 95c6d0d commit a4bd1fc

6 files changed

Lines changed: 313 additions & 0 deletions

File tree

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 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
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#! /usr/bin/env python
2+
3+
import sys
4+
import os
5+
import subprocess
6+
import thread
7+
8+
includeos_src = os.environ.get('INCLUDEOS_SRC',
9+
os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))).split('/test')[0])
10+
print 'includeos_src: {0}'.format(includeos_src)
11+
sys.path.insert(0,includeos_src)
12+
13+
from vmrunner import vmrunner
14+
15+
if1 = "tap0"
16+
if2 = "tap1"
17+
br1 = "bridge43"
18+
br2 = "bridge44"
19+
20+
iperf_cmd = "iperf3"
21+
iperf_server_proc = None
22+
transmit_size = "100M"
23+
24+
nsname="server1"
25+
26+
def move_tap1(o):
27+
print "Moving",if2, "to", br2
28+
subprocess.call(["sudo", "brctl", "delif", br1, if2])
29+
subprocess.call(["sudo", "brctl", "addif", br2, if2])
30+
31+
32+
def clean():
33+
subprocess.call(["sudo","pkill",iperf_cmd])
34+
subprocess.call(["./setup.sh", "--clean"])
35+
36+
37+
def iperf_server():
38+
global iperf_server_proc, iperf_srv_log
39+
iperf_server_proc = subprocess.Popen(["sudo","ip","netns","exec", nsname, iperf_cmd, "-s"],
40+
stdout = subprocess.PIPE,
41+
stdin = subprocess.PIPE,
42+
stderr = subprocess.PIPE)
43+
44+
def iperf_client(o):
45+
print "Starting iperf client. Iperf output: "
46+
print subprocess.check_output([iperf_cmd,"-c","10.42.42.2","-n", transmit_size])
47+
vmrunner.vms[0].exit(0, "Test completed without errors")
48+
return True
49+
50+
51+
subprocess.call("./setup.sh")
52+
53+
vm = vmrunner.vms[0]
54+
55+
# Move second interface to second bridge, right after boot
56+
# TODO: Add support for per-interface qemu-ifup scripts instead?
57+
vm.on_output("#include<os>", move_tap1)
58+
59+
60+
# Start iperf server right away, client when vm is up
61+
thread.start_new_thread(iperf_server, ())
62+
vm.on_output("Service ready", iperf_client)
63+
64+
65+
# Clean
66+
vm.on_exit(clean)
67+
68+
# Boot the VM, taking a timeout as parameter
69+
vm.cmake().boot(30).clean()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"image": "test_router.img",
3+
"cpu" : "host",
4+
"mem" : 320,
5+
"net" : [{"device" : "virtio"},
6+
{"device" : "virtio"}]
7+
}

0 commit comments

Comments
 (0)