nic_router: new user interface and optimizations

Fixes #2139
This commit is contained in:
Martin Stein 2016-09-12 12:55:12 +02:00 committed by Christian Helmuth
parent f90964fdcc
commit 89085096d2
53 changed files with 3352 additions and 2146 deletions

View File

@ -2,6 +2,9 @@
# Build
#
# the lwip/http_clnt requires FOC
assert_spec foc
set tcp_up_to_down_1 1
set tcp_up_to_down_2 1
set tcp_down_to_up_1 1
@ -63,67 +66,40 @@ append config {
<provides><service name="Nic"/></provides>
<config rtt_sec="3" verbose="no">
<policy label="uplink" src="10.0.2.55">
<ip dst="192.168.1.18/32" label="http_srv_1" />
<ip dst="10.0.2.0/24">
<tcp dst="29" label="commander"/>
<tcp dst="80" label="keen" via="192.168.1.18" />
<tcp dst="8080" via="192.168.1.72" />
<tcp dst="10" label="earthworm"/>
<tcp dst="2345" label="jim"/>
</ip>
<ip dst="10.0.0.0/16" label="link" />
<ip dst="192.168.1.72/31" label="http_srv_2" />
<ip dst="10.0.2.55/32">
<tcp dst="132" label="samus" to="192.168.1.72"
via="192.168.1.14" />
<policy label_prefix="udp_client_1" domain="udp_client_1" />
<policy label_prefix="http_client_3" domain="http_client_3" />
<policy label_prefix="udp_client_3" domain="udp_client_3" />
<policy label_prefix="http_server_1" domain="http_server_1" />
<policy label_prefix="http_server_2" domain="http_server_2" />
<policy label_prefix="udp_server_1" domain="udp_server_1" />
<policy label_prefix="udp_server_2" domain="udp_server_2" />
<tcp dst="80" label="pacman" />
<tcp dst="80" label="http_srv_1" to="192.168.1.18" />
<tcp dst="8080" />
<udp dst="1337" label="udp_srv_1" to="10.0.99.55"
via="10.0.99.55" />
<domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1">
<tcp-forward port="80" domain="http_server_1" to="192.168.1.18" />
<tcp-forward port="8080" domain="http_server_2" to="192.168.2.72" />
<udp-forward port="1" domain="udp_server_2" to="18.17.16.15" />
<nat domain="http_client_3" tcp-ports="6" />
<ip dst="217.13.192.0/24" domain="udp_client_3" />
</domain>
<udp dst="1" label="udp_srv_2" to="18.17.16.15" />
<tcp dst="8080" label="http_srv_2" to="192.168.1.72" />
</ip>
</policy>
<domain name="udp_client_1" interface="10.0.98.33/24">
<udp-forward port="1337" domain="udp_server_1" to="10.0.99.55" />
</domain>
<policy label_prefix="http_srv_1" src="192.168.1.1" nat="yes" nat-tcp-ports="4">
<ip dst="10.0.0.0/19" label="uplink" via="10.0.6.1" />
<ip dst="10.0.2.128/25" label="uplink" via="10.0.3.1" />
<ip dst="10.0.2.0/24" label="uplink" />
</policy>
<domain name="http_client_3" interface="100.200.0.1/24">
<tcp dst="10.0.0.0/16">
<permit port="2345" domain="uplink" />
</tcp>
</domain>
<policy label_prefix="http_srv_2" src="192.168.1.1" nat="yes" nat-tcp-ports="2">
<ip dst="10.0.2.0/24" label="uplink" />
</policy>
<domain name="udp_client_3" interface="217.13.192.1/24">
<ip dst="10.0.2.0/24" domain="uplink" />
</domain>
<policy label_prefix="http_clnt_3" src="100.200.0.1" nat="yes" nat-tcp-ports="4">
<ip dst="10.0.6.0/23" label="uplink" via="10.0.4.1" />
<ip dst="10.0.0.0/16" />
<ip dst="10.0.2.0/24" label="uplink" />
</policy>
<policy label_prefix="udp_srv_1" src="10.0.99.33" nat="yes" nat-udp-ports="1">
<ip dst="10.0.2.0/24" label="uplink" />
<ip dst="10.0.98.0/24" label="udp_clnt_1" />
</policy>
<policy label_prefix="udp_srv_2" src="18.17.15.14">
<ip dst="10.0.2.0/24" label="uplink" />
</policy>
<policy label_prefix="udp_clnt_1" src="10.0.98.33">
<ip dst="10.0.98.33/32">
<udp dst="1337" label="udp_srv_1" to="10.0.99.55"
via="10.0.99.55"/>
</ip>
</policy>
<policy label_prefix="udp_clnt_3" src="217.13.192.1" nat="yes" nat-udp-ports="2">
<ip dst="10.0.0.0/18" label="uplink" />
</policy>
<domain name="http_server_1" interface="192.168.1.1/24" />
<domain name="http_server_2" interface="192.168.2.1/24" />
<domain name="udp_server_1" interface="10.0.99.33/24" />
<domain name="udp_server_2" interface="18.17.16.14/24" />
</config>
<route>
@ -136,10 +112,10 @@ append config {
<resource name="RAM" quantum="10M"/>
<provides><service name="Nic"/></provides>
<config>
<policy label_prefix="nic_router" ip_addr="10.0.2.55"/>
<policy label_prefix="http_srv_3" ip_addr="10.0.2.11"/>
<policy label_prefix="udp_clnt_2" ip_addr="10.0.2.123"/>
<policy label_prefix="udp_srv_3" ip_addr="10.0.2.70"/>
<policy label_prefix="nic_router" ip_addr="10.0.2.55" />
<policy label_prefix="http_server_3" ip_addr="10.0.2.11" />
<policy label_prefix="udp_client_2" ip_addr="10.0.2.123" />
<policy label_prefix="udp_server_3" ip_addr="10.0.2.70" />
</config>
<route>
<service name="Nic"> <child name="nic_drv"/> </service>
@ -149,7 +125,7 @@ append config {
append_if $udp_down_to_down_1 config {
<start name="udp_clnt_1">
<start name="udp_client_1">
<binary name="test-lxip_udp_client" />
<resource name="RAM" quantum="2M"/>
<route>
@ -166,7 +142,7 @@ append_if $udp_down_to_down_1 config {
</config>
</start>
<start name="udp_srv_1">
<start name="udp_server_1">
<binary name="test-lxip_udp_echo" />
<resource name="RAM" quantum="2M"/>
<route>
@ -184,7 +160,7 @@ append_if $udp_down_to_down_1 config {
append_if $udp_up_to_down_1 config {
<start name="udp_clnt_2">
<start name="udp_client_2">
<binary name="test-lxip_udp_client" />
<resource name="RAM" quantum="2M"/>
<route>
@ -201,7 +177,7 @@ append_if $udp_up_to_down_1 config {
</config>
</start>
<start name="udp_srv_2">
<start name="udp_server_2">
<binary name="test-lxip_udp_echo" />
<resource name="RAM" quantum="2M"/>
<route>
@ -210,7 +186,7 @@ append_if $udp_up_to_down_1 config {
</route>
<config ld_verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="18.17.16.15"
gateway="18.17.15.14" netmask="255.255.0.0" port="1">
gateway="18.17.16.14" netmask="255.255.0.0" port="1">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
@ -219,7 +195,7 @@ append_if $udp_up_to_down_1 config {
append_if $udp_down_to_up_1 config {
<start name="udp_clnt_3">
<start name="udp_client_3">
<binary name="test-lxip_udp_client" />
<resource name="RAM" quantum="2M"/>
<route>
@ -236,7 +212,7 @@ append_if $udp_down_to_up_1 config {
</config>
</start>
<start name="udp_srv_3">
<start name="udp_server_3">
<binary name="test-lxip_udp_echo" />
<resource name="RAM" quantum="2M"/>
<route>
@ -245,7 +221,7 @@ append_if $udp_down_to_up_1 config {
</route>
<config ld_verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="10.0.2.70"
netmask="255.255.255.0" gateway="10.0.2.1" port="65535">
netmask="255.255.255.0" gateway="10.0.2.55" port="65535">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
@ -254,7 +230,7 @@ append_if $udp_down_to_up_1 config {
append_if $tcp_up_to_down_1 config {
<start name="http_srv_1">
<start name="http_server_1">
<binary name="test-lwip_httpsrv_static"/>
<resource name="RAM" quantum="2M"/>
<route>
@ -269,7 +245,7 @@ append_if $tcp_up_to_down_1 config {
</libc>
</config>
</start>
<start name="http_clnt_1">
<start name="http_client_1">
<binary name="test-http_clnt"/>
<resource name="RAM" quantum="2M"/>
<route>
@ -287,7 +263,7 @@ append_if $tcp_up_to_down_1 config {
append_if $tcp_up_to_down_2 config {
<start name="http_srv_2">
<start name="http_server_2">
<binary name="test-lwip_httpsrv_static"/>
<resource name="RAM" quantum="2M"/>
<route>
@ -295,15 +271,15 @@ append_if $tcp_up_to_down_2 config {
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="192.168.1.72"
netmask="255.255.255.0" gateway="192.168.1.1"
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="192.168.2.72"
netmask="255.255.255.0" gateway="192.168.2.1"
http_port="8080">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
<start name="http_clnt_2">
<start name="http_client_2">
<binary name="test-http_clnt"/>
<resource name="RAM" quantum="2M"/>
<route>
@ -321,7 +297,7 @@ append_if $tcp_up_to_down_2 config {
append_if $tcp_down_to_up_1 config {
<start name="http_srv_3">
<start name="http_server_3">
<binary name="test-lwip_httpsrv_static"/>
<resource name="RAM" quantum="2M"/>
<route>
@ -336,7 +312,7 @@ append_if $tcp_down_to_up_1 config {
</libc>
</config>
</start>
<start name="http_clnt_3">
<start name="http_client_3">
<binary name="test-http_clnt"/>
<resource name="RAM" quantum="2M"/>
<route>
@ -378,17 +354,11 @@ append_platform_drv_boot_modules
build_boot_image $boot_modules
# qemu config
append qemu_args " -m 128 -nographic "
append qemu_args " -m 256 -nographic "
append_if [have_spec x86] qemu_args " -net nic,model=e1000 "
append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 "
append qemu_args " -net user -redir udp:5555::1337 "
#
# FIXME: For unknown reasons, there seems to be a problem in the NIC router
# with the destruction of NIC sessions that had TCP connections running.
# This is why sometimes not all HTTP clients exit properly. Thus, look
# merely for 4 client exits (at least 3xUDP 1xTCP) by now.
#
run_genode_until ".*child \".*_clnt_.\" exited with exit value 0.*\n.*child \".*_clnt_.\" exited with exit value 0.*\n.*child \".*_clnt_.\" exited with exit value 0.*\n.*child \".*_clnt_.\" exited with exit value 0.*\n" 100
run_genode_until ".*Test done.*\n.*Test done.*\n.*Test done.*\n.*Test done.*\n.*Test done.*\n.*Test done.*\n" 60

View File

@ -11,10 +11,10 @@
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MAC_H_
#define _MAC_H_
#ifndef _MAC_ALLOCATOR_H_
#define _MAC_ALLOCATOR_H_
/* Genode */
/* Genode includes */
#include <base/exception.h>
#include <net/ethernet.h>
@ -22,7 +22,7 @@ namespace Net {
/**
* The MAC allocator is used to administer MAC addresses for
* the proxy-ARP's client's.
* NIC session clients.
*/
class Mac_allocator
{
@ -41,7 +41,7 @@ namespace Net {
public:
struct Alloc_failed : Genode::Exception {};
class Alloc_failed : Genode::Exception {};
/* reference MAC address */
@ -77,4 +77,4 @@ namespace Net {
};
}
#endif /* _MAC_H_ */
#endif /* _MAC_ALLOCATOR_H_ */

View File

@ -20,13 +20,13 @@
#include <nic/packet_allocator.h>
#include <nic_session/rpc_object.h>
#include <nic_session/connection.h>
#include <nic_bridge/mac_allocator.h>
#include <os/ram_session_guard.h>
#include <os/session_policy.h>
#include <root/component.h>
#include <util/arg_string.h>
#include <address_node.h>
#include <mac.h>
#include <nic.h>
#include <packet_handler.h>

View File

@ -1,80 +0,0 @@
/*
* \brief MAC-address allocator
* \author Stefan Kalkowski
* \date 2010-08-25
*/
/*
* Copyright (C) 2010-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MAC_H_
#define _MAC_H_
/* Genode */
#include <base/exception.h>
#include <net/ethernet.h>
namespace Net {
/**
* The MAC allocator is used to administer MAC addresses for
* the proxy-ARP's client's.
*/
class Mac_allocator
{
private:
/* limit available MAC addresses to one byte range */
enum { MSB_MAX = 0xFF };
/* signals, whether most significant byte is in use */
typedef struct
{
unsigned used : 1;
} Msb;
Msb _msbs[MSB_MAX]; /* bitfield of MSBs */
public:
class Alloc_failed : Genode::Exception {};
/* reference MAC address */
static Mac_address mac_addr_base;
Mac_allocator() { Genode::memset(&_msbs, 0, sizeof(_msbs)); }
/**
* Allocates a new MAC address.
*
* \throws Alloc_failed if no more MAC addresses are available.
* \return MAC address
*/
Mac_address alloc()
{
for (int i=0; i < MSB_MAX; i++) {
if (!_msbs[i].used) {
_msbs[i].used = 1;
Mac_address mac = mac_addr_base;
mac.addr[5] = i;
return mac;
}
}
throw Alloc_failed();
}
/**
* Frees a formerly allocated MAC address.
*/
void free(Mac_address mac) {
_msbs[(unsigned)mac.addr[5]].used = 0; }
};
}
#endif /* _MAC_H_ */

View File

@ -11,7 +11,8 @@
* under the terms of the GNU General Public License version 2.
*/
#include <mac.h>
/* Genode includes */
#include <nic_bridge/mac_allocator.h>
/**
* We take the range 02:02:02:02:02:XX for our MAC address allocator,

View File

@ -1,4 +1,4 @@
TARGET = nic_bridge
LIBS = base net
SRC_CC = component.cc mac.cc main.cc nic.cc packet_handler.cc
SRC_CC = component.cc mac_allocator.cc main.cc nic.cc packet_handler.cc
INC_DIR += $(PRG_DIR)

View File

@ -7,202 +7,390 @@
Brief
#####
The nic_router component can be used to individually route IPv4 packets between
multiple NIC sessions. Thereby, it can translate between different IP subnets.
The component supports port forwarding, as well as the partitioning of the TCP
and UDP port spaces.
The 'nic_router' component can be used to individually route IPv4 packets
between multiple NIC sessions. Thereby, it can translate between different
subnets. The component supports IP routing, TCP and UDP routing, the
partitioning of the TCP and UDP port spaces, port forwarding, and NAT.
Basic routing
#############
Basics
######
The nic_router component provides multiple sessions of the 'NIC' service
The 'nic_router' component provides multiple sessions of the 'NIC' service
(downlinks) while requesting one 'NIC' session (the uplink) itself. Through
common Genode session routing, the uplink can be connected to any other NIC
server. Inside the component, uplink and downlinks are treated the same. Its
routing algorithm is ultimately controlled through the configuration. For each
NIC session there must be a policy:
routing algorithm is ultimately controlled through the configuration. NIC
sessions are assigned to domains. Each domain represents one subnet and a
corresponding routing configuration. Currently, each domain can contain
only one NIC session at a time. The assigment of sessions to domains is
controlled through the the common Genode session-policy tag:
! <policy label="uplink" src="192.168.1.3" />
! <policy label="http_servers" src="10.0.2.1" />
! <policy label_prefix="http_server" domain="http_servers" />
! <policy label_prefix="imap_server" domain="imap_servers" />
The 'label' attribute must match the session label respectively 'uplink' for the
uplink session. The 'src' attribute contains a static IPv4 identity that
represents the nic_router with respect to that session. This identity is used
when doing Network-Address-Translation to the session. Moreover, a policy tag
may contain multiple 'ip' sub-tags:
The domain name can be freely choosen but must be unique. There is no need
to have a policy for the uplink. It is automatically assigned to the domain
named "uplink". For each domain there must be a domain tag:
! <ip dst="192.168.1.0/24" label="uplink" />
! <ip dst="10.0.0.0/16" label="http_servers" />
! <domain name="uplink" interface="10.0.2.55/24" />
! <domain name="http_servers" interface="192.168.1.18/24" />
! <domain name="imap_servers" interface="192.168.2.17/24" />
Each 'ip' tag defines a routing rule for IPv4 packets that are sent by the
session of the surrounding policy. Such a rule needs at least a 'dst' attribute
that contains an IPv4 address prefix. When the nic_router receives an IPv4
packet, it goes top-down through all the rules of the session, checking whether
the packet destination matches the 'dst' attribute. The first rule that matches
is inspected deeper. The next thing to be read is the 'label' attribute that
names the target session of the rule. If the label points to a valid session,
the packet could now be routed via this rule. But the IP rule is only remembered
as fallback yet because it may contain sub-tags for preferred UDP and TCP
routing:
The 'interface' attribute defines two things at once. First, it tells the
router which subnet can be found behind this domain, and second, which IP
identity the router shall use in case it has to communicate as itself with
the subnet.
! <udp dst="443" label="udp_servers" />
! <tcp dst="80" label="http_servers" />
Additionaly, the optional 'gateway' attribute can be set for a domain:
Those tags work pretty similar to the IP rules. The 'dst' attribute defines
the port that a packet must be addressed to for a match. The 'label' attribute
again points to the target session. If no matching UDP or TCP rule with a
valid target session is found inside the matching IP rule, the nic_router
falls back to the IP rule. If the target session of the matching IP rule isn't
valid, it continues with the next matching IP rule, and so on.
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1" />
It defines the standard gateway of the subnet behind this domain. If a packet
shall be routed to this domain and its final IP destination does not match
the subnet, its Ethernet destination is set to the MAC address of the gateway.
If a gateway isn't given for a domain, such packets get dropped.
For each domain, the routing of packets from this domain can be configured
individually by adding subtags to the corresponding domain tag. There are
multiple types of subtags expressing different types of routing rules. The
following table gives a brief overview over the different subtags and their
meaning:
Subtag | Description
---------------------------------------------------------------
<tcp-forward port="X" /> | Port forwarding for TCP port X *
---------------------------------------------------------------
<udp-forward port="X" /> | Port forwarding for UDP port X *
---------------------------------------------------------------
<tcp dst="X"> | Routing TCP packets that target
<permit-any /> | IP range X *
</tcp> |
---------------------------------------------------------------
<udp dst="X"> | Routing UDP packets that target
<permit-any /> | IP range X *
</udp> |
---------------------------------------------------------------
<tcp dst="X"> | Routing TCP packets that target
<permit port="Y" /> | IP range X and port Y or Z *
<permit port="Z" /> |
</tcp> |
---------------------------------------------------------------
<udp dst="X"> | Routing UDP packets that target
<permit port="Y" /> | IP range X and port Y or Z *
<permit port="Z" /> |
</udp> |
---------------------------------------------------------------
<ip dst="X" /> | Routing IP packets that target
| IP range X
A detailed explanation of the different routing rules is given in the
following sections of this document. For all rules marked with a star, the
router also keeps track of corresponding TCP connections and UDP
pseudo-connections. With these so-called link states, corresponding reply
packets are automatically routed back. The user doesn't have to add an
additional back-routing rule for that.
Now having this variety of ways of routing a packet, it is absolutely legal
that for one packet the domain may contain multiple rules that are applicable.
And additionally, there may even be a link state that fits. The router's
choice, however, is always deterministic. It follows a simple priority scheme:
1) Link states
2) Port forwarding rules
3) Longest prefix match amongst TCP respectively UDP rules
3.1) Subrule that permits any port
3.2) Subrules that permit specific ports
4) Longest prefix match amongst IP rules
Modify destination and gateway
##############################
IP rules
########
All three, the 'ip', 'udp', and 'tcp' tag may have further attributes 'to'
and/or 'via'. Both attributes define an IPv4 address. The address in the 'to'
attribute replaces the IPv4 destination of an affected packet. The address in
the 'via' attribute is the gateway for that rule. This means, when searching
for the next Ethernet destination of an affected packet, the nic_router uses the
'via' address as criterion. If not set, the 'via' address is always the IPv4
destination of the packet. Hence, if only the 'to' attribute is set for a
rule, the value is also used as 'via' address.
These are examples for IP rules:
! <udp ... via="192.168.2.1" />
! <ip ... to="10.0.2.234" via="192.168.2.1" />
! <tcp ... to="10.0.2.234" />
! <ip dst="10.0.2.0/24" domain="intranet" />
! <ip dst="192.168.1.18/32" domain="my_server" />
! <ip dst="0.0.0.0/0" domain="uplink" />
IP rules only apply to IPv4 packets from the session of the surrounding
domain. The 'dst' attribute is compared with the IP destination of the packet.
The rule with the longest prefix match is taken. The packet is then routed to
the domain given in the rule.
IP rules work pretty simple. They merely affect the Ethernet header of a
packet and they don't imply link-state tracking. This has consequences. First,
IP rules do not automatically route back reply packets from the remote side.
If you like to enable bidirectional communication via IP rules, both domains
must have an appropriate rule in their domain tag. And second, IP rules do not
consider a NAT configuration (Section [Configuring NAT]). As this could lead
to unexpected leakage of local IP addresses and ports, you should use the
combination of IP rules and NAT only with great care.
Network address translation
###########################
TCP and UDP rules
#################
The nic_router component can translate between different IPv4 subnets of clients,
and "uplink". When enabled within the policy of a client:
TCP and UDP rules must always be accompanied by one or more port permission
rules to get effective:
! <policy ... nat="yes" />
! <tcp dst="192.168.1.18/32">
! <permit port="70" domain="gopher_servers" />
! <permit port="80" domain="http_servers" />
! </tcp>
! <udp dst="10.0.2.0/24">
! <permit-any domain="uplink" />
! </udp>
the source address of all IPv4 packets of that client is replaced by the 'src'
value of the applied target session. And the TCP/UDP source port is replaced
by a dynamically allocated source port of the applied target session. There is
an exception from the latter. If the source port of the packet matches a
TCP/UDP rule of the applied target session, the packet is assumed to be a
reply to a port-forwarded request from the counterside. In this case, the
source port remains unchanged to enable the receiver to correlate the packet
correctly.
TCP rules only apply to TCP packets and UDP rules only to UDP packets from the
session of the surrounding domain. The 'dst' attribute is compared with the IP
destination of the packet. The rule with the longest prefix match is taken.
If the rule contains a 'permit-any' subrule or a 'permit' subrule whose 'port'
attribute matches the destination port of the packet, the packet is routed to
the domain given in the subrule.
For partitioning the TCP/UDP port space, e.g., the source ports regarding the
'uplink' session between different client sessions, one can configure how many
ports may be used concurrently per client session:
For bidirectional traffic, you'll need only one TCP or UDP rule describing the
client-to-server direction. The server-sided domain doesn't need a rule as the
router correlates replies to the client-sided rule (and only those) via a link
state (Section [Link states]) that was created at the clients initial request.
! <policy ... nat="yes" nat-udp-ports="1001" />
! <policy ... nat="yes" nat-tcp-ports="43" nat-udp-ports="21" />
! <policy ... nat="yes" nat-tcp-ports="3" />
TCP and UDP rules consider whether the router shall apply NAT
(Section [Configuring NAT]) for the client side. If this is the case, source
IP and port are replaced by the router's IP identity and a free port at the
server-sided domain. Also the corresponding link state takes this in account
to change back the destination of the replies.
The ports attribute contains the maximum number of ports that the nic_router
assigns to the given session. This is necessary to avoid that a NIC session
can run Denial-of-Service attacks against the nic_router by occupying all of
its ports.
For both, UDP and TCP activities, the nic_router holds link states. The link
states enable the nic_router to re-direct related packets from the counterside
correctly without the need for additional routing rules. Such link state rules
are the most preferred way of routing. Before the nic_router starts looking
for IP, UDP, or TCP rules for a packet, it tries to find a matching rule
amongst the link states.
Port-forwarding rules
#####################
As link state rules are created on demand and are bound to an active
connection between two peers, it is desirable to clear them away as soon as
they are not needed anymore. A precise algorithm for that enables the NIC
sessions to get the maximum out of their resources (ports, RAM). A TCP state
rule corresponding is held until the nic_router observes the four-way
termination handshake of TCP and two times the estimated round-trip time has
passed. The nic_router currently doesn't estimate any round-trip times by
itself. Instead it expects an attribute 'rtt_sec' in its 'config' tag:
These are examples for port-forwarding rules:
! <tcp-forward port="80" domain="http_servers" to="192.168.1.18" />
! <udp-forward port="69" domain="tftp_servers" to="192.168.2.23" />
Port-forwarding rules only apply to packets that come from the session of the
surrounding domain and are addressed to the router's IP identity at this
domain (Section [Basics]). Amongst those, 'tcp-forward' rules only apply to
the TCP packets and 'udp-forward' rules only to the UDP packets. The 'port'
attribute is compared with the packet's destination port. If a matching rule
is found, the IP destination of the packet is changed to the value of the 'to'
attribute. Then, the packet is routed to the domain given in the rule. Note
that the router accepts only system and registered ports (0 to 49151) for port
forwarding.
For bidirectional traffic, you'll need only one port-forwarding rule
describing the client-to-server direction. The server-sided domain doesn't
need a rule as the router correlates replies to the client-sided rule (and
only those) via a link state (Section [Link states]) that was created at the
clients initial request.
It's in the nature of port forwarding that it comes along with NAT for the
server side. However, the router only translates the server IP. The port
remains unchanged. For the client side, port-forwarding rules apply NAT only
when configured (Section [Configuring NAT]). If this is the case, client IP
and port are translated.
Link states
###########
Each time a packet gets routed by using a TCP, UDP, or port-forwarding rule,
the router creates a link state. From then on, all packets that belong
to the exchange this first packet initiated and come from one of the two
involved domains are routed by the link state and not by a rule. The costs
for the link state are paid by the session that sent the first packet.
If a link state exists for a packet, it is unambiguously correlated through
the source IP and port as well as the destination IP and port. This is also
the case if the transfer includes NAT no matter of what kind or for which
side.
It is desirable to discard a link state as soon as it is not needed anymore.
The more precise the algorithm for that, the more efficient can NIC sessions
use their resources (ports, RAM), and the less is the risk for DoS attacks.
In order to meet this requirement, the router needs to know the round-trip
time of the exchange behind a link state. This value is given through the
attribute 'rtt_sec' in the router's configuration:
! <config rtt_sec="3"> ... </config>
This would set the round-trip time to three seconds which means that link
state rules wait six seconds after a termination handshake before they close
themselves. As UDP has no notion of connections, UDP state rules are simply
held for a duration of two times the round-trip time after the last packet.
This way, the peers can keep alive a UDP pseudo-connection by frequently
sending empty packets.
This would set the round-trip time to three seconds. The value is used for all
link states so you should choose it with care. If it is too low, replies that
normally need no routing rule may get lost. If it is too high, link states are
held longer than necessary.
In general, each link state is discarded after a duration of two times the
round-trip time without a matching packet. For UDP link states, this is the
only rule and better known as hole punching. It allows peers to keep alive a
UDP pseudo-connection through the router by frequently sending empty packets.
The need for such a pseudo-connection arises from the router's demand to
support NAT for UDP transfers and the consequence of keeping the corresponding
mapping information.
The lifetime management of TCP link states, in contrast, is more complex. In
addition to the common timeout, they may be discarded even if they still
receive packets. This is the case when the router observed the four-way
termination handshake of TCP and two times the round-trip time has passed.
Configuring NAT
###############
In contrast to routing rules that affect packets coming from their domain,
NAT rules affect packets that go to their domain:
! <domain name="uplink" interface="10.0.2.55/24">
! <nat domain="http_client" tcp-ports="6" />
! </domain>
This would tell the router to apply NAT for the HTTP client when it speaks to
the uplink. This means, it affects all packets from the HTTP client that get
routed to the uplink by using a UDP, TCP, or port-forwarding rule respectively
a corresponding link state. If this is the case, the packet's source IP
address is changed to "10.0.2.55" and the source port is replaced by a free
source port of the router. When saying "free source port" this actually means
a port that the router currently doesn't use at the destination domain. So,
at each domain, the router has two complete port spaces for source NAT
available. One for UDP and one for TCP. Each port space contains the IANA
dynamic port range 49152 to 65535.
As you can see, the NAT rule also has a 'tcp-ports' attribute. It restricts
how many TCP source ports of the uplink the HTTP client may use at a time. The
same goes also for UDP:
! <nat domain="tftp_client" udp-ports="13" />
And even combined:
! <nat domain="intranet" tcp-ports="43" udp-ports="21" />
If one of the two attributes is not set, this means that no port shall be used
for this protocol which effectively disables it. Thus, at least one of the two
attributes must be set for the NAT rule to be sensible. Restricting the port
usage is necessary to avoid that a client can run Denial-of-Service attacks
against the destination domain by occupying all of its ports.
Examples
########
This paragraph will list and explain some interesting configuration snippets.
A comprehensive example of how to use the nic_router can be found in the test
script 'libports/run/nic_router.run' .
This section will list and explain some interesting configuration snippets. A
comprehensive example of how to use the router can be found in the test script
'libports/run/nic_router.run'. The environment for the examples shall be as
follows. There are two virtual subnets 192.168.1.0/24 and 192.168.2.0/24 that
connect as Virtnet A and B to the router. The standard gateway of the virtual
networks is the NIC router with IP 192.168.*.1 . The router's uplink leads to
the NIC driver that connects the machine with your home network 10.0.2.0/24.
Your home network is connected to the internet through its standard gateway
10.0.2.1 .
Accessing a private server network
==================================
Connecting local networks
=========================
In this example, we assume that there is a HTTP server "behind" the nic_router
(downlink) listening at port 80. Through the uplink session, several clients
may ask the nic_router for HTTP. The nic_router has the following
configuration:
Let's assume we simply want the virtual networks and the home network to be
able to talk to each other. Furthermore, the virtual networks shall be able to
use the internet connection of your home network. The router would have the
following configuration:
! <policy label="uplink" src="10.0.2.55">
! <ip dst="10.0.2.55/32">
! <tcp dst="80" label="http_servers" to="192.168.1.2" />
! </ip>
! </policy>
! <policy label_prefix="virtnet_a" domain="virtnet_a" />
! <policy label_prefix="virtnet_b" domain="virtnet_b" />
!
! <policy label="http_servers" src="192.168.1.1" nat="yes" nat-tcp-ports="30">
! <ip dst="10.0.2.0/24" label="uplink" />
! </policy>
The uplink IP rule matches only packets that directly address the nic_router's
uplink identity. Amongst those packets, the ones to TCP port 80 also match the
TCP sub-rule. The TCP sub-rule is rated higher than the surrounding IP rule
and thus gets applied. Consequently, the nic_router replaces the IPv4
destination of the packets with the 'to' value (which is the local server
address), then looks up and installs the next Ethernet destination for the
given server address, and finally sends the packet on the servers NIC session.
All other packets from the uplink are dropped. Even those that address the
nic_router directly but with another port. This is because, although the IP
rule still matches, it specifies no target session.
If the server sends back reply packets to the client, they address the clients
public IP because the IPv4 source of the previous request wasn't modified.
Thus, these packets match the IP rule in the server policy and get forwarded
to the uplink. But furthermore, the nic_router is configured do NAT for the
server (with 30 simultaneous TCP connections allowed). Hence, the nic_router
replaces the IPv4 source in the server replies by its uplink identity
'10.0.2.55'. The source port, at the other hand, is not replaced because it
matches a TCP rule in the uplink policy. This way, the client is able to
associate the replies to its TCP connection with the server.
Using public servers from a private network
===========================================
Let's assume we have a UDP client "behind" the nic_router and like to talk to an
"outside" server. An example configuration for that would be:
! <policy label="uplink" src="10.0.2.55" />
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1/24">
! <ip dst="192.168.1.0/24" domain="virtnet_a"/>
! <ip dst="192.168.2.0/24" domain="virtnet_b"/>
! </domain>
!
! <policy label="udp_clients" src="100.200.0.1" nat="yes" nat-udp-ports="2">
! <ip dst="10.0.2.0/24" label="uplink" />
! </policy>
! <domain name="virtnet_a" interface="192.168.1.1/24">
! <ip dst="192.168.2.0/24" domain="virtnet_b"/>
! <ip dst="0.0.0.0/0" domain="uplink"/>
! </domain>
!
! <domain name="virtnet_b" interface="192.168.2.1/24">
! <ip dst="192.168.1.0/24" domain="virtnet_a"/>
! <ip dst="0.0.0.0/0" domain="uplink"/>
! </domain>
UDP packets from the client to the public network match the clients IP rule
and therefore are forwarded to the uplink. Because of the NAT attributes, the
packets are modified to have '10.0.2.55' as IPv4 source and a free UDP port of
the uplink session as source port. The client is allowed to open a maximum of
two such connections at a time. The uplink doesn't need any rules to forward
the replies for the UDP client correctly as long as the server replies in
time. This is because the nic_router still holds the link state of the initial
UDP packet from the client and can use it to map back IP address and port.
IP packets from Virtnet A and uplink that target an IP address 192.168.2.* are
routed to Virtnet B. IP packets from Virtnet B and uplink that target an IP
address 192.168.1.* are routed to Virtnet A. Packets that are addressed to
hosts in the same local network should never reach the router as they can be
transmitted directly. If there's a packet from one of the virtual networks
that doesn't target 192.168.1.* or 192.168.2.*, the IP 0.0.0.0/0 rules route
them to the uplink. If these packets target an IP 10.0.2.*, the router sends
them directly to the host in your home network. Otherwise, the router sends
them to your gateway 10.0.2.1 . Note that none of the packets is modified on
layer 2 or higher, so, no NAT is done by the router to hide the virtual
networks.
Limitations
###########
Clients in a private network
============================
Currently, different client sessions should not share the same subnet to be able
to communicate with each other, because forwarding broadcast packets (e.g., ARP
packets) between different clients of the same subnet is not supported yet.
Now we have some clients in Virtnet A that like to talk to the internet as
well as to the home network. We want them to be hidden via NAT when they do so
and to be limited to HTTP+TLS/SSL and IMAP+TLS/SSL when talking to the
internet. The router would have the following configuration:
! <policy label_prefix="virtnet_a" domain="virtnet_a" />
! <policy label_prefix="virtnet_b" domain="virtnet_b" />
!
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1/24">
! <nat domain="virtnet_a" tcp_ports="1000" udp_ports="1000">
! </domain>
!
! <domain name="virtnet_a" interface="192.168.1.1/24">
! <tcp dst="10.0.2.0/24"><permit-any domain="uplink" /></tcp>
! <udp dst="10.0.2.0/24"><permit-any domain="uplink" /></udp>
! <tcp dst="0.0.0.0/0">
! <permit port="443" domain="uplink" />
! <permit port="993" domain="uplink" />
! </tcp>
! </domain>
From the packets that come from Virtnet A, those that target an IP 10.0.2.*
are routed to the uplink without inspecting the port. At the uplink, the
router notices that it shall apply NAT for Virtnet A. It replaces the source
IP with 10.0.2.55 and allocates one of its uplink source ports for the
exchange. On replies to Virtnet-A packets from the home network, the router
translates IP and port back using the corresponding link state. For packets
from Virtnet A that target other IPs, only the 0.0.0.0/0 rule applies and only
if the packet targets TCP port 443 or 993. Both ports route the packet to the
uplink where, again, NAT is applied and the packets are sent to the gateway
10.0.2.1 .
Servers in a private network
============================
In this example, we assume that there are three servers in Virtnet A. An HTTP
server at port 80 with IP 192.168.1.2, a GOPHER server at port 70 with IP
192.168.1.3, and a TFTP server at port 69 with IP 192.168.1.4 . Now you want
the servers (and only them) to be reachable to the home network via the
router's IP and to the internet via your gateway. The router would have the
following configuration:
! <policy label_prefix="virtnet_a" domain="virtnet_a" />
! <policy label_prefix="virtnet_b" domain="virtnet_b" />
!
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1">
! <tcp-forward port="80" domain="virtnet_a" to="192.168.1.2" />
! <tcp-forward port="70" domain="virtnet_a" to="192.168.1.3" />
! <udp-forward port="69" domain="virtnet_a" to="192.168.1.4" />
! </domain>
!
! <domain name="virtnet_a" interface="192.168.1.1/24" />
! <domain name="virtnet_b" interface="192.168.1.1/24" />
Amongst the packets that come from the uplink, only those that are addressed
to 10.0.2.55 and TCP port 80, TCP port 70, or UDP port 69 are forwarded.
All these packets are forwarded to Virtnet A. But beforehand, their IP
destination is adapted. TCP-port-80 packets are redirected to 192.168.1.2,
TCP-port-70 packets to 192.168.1.3, and UDP-port-69 packets to 192.168.1.4.
Amongst the packets that come from Virtnet A, only those that match a link
state at the uplink are forwarded, because the Virtnet-A domain contains no
rules. Thus, Virtnet A can only talk to the uplink in the context of
TCP-connections or UDP pseudo-connections that were opened by clients behind
the uplink. The servers IP addresses never leave Virtnet A.

View File

@ -18,40 +18,58 @@ using namespace Net;
using namespace Genode;
Arp_cache_entry::Arp_cache_entry(Ipv4_address ip_addr, Mac_address mac_addr)
/*********************
** Arp_cache_entry **
*********************/
Arp_cache_entry::Arp_cache_entry(Ipv4_address const &ip,
Mac_address const &mac)
:
_ip_addr(ip_addr), _mac_addr(mac_addr)
_ip(ip), _mac(mac)
{ }
bool Arp_cache_entry::higher(Arp_cache_entry *entry)
bool Arp_cache_entry::_higher(Ipv4_address const &ip) const
{
return (memcmp(&entry->_ip_addr.addr, &_ip_addr.addr,
sizeof(_ip_addr.addr)) > 0);
return memcmp(ip.addr, _ip.addr, sizeof(_ip.addr)) > 0;
}
Arp_cache_entry &Arp_cache_entry::find_by_ip_addr(Ipv4_address ip_addr)
Arp_cache_entry const &
Arp_cache_entry::find_by_ip(Ipv4_address const &ip) const
{
if (ip_addr == _ip_addr) {
if (ip == _ip) {
return *this; }
bool const side =
memcmp(&ip_addr.addr, _ip_addr.addr, sizeof(_ip_addr.addr)) > 0;
Arp_cache_entry * entry = Avl_node<Arp_cache_entry>::child(side);
Arp_cache_entry const *const entry = child(_higher(ip));
if (!entry) {
throw Arp_cache::No_matching_entry(); }
throw Arp_cache::No_match(); }
return entry->find_by_ip_addr(ip_addr);
return entry->find_by_ip(ip);
}
Arp_cache_entry &Arp_cache::find_by_ip_addr(Ipv4_address ip_addr)
/***************
** Arp_cache **
***************/
void Arp_cache::new_entry(Ipv4_address const &ip, Mac_address const &mac)
{
Arp_cache_entry * const entry = first();
if (!entry) {
throw No_matching_entry(); }
return entry->find_by_ip_addr(ip_addr);
if (_entries[_curr].constructed()) { remove(&(*_entries[_curr])); }
_entries[_curr].construct(ip, mac);
insert(&(*_entries[_curr]));
if (_curr < NR_OF_ENTRIES - 1) {
_curr++;
} else {
_curr = 0;
};
}
Arp_cache_entry const &Arp_cache::find_by_ip(Ipv4_address const &ip) const
{
if (!first()) {
throw No_match(); }
return first()->find_by_ip(ip);
}

View File

@ -11,54 +11,74 @@
* under the terms of the GNU General Public License version 2.
*/
#ifndef _ARP_CACHE_H_
#define _ARP_CACHE_H_
/* Genode includes */
#include <net/ipv4.h>
#include <net/ethernet.h>
#include <util/avl_tree.h>
#ifndef _ARP_CACHE_H_
#define _ARP_CACHE_H_
#include <util/volatile_object.h>
namespace Net {
class Arp_cache;
class Arp_cache_entry;
using Arp_cache_entry_slot = Genode::Lazy_volatile_object<Arp_cache_entry>;
}
class Net::Arp_cache_entry : public Genode::Avl_node<Arp_cache_entry>
{
private:
Ipv4_address const _ip_addr;
Mac_address const _mac_addr;
Ipv4_address const _ip;
Mac_address const _mac;
bool _higher(Ipv4_address const &ip) const;
public:
Arp_cache_entry(Ipv4_address ip_addr, Mac_address mac_addr);
Arp_cache_entry(Ipv4_address const &ip, Mac_address const &mac);
Arp_cache_entry &find_by_ip_addr(Ipv4_address ip_addr);
Arp_cache_entry const &find_by_ip(Ipv4_address const &ip) const;
/**************
** Avl_node **
**************/
bool higher(Arp_cache_entry *entry);
bool higher(Arp_cache_entry *entry) { return _higher(entry->_ip); }
/***************
** Accessors **
***************/
Ipv4_address ip_addr() const { return _ip_addr; }
Mac_address mac_addr() const { return _mac_addr; }
Mac_address const &mac() const { return _mac; }
};
struct Net::Arp_cache : Genode::Avl_tree<Arp_cache_entry>
{
struct No_matching_entry : Genode::Exception { };
Arp_cache_entry &find_by_ip_addr(Ipv4_address ip_addr);
class Net::Arp_cache : public Genode::Avl_tree<Arp_cache_entry>
{
private:
enum {
ENTRIES_SIZE = 1024 * sizeof(Genode::addr_t),
NR_OF_ENTRIES = ENTRIES_SIZE / sizeof(Arp_cache_entry),
};
Arp_cache_entry_slot _entries[NR_OF_ENTRIES];
bool _init = true;
unsigned _curr = 0;
public:
struct No_match : Genode::Exception { };
void new_entry(Ipv4_address const &ip, Mac_address const &mac);
Arp_cache_entry const &find_by_ip(Ipv4_address const &ip) const;
};
#endif /* _ARP_CACHE_H_ */

View File

@ -13,25 +13,27 @@
/* local includes */
#include <arp_waiter.h>
#include <arp_cache.h>
#include <interface.h>
using namespace Net;
using namespace Genode;
Arp_waiter::Arp_waiter(Interface &interface, Ipv4_address ip_addr,
Ethernet_frame &eth, size_t const eth_size,
Packet_descriptor &packet)
Arp_waiter::Arp_waiter(Interface &src,
Interface &dst,
Ipv4_address const &ip,
Packet_descriptor const &packet)
:
_interface(interface), _ip_addr(ip_addr), _eth(eth), _eth_size(eth_size),
_src_le(this), _src(src), _dst_le(this), _dst(dst), _ip(ip),
_packet(packet)
{ }
bool Arp_waiter::new_arp_cache_entry(Arp_cache_entry &entry)
{
if (!(entry.ip_addr() == _ip_addr)) { return false; }
_interface.continue_handle_ethernet(&_eth, _eth_size, &_packet);
return true;
_src.own_arp_waiters().insert(&_src_le);
_dst.foreign_arp_waiters().insert(&_dst_le);
}
Arp_waiter::~Arp_waiter()
{
_src.own_arp_waiters().remove(&_src_le);
_dst.foreign_arp_waiters().remove(&_dst_le);
}

View File

@ -1,5 +1,5 @@
/*
* \brief Aspect of waiting for an ARP reply
* \brief Remember packets that wait for ARP replies at different interfaces
* \author Martin Stein
* \date 2016-08-19
*/
@ -11,50 +11,52 @@
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <net/ipv4.h>
#include <nic_session/nic_session.h>
#include <util/list.h>
#ifndef _ARP_WAITER_H_
#define _ARP_WAITER_H_
/* Genode includes */
#include <net/ipv4.h>
#include <util/list.h>
#include <nic_session/nic_session.h>
namespace Net {
using ::Nic::Packet_descriptor;
using Packet_descriptor = ::Nic::Packet_descriptor;
class Interface;
class Ethernet_frame;
class Arp_waiter;
class Arp_cache_entry;
using Arp_waiter_list = Genode::List<Arp_waiter>;
using Arp_waiter_list_element = Genode::List_element<Arp_waiter>;
using Arp_waiter_list = Genode::List<Arp_waiter_list_element >;
}
class Net::Arp_waiter : public Genode::List<Arp_waiter>::Element
class Net::Arp_waiter
{
private:
Interface &_interface;
Ipv4_address _ip_addr;
Ethernet_frame &_eth;
Genode::size_t const _eth_size;
Packet_descriptor &_packet;
Arp_waiter_list_element _src_le;
Interface &_src;
Arp_waiter_list_element _dst_le;
Interface &_dst;
Ipv4_address const _ip;
Packet_descriptor const _packet;
public:
Arp_waiter(Interface &interface, Ipv4_address ip_addr,
Ethernet_frame &eth, Genode::size_t const eth_size,
Packet_descriptor &packet);
Arp_waiter(Interface &src,
Interface &dst,
Ipv4_address const &ip,
Packet_descriptor const &packet);
bool new_arp_cache_entry(Arp_cache_entry &entry);
~Arp_waiter();
/***************
** Accessors **
***************/
Interface &interface() const { return _interface; }
Ethernet_frame &eth() const { return _eth; }
Genode::size_t eth_size() const { return _eth_size; }
Interface &src() const { return _src; }
Ipv4_address const &ip() const { return _ip; }
Packet_descriptor const &packet() const { return _packet; }
};
#endif /* _ARP_WAITER_H_ */

View File

@ -11,59 +11,63 @@
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <os/session_policy.h>
/* local includes */
#include <component.h>
#include <arp_cache.h>
#include <port_allocator.h>
#include <configuration.h>
using namespace Net;
using namespace Genode;
bool Session_component::link_state()
{
warning(__func__, " not implemented");
return false;
}
/**************************
** Communication_buffer **
**************************/
void Session_component::link_state_sigh(Signal_context_capability sigh)
{
warning(__func__, " not implemented");
}
Net::Session_component::Session_component(Allocator &allocator,
size_t const amount,
size_t const tx_buf_size,
size_t const rx_buf_size,
Mac_address mac,
Server::Entrypoint &ep,
Mac_address router_mac,
Ipv4_address router_ip,
char const *args,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Tcp_proxy_list &tcp_proxys,
Udp_proxy_list &udp_proxys,
unsigned const rtt_sec,
Interface_tree &interface_tree,
Arp_cache &arp_cache,
Arp_waiter_list &arp_waiters,
bool verbose)
Communication_buffer::Communication_buffer(Ram_session &ram,
Genode::size_t const size)
:
Guarded_range_allocator(allocator, amount),
Tx_rx_communication_buffers(tx_buf_size, rx_buf_size),
Ram_dataspace_capability(ram.alloc(size)), _ram(ram)
{ }
Session_rpc_object(
Tx_rx_communication_buffers::tx_ds(),
Tx_rx_communication_buffers::rx_ds(),
&range_allocator(), ep.rpc_ep()),
Interface(
ep, router_mac, router_ip, guarded_allocator(), args, tcp_port_alloc,
udp_port_alloc, mac, tcp_proxys, udp_proxys, rtt_sec, interface_tree,
arp_cache, arp_waiters, verbose)
/****************************
** Session_component_base **
****************************/
Session_component_base::
Session_component_base(Allocator &guarded_alloc_backing,
size_t const guarded_alloc_amount,
Ram_session &buf_ram,
size_t const tx_buf_size,
size_t const rx_buf_size)
:
_guarded_alloc(&guarded_alloc_backing, guarded_alloc_amount),
_range_alloc(&_guarded_alloc), _tx_buf(buf_ram, tx_buf_size),
_rx_buf(buf_ram, rx_buf_size)
{ }
/***********************
** Session_component **
***********************/
Net::Session_component::Session_component(Allocator &alloc,
Genode::Timer &timer,
size_t const amount,
Ram_session &buf_ram,
size_t const tx_buf_size,
size_t const rx_buf_size,
Mac_address const mac,
Entrypoint &ep,
Mac_address const &router_mac,
Domain &domain)
:
Session_component_base(alloc, amount, buf_ram, tx_buf_size, rx_buf_size),
Session_rpc_object(_tx_buf, _rx_buf, &_range_alloc, ep.rpc_ep()),
Interface(ep, timer, router_mac, _guarded_alloc, mac, domain)
{
_tx.sigh_ready_to_ack(_sink_ack);
_tx.sigh_packet_avail(_sink_submit);
@ -72,106 +76,87 @@ Net::Session_component::Session_component(Allocator &allocator,
}
Session_component::~Session_component() { }
bool Session_component::link_state()
{
warning("Session_component::link_state not implemented");
return false;
}
Net::Root::Root(Server::Entrypoint &ep,
Allocator &md_alloc,
Mac_address router_mac,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Tcp_proxy_list &tcp_proxys,
Udp_proxy_list &udp_proxys,
unsigned rtt_sec,
Interface_tree &interface_tree,
Arp_cache &arp_cache,
Arp_waiter_list &arp_waiters,
bool verbose)
void Session_component::link_state_sigh(Signal_context_capability sigh)
{
warning("Session_component::link_state_sigh not implemented");
}
/**********
** Root **
**********/
Net::Root::Root(Entrypoint &ep,
Genode::Timer &timer,
Allocator &alloc,
Mac_address const &router_mac,
Configuration &config,
Ram_session &buf_ram)
:
Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_ep(ep), _router_mac(router_mac), _tcp_port_alloc(tcp_port_alloc),
_udp_port_alloc(udp_port_alloc), _tcp_proxys(tcp_proxys),
_udp_proxys(udp_proxys), _rtt_sec(rtt_sec),
_interface_tree(interface_tree), _arp_cache(arp_cache),
_arp_waiters(arp_waiters), _verbose(verbose)
Root_component<Session_component>(&ep.rpc_ep(), &alloc), _timer(timer),
_ep(ep), _router_mac(router_mac), _config(config), _buf_ram(buf_ram)
{ }
Session_component *Net::Root::_create_session(char const *args)
{
Ipv4_address src;
try {
Session_policy policy(label_from_args(args));
src = policy.attribute_value("src", Ipv4_address());
Session_label const label = label_from_args(args);
Session_policy policy(label, _config.node());
Domain_name domain_name(Cstring(policy.attribute("domain").value_base(),
policy.attribute("domain").value_size()));
} catch (Session_policy::No_policy_defined) {
Domain &domain = _config.domains().find_by_name(domain_name);
error("No matching policy");
throw Root::Unavailable();
size_t const ram_quota =
Arg_string::find_arg(args, "ram_quota").ulong_value(0);
size_t const tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
size_t const rx_buf_size =
Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
size_t const session_size =
max((size_t)4096, sizeof(Session_component));
if (ram_quota < session_size) {
throw Root::Quota_exceeded(); }
if (tx_buf_size > ram_quota - session_size ||
rx_buf_size > ram_quota - session_size ||
tx_buf_size + rx_buf_size > ram_quota - session_size)
{
error("insufficient 'ram_quota' for session creation");
throw Root::Quota_exceeded();
}
return new (md_alloc())
Session_component(*md_alloc(), _timer, ram_quota - session_size,
_buf_ram, tx_buf_size, rx_buf_size,
_mac_alloc.alloc(), _ep, _router_mac,
domain);
}
if (src == Ipv4_address()) {
error("Missing 'src' attribute in policy");
throw Root::Unavailable();
catch (Session_policy::No_policy_defined) {
error("no matching policy");
}
size_t const ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t const tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
size_t const rx_buf_size =
Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
size_t const session_size = max((size_t)4096, sizeof(Session_component));
if (ram_quota < session_size) {
throw Root::Quota_exceeded(); }
if (tx_buf_size > ram_quota - session_size ||
rx_buf_size > ram_quota - session_size ||
tx_buf_size + rx_buf_size > ram_quota - session_size)
{
error("insufficient 'ram_quota' for session creation");
throw Root::Quota_exceeded();
catch (Domain_tree::No_match) {
error("no matching domain");
}
Mac_address mac;
try { mac = _mac_alloc.alloc(); }
catch (Mac_allocator::Alloc_failed) {
error("failed to allocate MAC address");
throw Root::Unavailable();
}
return new (md_alloc())
Session_component(*env()->heap(), ram_quota - session_size,
tx_buf_size, rx_buf_size, mac, _ep, _router_mac,
src, args, _tcp_port_alloc, _udp_port_alloc,
_tcp_proxys, _udp_proxys, _rtt_sec, _interface_tree,
_arp_cache, _arp_waiters, _verbose);
catch (Xml_node::Nonexistent_attribute) {
error("missing domain attribute in policy");
}
catch (Pointer<Interface>::Valid) {
error("one session per domain only");
}
throw Root::Unavailable();
}
Tx_rx_communication_buffers::
Tx_rx_communication_buffers(Genode::size_t const tx_size,
Genode::size_t const rx_size)
:
_tx_buf(tx_size), _rx_buf(rx_size)
{ }
Communication_buffer::Communication_buffer(Genode::size_t size)
:
Ram_dataspace_capability(Genode::env()->ram_session()->alloc(size))
{ }
Range_allocator &Guarded_range_allocator::range_allocator()
{
return *static_cast<Range_allocator *>(&_range_alloc);
}
Guarded_range_allocator::
Guarded_range_allocator(Allocator &backing_store, size_t const amount)
:
_guarded_alloc(&backing_store, amount), _range_alloc(&_guarded_alloc)
{ }

View File

@ -15,123 +15,93 @@
#define _COMPONENT_H_
/* Genode includes */
#include <base/allocator_guard.h>
#include <root/component.h>
#include <nic/packet_allocator.h>
#include <nic_session/rpc_object.h>
#include <net/ipv4.h>
#include <base/allocator_guard.h>
#include <nic_bridge/mac_allocator.h>
/* local includes */
#include <mac_allocator.h>
#include <interface.h>
namespace Net {
class Port_allocator;
class Guarded_range_allocator;
class Domain;
class Communication_buffer;
class Tx_rx_communication_buffers;
class Session_component_base;
class Session_component;
class Root;
}
class Net::Guarded_range_allocator
class Net::Communication_buffer : public Genode::Ram_dataspace_capability
{
private:
Genode::Ram_session &_ram;
public:
Communication_buffer(Genode::Ram_session &ram,
Genode::size_t const size);
~Communication_buffer() { _ram.free(*this); }
};
class Net::Session_component_base
{
protected:
Genode::Allocator_guard _guarded_alloc;
Nic::Packet_allocator _range_alloc;
Communication_buffer _tx_buf;
Communication_buffer _rx_buf;
public:
Guarded_range_allocator(Genode::Allocator &backing_store,
Genode::size_t const amount);
/***************
** Accessors **
***************/
Genode::Allocator_guard &guarded_allocator() { return _guarded_alloc; }
Genode::Range_allocator &range_allocator();
Session_component_base(Genode::Allocator &guarded_alloc_backing,
Genode::size_t const guarded_alloc_amount,
Genode::Ram_session &buf_ram,
Genode::size_t const tx_buf_size,
Genode::size_t const rx_buf_size);
};
struct Net::Communication_buffer : Genode::Ram_dataspace_capability
{
Communication_buffer(Genode::size_t const size);
~Communication_buffer() { Genode::env()->ram_session()->free(*this); }
Genode::Dataspace_capability dataspace() { return *this; }
};
class Net::Tx_rx_communication_buffers
class Net::Session_component : public Session_component_base,
public ::Nic::Session_rpc_object,
public Interface
{
private:
Communication_buffer _tx_buf, _rx_buf;
/********************
** Net::Interface **
********************/
Packet_stream_sink &_sink() { return *_tx.sink(); }
Packet_stream_source &_source() { return *_rx.source(); }
public:
Tx_rx_communication_buffers(Genode::size_t const tx_size,
Genode::size_t const rx_size);
Genode::Dataspace_capability tx_ds() { return _tx_buf.dataspace(); }
Genode::Dataspace_capability rx_ds() { return _rx_buf.dataspace(); }
};
class Net::Session_component : public Guarded_range_allocator,
private Tx_rx_communication_buffers,
public ::Nic::Session_rpc_object,
public Interface
{
private:
void _arp_broadcast(Interface &interface, Ipv4_address ip_addr);
public:
Session_component(Genode::Allocator &allocator,
Genode::size_t amount,
Genode::size_t tx_buf_size,
Genode::size_t rx_buf_size,
Mac_address vmac,
Server::Entrypoint &ep,
Mac_address router_mac,
Ipv4_address router_ip,
char const *args,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Tcp_proxy_list &tcp_proxys,
Udp_proxy_list &udp_proxys,
unsigned rtt_sec,
Interface_tree &interface_tree,
Arp_cache &arp_cache,
Arp_waiter_list &arp_waiters,
bool verbose);
~Session_component();
Session_component(Genode::Allocator &alloc,
Genode::Timer &timer,
Genode::size_t const amount,
Genode::Ram_session &buf_ram,
Genode::size_t const tx_buf_size,
Genode::size_t const rx_buf_size,
Mac_address const mac,
Genode::Entrypoint &ep,
Mac_address const &router_mac,
Domain &domain);
/******************
** Nic::Session **
******************/
Mac_address mac_address() { return Interface::mac(); }
Mac_address mac_address() { return _mac; }
bool link_state();
void link_state_sigh(Genode::Signal_context_capability sigh);
/********************
** Net::Interface **
********************/
Packet_stream_sink<Nic::Session::Policy> *sink() { return _tx.sink(); }
Packet_stream_source<Nic::Session::Policy> *source() { return _rx.source(); }
};
@ -139,40 +109,28 @@ class Net::Root : public Genode::Root_component<Session_component>
{
private:
Mac_allocator _mac_alloc;
Server::Entrypoint &_ep;
Mac_address _router_mac;
Port_allocator &_tcp_port_alloc;
Port_allocator &_udp_port_alloc;
Tcp_proxy_list &_tcp_proxys;
Udp_proxy_list &_udp_proxys;
unsigned _rtt_sec;
Interface_tree &_interface_tree;
Arp_cache &_arp_cache;
Arp_waiter_list &_arp_waiters;
bool _verbose;
Genode::Timer &_timer;
Mac_allocator _mac_alloc;
Genode::Entrypoint &_ep;
Mac_address const _router_mac;
Configuration &_config;
Genode::Ram_session &_buf_ram;
/********************
** Root_component **
********************/
Session_component *_create_session(const char *args);
Session_component *_create_session(char const *args);
public:
Root(Server::Entrypoint &ep,
Genode::Allocator &md_alloc,
Mac_address router_mac,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Tcp_proxy_list &tcp_proxys,
Udp_proxy_list &udp_proxys,
unsigned rtt_sec,
Interface_tree &interface_tree,
Arp_cache &arp_cache,
Arp_waiter_list &arp_waiters,
bool verbose);
Root(Genode::Entrypoint &ep,
Genode::Timer &timer,
Genode::Allocator &alloc,
Mac_address const &router_mac,
Configuration &config,
Genode::Ram_session &buf_ram);
};
#endif /* _COMPONENT_H_ */

View File

@ -0,0 +1,62 @@
/*
* \brief Reflects the current router configuration through objects
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <configuration.h>
/* Genode includes */
#include <util/xml_node.h>
#include <base/allocator.h>
#include <base/log.h>
using namespace Net;
using namespace Genode;
/***************
** Utilities **
***************/
static unsigned read_rtt_sec(Xml_node const &node)
{
unsigned const rtt_sec = node.attribute_value("rtt_sec", 0UL);
if (!rtt_sec) {
warning("fall back to default rtt_sec=\"3\"");
return 3;
}
return rtt_sec;
}
/*******************
** Configuration **
*******************/
Configuration::Configuration(Xml_node const node, Allocator &alloc)
:
_alloc(alloc), _verbose(node.attribute_value("verbose", false)),
_rtt_sec(read_rtt_sec(node)), _node(node)
{
/* read domains */
node.for_each_sub_node("domain", [&] (Xml_node const &node) {
try { _domains.insert(*new (_alloc) Domain(*this, node, _alloc)); }
catch (Domain::Invalid) { warning("invalid domain"); }
});
/* as they must resolve domain names, create rules after domains */
_domains.for_each([&] (Domain &domain) {
if (_verbose) {
log("Domain: ", domain); }
domain.create_rules(_domains);
});
}

View File

@ -0,0 +1,50 @@
/*
* \brief Reflects the current router configuration through objects
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _CONFIGURATION_H_
#define _CONFIGURATION_H_
/* local includes */
#include <domain.h>
namespace Genode { class Allocator; }
namespace Net { class Configuration; }
class Net::Configuration
{
private:
Genode::Allocator &_alloc;
bool const _verbose;
unsigned const _rtt_sec;
Domain_tree _domains;
Genode::Xml_node const _node;
public:
Configuration(Genode::Xml_node const node, Genode::Allocator &alloc);
/***************
** Accessors **
***************/
bool verbose() const { return _verbose; }
unsigned rtt_sec() const { return _rtt_sec; }
Domain_tree &domains() { return _domains; }
Genode::Xml_node node() const { return _node; }
};
#endif /* _CONFIGURATION_H_ */

View File

@ -0,0 +1,36 @@
/*
* \brief Routing rule for direct traffic between two interfaces
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <direct_rule.h>
/* Genode includes */
#include <util/xml_node.h>
using namespace Net;
using namespace Genode;
Direct_rule_base::Direct_rule_base(Xml_node const &node)
:
_dst(node.attribute_value("dst", Ipv4_address_prefix()))
{
if (!_dst.valid()) {
throw Invalid(); }
}
void Direct_rule_base::print(Output &output) const
{
Genode::print(output, _dst);
}

View File

@ -0,0 +1,99 @@
/*
* \brief Routing rule for direct traffic between two interfaces
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _DIRECT_RULE_H_
#define _DIRECT_RULE_H_
/* Genode includes */
#include <net/ipv4.h>
#include <util/list.h>
/* local includes */
#include <rule.h>
namespace Genode { class Xml_node; }
namespace Net {
class Direct_rule_base;
template <typename> class Direct_rule;
template <typename> class Direct_rule_list;
}
class Net::Direct_rule_base : public Rule
{
protected:
Ipv4_address_prefix const _dst;
public:
Direct_rule_base(Genode::Xml_node const &node);
/*********
** log **
*********/
void print(Genode::Output &output) const;
/***************
** Accessors **
***************/
Ipv4_address_prefix const &dst() const { return _dst; }
};
template <typename T>
struct Net::Direct_rule : Direct_rule_base,
Direct_rule_list<T>::Element
{
Direct_rule(Genode::Xml_node const &node) : Direct_rule_base(node) { }
};
template <typename T>
struct Net::Direct_rule_list : Genode::List<T>
{
using List = Genode::List<T>;
struct No_match : Genode::Exception { };
T const &longest_prefix_match(Ipv4_address const &ip) const
{
/* first match is sufficient as the list is prefix-size-sorted */
for (T const *curr = List::first(); curr; curr = curr->next()) {
if (curr->dst().prefix_matches(ip)) {
return *curr; }
}
throw No_match();
}
void insert(T &rule)
{
/* ensure that the list stays prefix-size-sorted (descending) */
T *behind = nullptr;
for (T *curr = List::first(); curr; curr = curr->next()) {
if (rule.dst().prefix >= curr->dst().prefix) {
break; }
behind = curr;
}
List::insert(&rule, behind);
}
};
#endif /* _RULE_H_ */

View File

@ -0,0 +1,165 @@
/*
* \brief Reflects an effective domain configuration node
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <configuration.h>
#include <protocol_name.h>
/* Genode includes */
#include <util/xml_node.h>
#include <base/allocator.h>
#include <base/log.h>
using namespace Net;
using namespace Genode;
/***********************
** Domain_avl_member **
***********************/
Domain_avl_member::Domain_avl_member(Domain_name const &name,
Domain &domain)
:
Avl_string_base(name.string()), _domain(domain)
{ }
/*****************
** Domain_base **
*****************/
Domain_base::Domain_base(Xml_node const &node)
:
_name(Cstring(node.attribute("name").value_base(),
node.attribute("name").value_size()))
{ }
/************
** Domain **
************/
void Domain::_read_forward_rules(Cstring const &protocol,
Domain_tree &domains,
Xml_node const &node,
char const *type,
Forward_rule_tree &rules)
{
node.for_each_sub_node(type, [&] (Xml_node const &node) {
try {
Forward_rule &rule = *new (_alloc) Forward_rule(domains, node);
rules.insert(&rule);
if (_config.verbose()) {
log(" Forward rule: ", protocol, " ", rule); }
}
catch (Rule::Invalid) { warning("invalid forward rule"); }
});
}
void Domain::_read_transport_rules(Cstring const &protocol,
Domain_tree &domains,
Xml_node const &node,
char const *type,
Transport_rule_list &rules)
{
node.for_each_sub_node(type, [&] (Xml_node const &node) {
try {
rules.insert(*new (_alloc) Transport_rule(domains, node, _alloc,
protocol, _config));
}
catch (Rule::Invalid) { warning("invalid transport rule"); }
});
}
void Domain::print(Output &output) const
{
Genode::print(output, "\"", _name, "\"");
}
Domain::Domain(Configuration &config, Xml_node const &node, Allocator &alloc)
:
Domain_base(node), _avl_member(_name, *this), _config(config),
_node(node), _alloc(alloc),
_interface_attr(node.attribute_value("interface", Ipv4_address_prefix())),
_gateway(node.attribute_value("gateway", Ipv4_address())),
_gateway_valid(_gateway.valid())
{
if (_name == Domain_name() || !_interface_attr.valid() ||
(_gateway_valid && !_interface_attr.prefix_matches(_gateway)))
{
throw Invalid();
}
}
void Domain::create_rules(Domain_tree &domains)
{
/* read forward rules */
_read_forward_rules(tcp_name(), domains, _node, "tcp-forward",
_tcp_forward_rules);
_read_forward_rules(udp_name(), domains, _node, "udp-forward",
_udp_forward_rules);
/* read UDP and TCP rules */
_read_transport_rules(tcp_name(), domains, _node, "tcp", _tcp_rules);
_read_transport_rules(udp_name(), domains, _node, "udp", _udp_rules);
/* read NAT rules */
_node.for_each_sub_node("nat", [&] (Xml_node const &node) {
try {
_nat_rules.insert(
new (_alloc) Nat_rule(domains, _tcp_port_alloc,
_udp_port_alloc, node));
}
catch (Rule::Invalid) { warning("invalid NAT rule"); }
});
/* read IP rules */
_node.for_each_sub_node("ip", [&] (Xml_node const &node) {
try { _ip_rules.insert(*new (_alloc) Ip_rule(domains, node)); }
catch (Rule::Invalid) { warning("invalid IP rule"); }
});
}
Ipv4_address const &Domain::next_hop(Ipv4_address const &ip) const
{
if (_interface_attr.prefix_matches(ip)) { return ip; }
if (_gateway_valid) { return _gateway; }
throw No_next_hop();
}
/*****************
** Domain_tree **
*****************/
Domain &Domain_tree::domain(Avl_string_base const &node)
{
return static_cast<Domain_avl_member const *>(&node)->domain();
}
Domain &Domain_tree::find_by_name(Domain_name name)
{
if (name == Domain_name() || !first()) {
throw No_match(); }
Avl_string_base *node = first()->find_by_name(name.string());
if (!node) {
throw No_match(); }
return domain(*node);
}

View File

@ -0,0 +1,165 @@
/*
* \brief Reflects an effective domain configuration node
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _DOMAIN_H_
#define _DOMAIN_H_
/* local includes */
#include <forward_rule.h>
#include <transport_rule.h>
#include <nat_rule.h>
#include <ip_rule.h>
#include <port_allocator.h>
#include <pointer.h>
/* Genode includes */
#include <util/avl_string.h>
#include <util/xml_node.h>
namespace Genode { class Allocator; }
namespace Net {
class Interface;
class Configuration;
class Domain_avl_member;
class Domain_base;
class Domain;
class Domain_tree;
using Domain_name = Genode::String<160>;
}
class Net::Domain_avl_member : public Genode::Avl_string_base
{
private:
Domain &_domain;
public:
Domain_avl_member(Domain_name const &name,
Domain &domain);
/***************
** Accessors **
***************/
Domain &domain() const { return _domain; }
};
class Net::Domain_base
{
protected:
Domain_name const _name;
Domain_base(Genode::Xml_node const &node);
};
class Net::Domain : public Domain_base
{
private:
Domain_avl_member _avl_member;
Configuration &_config;
Genode::Xml_node _node;
Genode::Allocator &_alloc;
Ipv4_address_prefix _interface_attr;
Ipv4_address const _gateway;
bool const _gateway_valid;
Ip_rule_list _ip_rules;
Forward_rule_tree _tcp_forward_rules;
Forward_rule_tree _udp_forward_rules;
Transport_rule_list _tcp_rules;
Transport_rule_list _udp_rules;
Port_allocator _tcp_port_alloc;
Port_allocator _udp_port_alloc;
Nat_rule_tree _nat_rules;
Pointer<Interface> _interface;
void _read_forward_rules(Genode::Cstring const &protocol,
Domain_tree &domains,
Genode::Xml_node const &node,
char const *type,
Forward_rule_tree &rules);
void _read_transport_rules(Genode::Cstring const &protocol,
Domain_tree &domains,
Genode::Xml_node const &node,
char const *type,
Transport_rule_list &rules);
public:
struct Invalid : Genode::Exception { };
struct No_next_hop : Genode::Exception { };
Domain(Configuration &config,
Genode::Xml_node const &node,
Genode::Allocator &alloc);
void create_rules(Domain_tree &domains);
Ipv4_address const &next_hop(Ipv4_address const &ip) const;
/*********
** log **
*********/
void print(Genode::Output &output) const;
/***************
** Accessors **
***************/
Domain_name const &name() { return _name; }
Ip_rule_list &ip_rules() { return _ip_rules; }
Forward_rule_tree &tcp_forward_rules() { return _tcp_forward_rules; }
Forward_rule_tree &udp_forward_rules() { return _udp_forward_rules; }
Transport_rule_list &tcp_rules() { return _tcp_rules; }
Transport_rule_list &udp_rules() { return _udp_rules; }
Nat_rule_tree &nat_rules() { return _nat_rules; }
Ipv4_address_prefix &interface_attr() { return _interface_attr; }
Pointer<Interface> &interface() { return _interface; }
Configuration &config() const { return _config; }
Domain_avl_member &avl_member() { return _avl_member; }
};
struct Net::Domain_tree : Genode::Avl_tree<Genode::Avl_string_base>
{
using Avl_tree = Genode::Avl_tree<Genode::Avl_string_base>;
struct No_match : Genode::Exception { };
static Domain &domain(Genode::Avl_string_base const &node);
Domain &find_by_name(Domain_name name);
template <typename FUNC>
void for_each(FUNC && functor) const {
Avl_tree::for_each([&] (Genode::Avl_string_base const &node) {
functor(domain(node));
});
}
void insert(Domain &domain) { Avl_tree::insert(&domain.avl_member()); }
};
#endif /* _DOMAIN_H_ */

View File

@ -0,0 +1,70 @@
/*
* \brief Rule for forwarding a TCP/UDP port of the router to an interface
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <forward_rule.h>
#include <domain.h>
/* Genode includes */
#include <util/xml_node.h>
using namespace Net;
using namespace Genode;
/******************
** Forward_rule **
******************/
void Forward_rule::print(Output &output) const
{
Genode::print(output, "port ", _port, " requests to ", _to,
" at ", _domain);
}
Forward_rule::Forward_rule(Domain_tree &domains, Xml_node const &node)
:
Leaf_rule(domains, node),
_port(node.attribute_value("port", 0UL)),
_to(node.attribute_value("to", Ipv4_address()))
{
if (!_port || !_to.valid() || dynamic_port(_port)) {
throw Invalid(); }
}
Forward_rule const &Forward_rule::find_by_port(uint8_t const port) const
{
if (port == _port) {
return *this; }
Forward_rule *const rule = Avl_node<Forward_rule>::child(port > _port);
if (!rule) {
throw Forward_rule_tree::No_match(); }
return rule->find_by_port(port);
}
/***********************
** Forward_rule_tree **
***********************/
Forward_rule const &Forward_rule_tree::find_by_port(uint8_t const port) const
{
if (!first()) {
throw No_match(); }
return first()->find_by_port(port);
}

View File

@ -0,0 +1,77 @@
/*
* \brief Rule for forwarding a TCP/UDP port of the router to an interface
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _FORWARD_RULE_H_
#define _FORWARD_RULE_H_
/* local includes */
#include <leaf_rule.h>
/* Genode includes */
#include <util/avl_tree.h>
#include <net/ipv4.h>
namespace Net {
class Forward_rule;
class Forward_rule_tree;
class Forward_link;
class Forward_link_tree;
}
class Net::Forward_rule : public Leaf_rule,
public Genode::Avl_node<Forward_rule>
{
private:
Genode::uint8_t const _port;
Ipv4_address const _to;
public:
Forward_rule(Domain_tree &domains, Genode::Xml_node const &node);
Forward_rule const &find_by_port(Genode::uint8_t const port) const;
/*********
** log **
*********/
void print(Genode::Output &output) const;
/**************
** Avl_node **
**************/
bool higher(Forward_rule *rule) { return rule->_port > _port; }
/***************
** Accessors **
***************/
Ipv4_address const &to() const { return _to; }
};
struct Net::Forward_rule_tree : Genode::Avl_tree<Forward_rule>
{
struct No_match : Genode::Exception { };
Forward_rule const &find_by_port(Genode::uint8_t const port) const;
};
#endif /* _FORWARD_RULE_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -14,192 +14,168 @@
#ifndef _INTERFACE_H_
#define _INTERFACE_H_
/* Genode includes */
#include <os/server.h>
#include <os/session_policy.h>
#include <util/avl_string.h>
#include <nic_session/nic_session.h>
/* local includes */
#include <ip_route.h>
#include <link.h>
#include <arp_cache.h>
#include <arp_waiter.h>
/* Genode includes */
#include <nic_session/nic_session.h>
namespace Net {
using ::Nic::Packet_stream_sink;
using ::Nic::Packet_stream_source;
using ::Nic::Packet_descriptor;
using Packet_descriptor = ::Nic::Packet_descriptor;
using Packet_stream_sink = ::Nic::Packet_stream_sink< ::Nic::Session::Policy>;
using Packet_stream_source = ::Nic::Packet_stream_source< ::Nic::Session::Policy>;
class Forward_rule_tree;
class Transport_rule_list;
class Ethernet_frame;
class Arp_packet;
class Arp_waiter;
class Arp_cache;
class Arp_cache_entry;
class Port_allocator;
class Tcp_proxy;
class Udp_proxy;
class Interface;
class Interface_tree;
using Interface_list = Genode::List<Interface>;
using Arp_waiter_list = Genode::List<Arp_waiter>;
using Tcp_proxy_list = Genode::List<Tcp_proxy>;
using Udp_proxy_list = Genode::List<Udp_proxy>;
using Signal_rpc_member = Genode::Signal_rpc_member<Interface>;
class Configuration;
class Domain;
}
class Net::Interface : public Genode::Session_label, public Genode::Avl_string_base
class Net::Interface
{
protected:
Signal_rpc_member _sink_ack;
Signal_rpc_member _sink_submit;
Signal_rpc_member _source_ack;
Signal_rpc_member _source_submit;
using Signal_handler = Genode::Signal_handler<Interface>;
Signal_handler _sink_ack;
Signal_handler _sink_submit;
Signal_handler _source_ack;
Signal_handler _source_submit;
Mac_address const _router_mac;
Mac_address const _mac;
private:
Packet_descriptor _packet;
Genode::Entrypoint &_ep;
Ip_route_list _ip_routes;
Mac_address const _router_mac;
Ipv4_address const _router_ip;
Mac_address const _mac;
Genode::Allocator &_allocator;
Genode::Session_policy _policy;
bool const _proxy;
unsigned _tcp_proxy;
unsigned _tcp_proxy_used;
Tcp_proxy_list &_tcp_proxies;
Port_allocator &_tcp_port_alloc;
unsigned _udp_proxy;
unsigned _udp_proxy_used;
Udp_proxy_list &_udp_proxies;
Port_allocator &_udp_port_alloc;
unsigned const _rtt_sec;
Interface_tree &_interface_tree;
Arp_cache &_arp_cache;
Arp_waiter_list &_arp_waiters;
bool _verbose;
Genode::Timer &_timer;
Genode::Allocator &_alloc;
Domain &_domain;
Arp_cache _arp_cache;
Arp_waiter_list _own_arp_waiters;
Arp_waiter_list _foreign_arp_waiters;
Link_side_tree _tcp_links;
Link_side_tree _udp_links;
Link_list _closed_tcp_links;
Link_list _closed_udp_links;
void _read_route(Genode::Xml_node &route_xn);
void _new_link(Genode::uint8_t const protocol,
Link_side_id const &local_id,
Pointer<Port_allocator_guard> const remote_port_alloc,
Interface &remote_interface,
Link_side_id const &remote_id);
Tcp_proxy *_find_tcp_proxy_by_client(Ipv4_address ip,
Genode::uint16_t port);
Forward_rule_tree &_forward_rules(Genode::uint8_t const prot) const;
Udp_proxy *_find_udp_proxy_by_client(Ipv4_address ip,
Genode::uint16_t port);
Transport_rule_list &_transport_rules(Genode::uint8_t const prot) const;
Interface *_tlp_proxy_route(Genode::uint8_t tlp, void *ptr,
Genode::uint16_t &dst_port,
Ipv4_packet *ip, Ipv4_address &to,
Ipv4_address &via);
void _handle_arp(Ethernet_frame &eth, Genode::size_t const eth_size);
void _tlp_apply_port_proxy(Genode::uint8_t tlp, void *tlp_ptr,
Ipv4_packet *ip, Ipv4_address client_ip,
Genode::uint16_t src_port);
void _handle_arp_reply(Arp_packet &arp);
Tcp_proxy *_find_tcp_proxy_by_proxy(Ipv4_address ip,
Genode::uint16_t port);
void _handle_arp_request(Ethernet_frame &eth,
Genode::size_t const eth_size,
Arp_packet &arp);
Udp_proxy *_find_udp_proxy_by_proxy(Ipv4_address ip,
Genode::uint16_t port);
void _handle_ip(Ethernet_frame &eth,
Genode::size_t const eth_size,
Packet_descriptor const &pkt);
void _handle_arp_reply(Arp_packet * const arp);
void _adapt_eth(Ethernet_frame &eth,
Genode::size_t const eth_size,
Ipv4_address const &ip,
Packet_descriptor const &pkt,
Interface &interface);
void _handle_arp_request(Ethernet_frame * const eth,
Genode::size_t const eth_size,
Arp_packet * const arp);
void _nat_link_and_pass(Ethernet_frame &eth,
Genode::size_t const eth_size,
Ipv4_packet &ip,
Genode::uint8_t const prot,
void *const prot_base,
Genode::size_t const prot_size,
Link_side_id const &local_id,
Interface &interface);
Arp_waiter *_new_arp_entry(Arp_waiter *arp_waiter,
Arp_cache_entry *entry);
void _broadcast_arp_request(Ipv4_address const &ip);
void _remove_arp_waiter(Arp_waiter *arp_waiter);
void _send(Ethernet_frame &eth, Genode::size_t const eth_size);
void _handle_arp(Ethernet_frame *eth, Genode::size_t size);
void _pass_ip(Ethernet_frame &eth,
Genode::size_t const eth_size,
Ipv4_packet &ip,
Genode::uint8_t const prot,
void *const prot_base,
Genode::size_t const prot_size);
void _handle_ip(Ethernet_frame *eth, Genode::size_t eth_size,
bool &ack_packet, Packet_descriptor *packet);
void _continue_handle_eth(Packet_descriptor const &pkt);
Tcp_proxy *_new_tcp_proxy(unsigned const client_port,
Ipv4_address client_ip,
Ipv4_address proxy_ip);
Link_list &_closed_links(Genode::uint8_t const protocol);
Udp_proxy *_new_udp_proxy(unsigned const client_port,
Ipv4_address client_ip,
Ipv4_address proxy_ip);
Link_side_tree &_links(Genode::uint8_t const protocol);
void _delete_tcp_proxy(Tcp_proxy * const proxy);
Configuration &_config() const;
bool _chk_delete_tcp_proxy(Tcp_proxy * &proxy);
Ipv4_address const &_router_ip() const;
void _delete_udp_proxy(Udp_proxy * const proxy);
void _handle_eth(void *const eth_base,
Genode::size_t const eth_size,
Packet_descriptor const &pkt);
bool _chk_delete_udp_proxy(Udp_proxy * &proxy);
void _ack_packet(Packet_descriptor const &pkt);
void _cancel_arp_waiting(Arp_waiter &waiter);
virtual Packet_stream_sink &_sink() = 0;
virtual Packet_stream_source &_source() = 0;
/***********************************
** Packet-stream signal handlers **
***********************************/
void _ready_to_submit(unsigned);
void _ack_avail(unsigned) { }
void _ready_to_ack(unsigned);
void _packet_avail(unsigned) { }
void _ready_to_submit();
void _ack_avail() { }
void _ready_to_ack();
void _packet_avail() { }
public:
struct Too_many_tcp_proxies : Genode::Exception { };
struct Too_many_udp_proxies : Genode::Exception { };
struct Bad_transport_protocol : Genode::Exception { };
struct Bad_network_protocol : Genode::Exception { };
struct Packet_postponed : Genode::Exception { };
Interface(Server::Entrypoint &ep,
Mac_address const router_mac,
Ipv4_address const router_ip,
Genode::Allocator &allocator,
char const *args,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Mac_address const mac,
Tcp_proxy_list &tcp_proxies,
Udp_proxy_list &udp_proxies,
unsigned const rtt_sec,
Interface_tree &interface_tree,
Arp_cache &arp_cache,
Arp_waiter_list &arp_waiters,
bool verbose);
Interface(Genode::Entrypoint &ep,
Genode::Timer &timer,
Mac_address const router_mac,
Genode::Allocator &alloc,
Mac_address const mac,
Domain &domain);
~Interface();
void arp_broadcast(Ipv4_address ip_addr);
void link_closed(Link &link, Genode::uint8_t const prot);
void send(Ethernet_frame *eth, Genode::size_t eth_size);
void dissolve_link(Link_side &link_side, Genode::uint8_t const prot);
void handle_ethernet(void *src, Genode::size_t size, bool &ack,
Packet_descriptor *packet);
void continue_handle_ethernet(void *src, Genode::size_t size,
Packet_descriptor *packet);
/*********
** log **
*********/
void print(Genode::Output &output) const;
/***************
** Accessors **
***************/
Mac_address router_mac() const { return _router_mac; }
Mac_address mac() const { return _mac; }
Ipv4_address router_ip() const { return _router_ip; }
Ip_route_list &ip_routes() { return _ip_routes; }
Genode::Allocator &allocator() const { return _allocator; }
Session_label &label() { return *this; }
virtual Packet_stream_sink< ::Nic::Session::Policy> *sink() = 0;
virtual Packet_stream_source< ::Nic::Session::Policy> *source() = 0;
};
struct Net::Interface_tree : Genode::Avl_tree<Genode::Avl_string_base>
{
Interface *find_by_label(char const *label);
Arp_waiter_list &own_arp_waiters() { return _own_arp_waiters; }
Arp_waiter_list &foreign_arp_waiters() { return _foreign_arp_waiters; }
};
#endif /* _INTERFACE_H_ */

View File

@ -1,141 +0,0 @@
/*
* \brief IP routing entry
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <net/ipv4.h>
#include <util/xml_node.h>
#include <base/allocator.h>
#include <base/log.h>
/* local includes */
#include <ip_route.h>
using namespace Net;
using namespace Genode;
bool Ip_route::matches(Ipv4_address ip_addr)
{
if (memcmp(&ip_addr.addr, _ip_addr.addr, _prefix_bytes)) {
return false; }
if (!_prefix_tail) {
return true; }
uint8_t ip_tail = ip_addr.addr[_prefix_bytes] & _prefix_tail;
if (ip_tail != _ip_addr.addr[_prefix_bytes]) {
return false; }
return true;
}
void Ip_route::print(Output &output) const
{
Genode::print(output, _ip_addr, "/", _prefix, " -> \"",
_label, "\" to ", _to, " via ", _via);
}
void Ip_route::_read_tcp_port(Xml_node &port, Allocator &alloc)
{
uint16_t const dst = port.attribute_value("dst", 0UL);
if (!dst) {
return; }
Port_route *port_route;
try {
char const *label = port.attribute("label").value_base();
size_t label_size = port.attribute("label").value_size();
Ipv4_address const via = port.attribute_value("via", Ipv4_address());
Ipv4_address const to = port.attribute_value("to", Ipv4_address());
port_route = new (&alloc) Port_route(dst, label, label_size, via, to);
} catch (Xml_attribute::Nonexistent_attribute) {
Ipv4_address const via = port.attribute_value("via", Ipv4_address());
Ipv4_address const to = port.attribute_value("to", Ipv4_address());
port_route = new (alloc) Port_route(dst, "", 0, via, to);
}
_tcp_port_tree.insert(port_route);
_tcp_port_list.insert(port_route);
if (_verbose) { log(" TCP port route: ", *port_route); }
}
void Ip_route::_read_udp_port(Xml_node &port, Allocator &alloc)
{
uint16_t const dst = port.attribute_value("dst", 0UL);
if (!dst) {
warning("missing 'dst' attribute in port route");
return;
}
char const *label = port.attribute("label").value_base();
size_t label_size = port.attribute("label").value_size();
Ipv4_address const via = port.attribute_value("via", Ipv4_address());
Ipv4_address const to = port.attribute_value("to", Ipv4_address());
Port_route *port_route = new (alloc)
Port_route(dst, label, label_size, via, to);
_udp_port_tree.insert(port_route);
_udp_port_list.insert(port_route);
if (_verbose) {
log(" UDP port route: ", *port_route); }
}
Ip_route::Ip_route(Ipv4_address ip_addr, uint8_t prefix, Ipv4_address via,
Ipv4_address to, char const *label, size_t label_size,
Allocator &alloc, Xml_node &route, bool verbose)
:
_ip_addr(ip_addr), _prefix(prefix), _prefix_bytes(_prefix / 8),
_prefix_tail(~(((uint8_t)~0) >> (_prefix - (_prefix_bytes * 8)))),
_via(via), _to(to), _label(Genode::Cstring(label, label_size)),
_verbose(verbose)
{
if (_prefix_bytes < sizeof(ip_addr.addr)) {
_ip_addr.addr[_prefix_bytes] &= _prefix_tail; }
try {
Xml_node port = route.sub_node("tcp");
for (; ; port = port.next("tcp")) { _read_tcp_port(port, alloc); }
} catch (Xml_node::Nonexistent_sub_node) { }
try {
Xml_node port = route.sub_node("udp");
for (; ; port = port.next("udp")) { _read_udp_port(port, alloc); }
} catch (Xml_node::Nonexistent_sub_node) { }
}
Ip_route *Ip_route_list::longest_prefix_match(Ipv4_address ip_addr)
{
for (Ip_route *curr = first(); curr; curr = curr->next()) {
if (curr->matches(ip_addr)) {
return curr; }
}
return nullptr;
}
void Ip_route_list::insert(Ip_route *route)
{
Ip_route *behind = nullptr;
for (Ip_route *curr = first(); curr; curr = curr->next()) {
if (route->prefix() >= curr->prefix()) {
break; }
behind = curr;
}
Genode::List<Ip_route>::insert(route, behind);
}

View File

@ -1,89 +0,0 @@
/*
* \brief IP routing entry
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <port_route.h>
#ifndef _IP_ROUTE_H_
#define _IP_ROUTE_H_
namespace Genode {
class Xml_node;
class Allocator;
}
namespace Net {
class Ip_route;
class Ip_route_list;
}
class Net::Ip_route : public Genode::List<Ip_route>::Element
{
private:
Ipv4_address _ip_addr;
Genode::uint8_t _prefix;
Genode::uint8_t _prefix_bytes;
Genode::uint8_t _prefix_tail;
Ipv4_address _via;
Ipv4_address _to;
Genode::Session_label _label;
Port_route_tree _udp_port_tree;
Port_route_tree _tcp_port_tree;
Port_route_list _udp_port_list;
Port_route_list _tcp_port_list;
bool _verbose;
void _read_tcp_port(Genode::Xml_node &port, Genode::Allocator &alloc);
void _read_udp_port(Genode::Xml_node &port, Genode::Allocator &alloc);
public:
Ip_route(Ipv4_address ip_addr, Genode::uint8_t prefix,
Ipv4_address via, Ipv4_address to, char const *label,
Genode::size_t label_size, Genode::Allocator &alloc,
Genode::Xml_node &route, bool verbose);
void print(Genode::Output &output) const;
bool matches(Ipv4_address ip_addr);
/***************
** Accessors **
***************/
Ipv4_address ip_addr() const { return _ip_addr; }
Ipv4_address via() const { return _via; }
Ipv4_address to() const { return _to; }
Genode::uint8_t prefix() const { return _prefix; }
Genode::Session_label &label() { return _label; }
Port_route_tree *tcp_port_tree() { return &_tcp_port_tree; }
Port_route_tree *udp_port_tree() { return &_udp_port_tree; }
Port_route_list *tcp_port_list() { return &_tcp_port_list; }
Port_route_list *udp_port_list() { return &_udp_port_list; }
};
class Net::Ip_route_list : public Genode::List<Ip_route>
{
public:
Ip_route *longest_prefix_match(Ipv4_address ip_addr);
void insert(Ip_route *route);
};
#endif /* _IP_ROUTE_H_ */

View File

@ -0,0 +1,25 @@
/*
* \brief IP routing entry
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <ip_rule.h>
using namespace Net;
using namespace Genode;
Ip_rule::Ip_rule(Domain_tree &domains, Xml_node const &node)
:
Leaf_rule(domains, node),
Direct_rule(node)
{ }

View File

@ -0,0 +1,35 @@
/*
* \brief IP routing entry
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _IP_RULE_H_
#define _IP_RULE_H_
/* local includes */
#include <leaf_rule.h>
#include <direct_rule.h>
namespace Net {
class Ip_rule;
struct Ip_rule_list : Direct_rule_list<Ip_rule> { };
}
struct Net::Ip_rule : Leaf_rule, Direct_rule<Ip_rule>
{
public:
Ip_rule(Domain_tree &domains, Genode::Xml_node const &node);
};
#endif /* _IP_RULE_H_ */

View File

@ -0,0 +1,37 @@
/*
* \brief Routing rule that defines a target interface
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <leaf_rule.h>
#include <domain.h>
using namespace Net;
using namespace Genode;
Domain &Leaf_rule::_find_domain(Domain_tree &domains,
Xml_node const &node)
{
try {
return domains.find_by_name(
Cstring(node.attribute("domain").value_base(),
node.attribute("domain").value_size()));
}
catch (Domain_tree::No_match) { throw Invalid(); }
}
Leaf_rule::Leaf_rule(Domain_tree &domains, Xml_node const &node)
:
_domain(_find_domain(domains, node))
{ }

View File

@ -0,0 +1,50 @@
/*
* \brief Routing rule that defines a target interface
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _LEAF_RULE_H_
#define _LEAF_RULE_H_
/* local includes */
#include <rule.h>
namespace Genode { class Xml_node; }
namespace Net {
class Domain;
class Domain_tree;
class Leaf_rule;
}
class Net::Leaf_rule : public Rule
{
protected:
Domain &_domain;
static Domain &_find_domain(Domain_tree &domains,
Genode::Xml_node const &node);
public:
Leaf_rule(Domain_tree &domains, Genode::Xml_node const &node);
/***************
** Accessors **
***************/
Domain &domain() const { return _domain; }
};
#endif /* _LEAF_RULE_H_ */

View File

@ -0,0 +1,237 @@
/*
* \brief State tracking for UDP/TCP connections
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <net/tcp.h>
/* local includes */
#include <link.h>
#include <interface.h>
#include <configuration.h>
#include <protocol_name.h>
using namespace Net;
using namespace Genode;
/******************
** Link_side_id **
******************/
constexpr size_t Link_side_id::data_size()
{
return sizeof(src_ip) + sizeof(src_port) +
sizeof(dst_ip) + sizeof(dst_port);
}
bool Link_side_id::operator == (Link_side_id const &id) const
{
return memcmp(id.data, data, data_size()) == 0;
}
bool Link_side_id::operator > (Link_side_id const &id) const
{
return memcmp(id.data, data, data_size()) > 0;
}
/***************
** Link_side **
***************/
Link_side::Link_side(Interface &interface,
Link_side_id const &id,
Link &link)
:
_interface(interface), _id(id), _link(link)
{ }
Link_side const &Link_side::find_by_id(Link_side_id const &id) const
{
if (id == _id) {
return *this; }
bool const side = id > _id;
Link_side *const link_side = Avl_node<Link_side>::child(side);
if (!link_side) {
throw Link_side_tree::No_match(); }
return link_side->find_by_id(id);
}
void Link_side::print(Output &output) const
{
Genode::print(output, "src ", src_ip(), ":", src_port(),
" dst ", dst_ip(), ":", dst_port());
}
bool Link_side::is_client() const
{
return this == &_link.client();
}
/********************
** Link_side_tree **
********************/
Link_side const &Link_side_tree::find_by_id(Link_side_id const &id) const
{
Link_side *const link_side = first();
if (!link_side) {
throw No_match(); }
return link_side->find_by_id(id);
}
/**********
** Link **
**********/
void Link::print(Output &output) const
{
Genode::print(output, "CLN ", _client, " SRV ", _server);
}
Link::Link(Interface &cln_interface,
Link_side_id const &cln_id,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Configuration &config,
uint8_t const protocol)
:
_config(config),
_client(cln_interface, cln_id, *this),
_server_port_alloc(srv_port_alloc),
_server(srv_interface, srv_id, *this),
_close_timeout(timer, *this, &Link::_handle_close_timeout),
_close_timeout_us(_config.rtt_sec() * 2 * 1000 * 1000),
_protocol(protocol)
{
_close_timeout.start(_close_timeout_us);
}
void Link::_handle_close_timeout(Genode::Timer::Microseconds)
{
dissolve();
_client._interface.link_closed(*this, _protocol);
}
void Link::dissolve()
{
_client._interface.dissolve_link(_client, _protocol);
_server._interface.dissolve_link(_server, _protocol);
if (_config.verbose()) {
log("Dissolve ", protocol_name(_protocol), " link: ", *this); }
try {
_server_port_alloc.deref().free(_server.dst_port());
if (_config.verbose()) {
log("Free ", protocol_name(_protocol),
" port ", _server.dst_port(),
" at ", _server.interface(),
" that was used by ", _client.interface());
}
}
catch (Pointer<Port_allocator_guard>::Invalid) { }
}
/**************
** Tcp_link **
**************/
Tcp_link::Tcp_link(Interface &cln_interface,
Link_side_id const &cln_id,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Configuration &config,
uint8_t const protocol)
:
Link(cln_interface, cln_id, srv_port_alloc, srv_interface, srv_id, timer,
config, protocol)
{ }
void Tcp_link::_fin_acked()
{
if (_server_fin_acked && _client_fin_acked) {
_close_timeout.start(_close_timeout_us);
_closed = true;
}
}
void Tcp_link::server_packet(Tcp_packet &tcp)
{
if (_closed) {
return; }
if (tcp.fin()) {
_server_fin = true; }
if (tcp.ack() && _client_fin) {
_client_fin_acked = true;
_fin_acked();
}
if (!_closed) {
_packet(); }
}
void Tcp_link::client_packet(Tcp_packet &tcp)
{
if (_closed) {
return; }
if (tcp.fin()) {
_client_fin = true; }
if (tcp.ack() && _server_fin) {
_server_fin_acked = true;
_fin_acked();
}
if (!_closed) {
_packet(); }
}
/**************
** Udp_link **
**************/
Udp_link::Udp_link(Interface &cln_interface,
Link_side_id const &cln_id,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Configuration &config,
uint8_t const protocol)
:
Link(cln_interface, cln_id, srv_port_alloc, srv_interface, srv_id, timer,
config, protocol)
{ }

View File

@ -0,0 +1,213 @@
/*
* \brief State tracking for UDP/TCP connections
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _LINK_H_
#define _LINK_H_
/* Genode includes */
#include <os/timer.h>
#include <util/avl_tree.h>
#include <util/list.h>
#include <net/ipv4.h>
/* local includes */
#include <pointer.h>
namespace Net {
class Configuration;
class Port_allocator_guard;
class Tcp_packet;
class Interface;
class Link_side_id;
class Link_side;
class Link_side_tree;
class Link;
struct Link_list : Genode::List<Link> { };
class Tcp_link;
class Udp_link;
}
struct Net::Link_side_id
{
int const data[];
Ipv4_address const src_ip;
Genode::uint16_t const src_port;
Ipv4_address const dst_ip;
Genode::uint16_t const dst_port;
static constexpr Genode::size_t data_size();
/************************
** Standard operators **
************************/
bool operator == (Link_side_id const &id) const;
bool operator > (Link_side_id const &id) const;
}
__attribute__((__packed__));
class Net::Link_side : public Genode::Avl_node<Link_side>
{
friend class Link;
private:
Interface &_interface;
Link_side_id const _id;
Link &_link;
public:
Link_side(Interface &interface,
Link_side_id const &id,
Link &link);
Link_side const &find_by_id(Link_side_id const &id) const;
bool is_client() const;
/**************
** Avl_node **
**************/
bool higher(Link_side *side) { return side->_id > _id; }
/*********
** Log **
*********/
void print(Genode::Output &output) const;
/***************
** Accessors **
***************/
Interface &interface() const { return _interface; }
Link &link() const { return _link; }
Ipv4_address const &src_ip() const { return _id.src_ip; }
Ipv4_address const &dst_ip() const { return _id.dst_ip; }
Genode::uint16_t src_port() const { return _id.src_port; }
Genode::uint16_t dst_port() const { return _id.dst_port; }
};
struct Net::Link_side_tree : Genode::Avl_tree<Link_side>
{
struct No_match : Genode::Exception { };
Link_side const &find_by_id(Link_side_id const &id) const;
};
class Net::Link : public Link_list::Element
{
protected:
using Signal_handler = Genode::Signal_handler<Link>;
Configuration &_config;
Link_side _client;
Pointer<Port_allocator_guard> const _server_port_alloc;
Link_side _server;
Genode::One_shot_timeout<Link> _close_timeout;
Genode::Timer::Microseconds const _close_timeout_us;
Genode::uint8_t const _protocol;
void _handle_close_timeout(Genode::Timer::Microseconds);
void _packet() { _close_timeout.start(_close_timeout_us); }
public:
struct No_port_allocator : Genode::Exception { };
Link(Interface &cln_interface,
Link_side_id const &cln_id,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Configuration &config,
Genode::uint8_t const protocol);
void dissolve();
/*********
** Log **
*********/
void print(Genode::Output &output) const;
/***************
** Accessors **
***************/
Link_side &client() { return _client; }
Link_side &server() { return _server; }
};
class Net::Tcp_link : public Link
{
private:
bool _client_fin = false;
bool _server_fin = false;
bool _client_fin_acked = false;
bool _server_fin_acked = false;
bool _closed = false;
void _fin_acked();
public:
Tcp_link(Interface &cln_interface,
Link_side_id const &cln_id,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Configuration &config,
Genode::uint8_t const protocol);
void client_packet(Tcp_packet &tcp);
void server_packet(Tcp_packet &tcp);
};
struct Net::Udp_link : Link
{
Udp_link(Interface &cln_interface,
Link_side_id const &cln_id,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Configuration &config,
Genode::uint8_t const protocol);
void packet() { _packet(); }
};
#endif /* _LINK_H_ */

View File

@ -1,21 +1,17 @@
/*
* \brief MAC-address allocator
* \author Stefan Kalkowski
* \date 2010-08-25
* \author Martin Stein
* \date 2016-10-24
*/
/*
* Copyright (C) 2010-2013 Genode Labs GmbH
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <mac_allocator.h>
/* Genode includes */
#include <nic_bridge/mac_allocator.h>
/**
* We take the range 02:02:02:02:02:XX for our MAC address allocator,
* it's likely, that we will have no clashes here.
* (e.g. Linux uses 02:00... for its tap-devices.)
*/
Net::Mac_address Net::Mac_allocator::mac_addr_base(0x03);

View File

@ -12,118 +12,53 @@
*/
/* Genode */
#include <base/component.h>
#include <base/heap.h>
#include <os/config.h>
#include <os/timer.h>
#include <nic/xml_node.h>
#include <os/server.h>
#include <timer_session/connection.h>
/* local includes */
#include <component.h>
#include <arp_cache.h>
#include <uplink.h>
#include <port_allocator.h>
#include <configuration.h>
using namespace Net;
using namespace Genode;
unsigned read_rtt_sec()
{
unsigned rtt_sec = config()->xml_node().attribute_value("rtt_sec", 0UL);
if (!rtt_sec) {
rtt_sec = 3;
warning("missing 'rtt_sec' attribute in config tag,",
"using default value \"3\"");
}
return rtt_sec;
}
class Main
{
private:
bool const _verbose;
Port_allocator _tcp_port_alloc;
Port_allocator _udp_port_alloc;
Server::Entrypoint &_ep;
Interface_tree _interface_tree;
Arp_cache _arp_cache;
Arp_waiter_list _arp_waiters;
Tcp_proxy_list _tcp_proxys;
Udp_proxy_list _udp_proxys;
unsigned _rtt_sec;
Uplink _uplink;
Net::Root _root;
void _read_ports(Genode::Xml_node &route, char const *name,
Port_allocator &_port_alloc);
Timer::Connection _timer_connection;
Genode::Timer _timer;
Genode::Heap _heap;
Configuration _config;
Uplink _uplink;
Net::Root _root;
public:
Main(Server::Entrypoint &ep);
Main(Env &env);
};
void Main::_read_ports(Xml_node &route, char const *name,
Port_allocator &port_alloc)
{
try {
for (Xml_node port = route.sub_node(name); ; port = port.next(name)) {
uint32_t const dst = port.attribute_value("dst", 0UL);
if (!dst) {
warning("missing 'dst' attribute in port route");
continue;
}
if (dst >= Port_allocator::FIRST &&
dst < Port_allocator::FIRST + Port_allocator::COUNT)
{
error("port forwarding clashes with dynamic port range"); }
}
} catch (Xml_node::Nonexistent_sub_node) { }
}
Main::Main(Server::Entrypoint &ep)
Main::Main(Env &env)
:
_verbose(config()->xml_node().attribute_value("verbose", false)),
_ep(ep), _rtt_sec(read_rtt_sec()),
_uplink(_ep, _tcp_port_alloc, _udp_port_alloc, _tcp_proxys,
_udp_proxys, _rtt_sec, _interface_tree, _arp_cache,
_arp_waiters, _verbose),
_root(_ep, *env()->heap(), _uplink.router_mac(), _tcp_port_alloc,
_udp_port_alloc, _tcp_proxys, _udp_proxys,
_rtt_sec, _interface_tree, _arp_cache, _arp_waiters, _verbose)
_timer_connection(env), _timer(_timer_connection, env.ep()),
_heap(&env.ram(), &env.rm()), _config(config()->xml_node(), _heap),
_uplink(env.ep(), _timer, _heap, _config),
_root(env.ep(), _timer, _heap, _uplink.router_mac(), _config, env.ram())
{
/* reserve all ports that are used in port routes */
try {
Xml_node policy = config()->xml_node().sub_node("policy");
for (; ; policy = policy.next("policy")) {
try {
Xml_node route = policy.sub_node("ip");
for (; ; route = route.next("ip")) {
_read_ports(route, "tcp", _tcp_port_alloc);
_read_ports(route, "udp", _udp_port_alloc);
}
} catch (Xml_node::Nonexistent_sub_node) { }
}
} catch (Xml_node::Nonexistent_sub_node) { }
/* announce service */
env()->parent()->announce(ep.manage(_root));
env.parent().announce(env.ep().manage(_root));
}
/************
** Server **
************/
/***************
** Component **
***************/
namespace Server {
char const *name() { return "nic_router_ep"; }
size_t stack_size() { return 4096 *sizeof(addr_t); }
void construct(Entrypoint &ep) { static Main router(ep); }
}
size_t Component::stack_size() { return 4 * 1024 * sizeof(addr_t); }
void Component::construct(Env &env) { static Main main(env); }

View File

@ -0,0 +1,84 @@
/*
* \brief Rule for doing NAT from one given interface to another
* \author Martin Stein
* \date 2016-09-13
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <util/xml_node.h>
#include <base/log.h>
#include <net/tcp.h>
#include <net/udp.h>
/* local includes */
#include <nat_rule.h>
#include <domain.h>
#include <interface.h>
using namespace Net;
using namespace Genode;
bool Nat_rule::higher(Nat_rule *rule)
{
return (addr_t)&rule->domain() > (addr_t)&_domain;
}
Nat_rule::Nat_rule(Domain_tree &domains,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Xml_node const &node)
:
Leaf_rule(domains, node),
_tcp_port_alloc(tcp_port_alloc, node.attribute_value("tcp-ports", 0UL)),
_udp_port_alloc(udp_port_alloc, node.attribute_value("udp-ports", 0UL))
{ }
Nat_rule &Nat_rule::find_by_domain(Domain &domain)
{
if (&domain == &_domain) {
return *this; }
bool const side = (addr_t)&domain > (addr_t)&_domain;
Nat_rule *const rule = Avl_node<Nat_rule>::child(side);
if (!rule) {
throw Nat_rule_tree::No_match(); }
return rule->find_by_domain(domain);
}
Nat_rule &Nat_rule_tree::find_by_domain(Domain &domain)
{
Nat_rule *const rule = first();
if (!rule) {
throw No_match(); }
return rule->find_by_domain(domain);
}
void Nat_rule::print(Output &output) const
{
Genode::print(output, "domain ", _domain,
" tcp-ports ", _tcp_port_alloc.max(),
" udp-ports ", _udp_port_alloc.max());
}
Port_allocator_guard &Nat_rule::port_alloc(uint8_t const prot)
{
switch (prot) {
case Tcp_packet::IP_ID: return _tcp_port_alloc;
case Udp_packet::IP_ID: return _udp_port_alloc;
default: throw Interface::Bad_transport_protocol(); }
}

View File

@ -0,0 +1,83 @@
/*
* \brief Rule for doing NAT from one given interface to another
* \author Martin Stein
* \date 2016-09-13
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _NAT_RULE_H_
#define _NAT_RULE_H_
/* local includes */
#include <port_allocator.h>
#include <leaf_rule.h>
/* Genode includes */
#include <util/avl_string.h>
namespace Net {
class Port_allocator;
class Nat_rule_base;
class Nat_rule;
class Nat_rule_tree;
}
class Net::Nat_rule : public Leaf_rule,
public Genode::Avl_node<Nat_rule>
{
private:
Port_allocator_guard _tcp_port_alloc;
Port_allocator_guard _udp_port_alloc;
public:
Nat_rule(Domain_tree &domains,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Genode::Xml_node const &node);
Nat_rule &find_by_domain(Domain &domain);
Port_allocator_guard &port_alloc(Genode::uint8_t const prot);
/*********
** log **
*********/
void print(Genode::Output &output) const;
/**************
** Avl_node **
**************/
bool higher(Nat_rule *rule);
/***************
** Accessors **
***************/
Port_allocator_guard &tcp_port_alloc() { return _tcp_port_alloc; }
Port_allocator_guard &udp_port_alloc() { return _udp_port_alloc; }
};
struct Net::Nat_rule_tree : Genode::Avl_tree<Nat_rule>
{
struct No_match : Genode::Exception { };
Nat_rule &find_by_domain(Domain &domain);
};
#endif /* _NAT_RULE_H_ */

View File

@ -0,0 +1,106 @@
/*
* \brief Rule for permitting ports in the context of a transport rule
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/log.h>
/* local includes */
#include <permit_rule.h>
#include <domain.h>
using namespace Net;
using namespace Genode;
/*****************
** Permit_rule **
*****************/
Permit_rule::Permit_rule(Domain_tree &domains, Xml_node const &node)
:
Leaf_rule(domains, node)
{ }
/*********************
** Permit_any_rule **
*********************/
void Permit_any_rule::print(Output &output) const
{
Genode::print(output, "requests to ", _domain);
}
Permit_any_rule::Permit_any_rule(Domain_tree &domains, Xml_node const &node)
:
Permit_rule(domains, node)
{ }
/************************
** Permit_single_rule **
************************/
bool Permit_single_rule::higher(Permit_single_rule *rule)
{
return rule->_port > _port;
}
void Permit_single_rule::print(Output &output) const
{
Genode::print(output, "port ", _port, " requests to ", _domain);
}
Permit_single_rule::Permit_single_rule(Domain_tree &domains,
Xml_node const &node)
:
Permit_rule(domains, node),
_port(node.attribute_value("port", 0UL))
{
if (!_port || dynamic_port(_port)) {
throw Invalid(); }
}
Permit_single_rule const &
Permit_single_rule::find_by_port(uint16_t const port) const
{
if (port == _port) {
return *this; }
bool const side = port > _port;
Permit_single_rule *const rule = Avl_node<Permit_single_rule>::child(side);
if (!rule) {
throw Permit_single_rule_tree::No_match(); }
return rule->find_by_port(port);
}
/*****************************
** Permit_single_rule_tree **
*****************************/
Permit_single_rule const &
Permit_single_rule_tree::find_by_port(uint16_t const port) const
{
Permit_single_rule *const rule = first();
if (!rule) {
throw No_match(); }
return rule->find_by_port(port);
}

View File

@ -0,0 +1,105 @@
/*
* \brief Rules for permitting ports in the context of a transport rule
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PERMIT_RULE_H_
#define _PERMIT_RULE_H_
/* local includes */
#include <leaf_rule.h>
/* Genode includes */
#include <util/avl_tree.h>
namespace Genode { class Output; }
namespace Net {
class Permit_rule;
class Permit_any_rule;
class Permit_single_rule;
class Permit_single_rule_tree;
}
struct Net::Permit_rule : Leaf_rule
{
Permit_rule(Domain_tree &domains, Genode::Xml_node const &node);
/*********
** log **
*********/
virtual void print(Genode::Output &output) const = 0;
};
struct Net::Permit_any_rule : Permit_rule
{
Permit_any_rule(Domain_tree &domains, Genode::Xml_node const &node);
/*********
** log **
*********/
void print(Genode::Output &output) const;
};
class Net::Permit_single_rule : public Permit_rule,
public Genode::Avl_node<Permit_single_rule>
{
private:
Genode::uint16_t const _port;
public:
Permit_single_rule(Domain_tree &domains,
Genode::Xml_node const &node);
Permit_single_rule const &
find_by_port(Genode::uint16_t const port) const;
/*********
** log **
*********/
void print(Genode::Output &output) const;
/**************
** Avl_node **
**************/
bool higher(Permit_single_rule *rule);
/***************
** Accessors **
***************/
Genode::uint16_t port() const { return _port; }
};
struct Net::Permit_single_rule_tree : Genode::Avl_tree<Permit_single_rule>
{
struct No_match : Genode::Exception { };
Permit_single_rule const &find_by_port(Genode::uint16_t const port) const;
};
#endif /* _PERMIT_RULE_H_ */

View File

@ -0,0 +1,55 @@
/*
* \brief Pointer that can be dereferenced only when valid
* \author Martin Stein
* \date 2016-08-24
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _POINTER_H_
#define _POINTER_H_
/* Genode includes */
#include <base/exception.h>
namespace Net { template <typename> class Pointer; }
template <typename T>
class Net::Pointer
{
private:
T *_ptr;
public:
struct Valid : Genode::Exception { };
struct Invalid : Genode::Exception { };
Pointer() : _ptr(nullptr) { }
T &deref() const
{
if (_ptr == nullptr) {
throw Invalid(); }
return *_ptr;
}
void set(T &ptr)
{
if (_ptr != nullptr) {
throw Valid(); }
_ptr = &ptr;
}
void unset() { _ptr = nullptr; }
};
#endif /* _POINTER_H_ */

View File

@ -18,8 +18,33 @@ using namespace Net;
using namespace Genode;
Port_allocator::Port_allocator()
Genode::uint16_t Port_allocator_guard::alloc()
{
try { alloc_index(0); }
catch (Already_allocated) { throw Failed_to_reserve_port_0(); }
if (_used == _max) {
throw Out_of_indices(); }
uint16_t const port = _port_alloc.alloc();
_used++;
return port;
}
void Port_allocator_guard::free(Genode::uint16_t port)
{
_port_alloc.free(port);
_used = _used ? _used - 1 : 0;
}
Port_allocator_guard::Port_allocator_guard(Port_allocator & port_alloc,
unsigned const max)
:
_port_alloc(port_alloc), _max(max)
{ }
bool Net::dynamic_port(uint16_t const port)
{
return port >= Port_allocator::FIRST &&
port < (uint32_t)Port_allocator::FIRST + Port_allocator::COUNT;
}

View File

@ -18,7 +18,14 @@
/* Genode includes */
#include <util/bit_allocator.h>
namespace Net { class Port_allocator; }
namespace Net {
class Port_allocator;
class Port_allocator_guard;
bool dynamic_port(Genode::uint16_t const port);
}
class Net::Port_allocator
{
@ -37,4 +44,26 @@ class Net::Port_allocator
void free(Genode::uint16_t port) { _alloc.free(port - FIRST); }
};
class Net::Port_allocator_guard
{
private:
Port_allocator &_port_alloc;
unsigned const _max;
unsigned _used = 0;
public:
class Out_of_indices : Genode::Exception {};
Genode::uint16_t alloc();
void free(Genode::uint16_t port);
Port_allocator_guard(Port_allocator & port_alloc, unsigned const max);
unsigned max() const { return _max; }
};
#endif /* _PORT_ALLOCATOR_H_ */

View File

@ -1,54 +0,0 @@
/*
* \brief Port routing entry
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <port_route.h>
using namespace Net;
using namespace Genode;
Port_route::Port_route(uint16_t dst, char const *label, size_t label_size,
Ipv4_address via, Ipv4_address to)
:
_dst(dst), _label(Genode::Cstring(label, label_size)), _via(via), _to(to)
{ }
void Port_route::print(Output &output) const
{
Genode::print(output, "", _dst, " -> \"", _label, "\" to ", _to,
" via ", _via);
}
Port_route *Port_route::find_by_dst(uint16_t dst)
{
if (dst == _dst) {
return this; }
bool const side = dst > _dst;
Port_route *c = Avl_node<Port_route>::child(side);
return c ? c->find_by_dst(dst) : 0;
}
Port_route *Port_route_tree::find_by_dst(uint16_t dst)
{
Port_route *port = first();
if (!port) {
return port; }
port = port->find_by_dst(dst);
return port;
}

View File

@ -1,75 +0,0 @@
/*
* \brief Port routing entry
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/session_label.h>
#include <util/avl_tree.h>
#include <util/list.h>
#include <net/ipv4.h>
#ifndef _PORT_ROUTE_H_
#define _PORT_ROUTE_H_
namespace Net {
class Port_route;
class Port_route_tree;
using Port_route_list = Genode::List<Port_route>;
}
class Net::Port_route : public Genode::Avl_node<Port_route>,
public Port_route_list::Element
{
private:
Genode::uint16_t _dst;
Genode::Session_label _label;
Ipv4_address _via;
Ipv4_address _to;
public:
Port_route(Genode::uint16_t dst, char const *label,
Genode::size_t label_size, Ipv4_address via,
Ipv4_address to);
void print(Genode::Output &output) const;
Port_route *find_by_dst(Genode::uint16_t dst);
/**************
** Avl_node **
**************/
bool higher(Port_route *route) { return route->_dst > _dst; }
/***************
** Accessors **
***************/
Genode::Session_label &label() { return _label; }
Ipv4_address via() { return _via; }
Ipv4_address to() { return _to; }
Genode::uint16_t dst() { return _dst; }
};
struct Net::Port_route_tree : Genode::Avl_tree<Port_route>
{
Port_route *find_by_dst(Genode::uint16_t dst);
};
#endif /* _PORT_ROUTE_H_ */

View File

@ -0,0 +1,39 @@
/*
* \brief Provide protocol names as Genode Cstring objects
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <protocol_name.h>
#include <interface.h>
/* Genode includes */
#include <util/string.h>
#include <net/tcp.h>
#include <net/udp.h>
using namespace Net;
using namespace Genode;
static Cstring const _udp_name("UDP");
static Cstring const _tcp_name("TCP");
Cstring const &Net::udp_name() { return _udp_name; }
Cstring const &Net::tcp_name() { return _tcp_name; }
Cstring const &Net::protocol_name(uint8_t protocol)
{
switch (protocol) {
case Tcp_packet::IP_ID: return tcp_name();
case Udp_packet::IP_ID: return udp_name();
default: throw Interface::Bad_transport_protocol(); }
}

View File

@ -0,0 +1,29 @@
/*
* \brief Provide protocol names as Genode Cstring objects
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PROTOCOL_NAME_H_
#define _PROTOCOL_NAME_H_
/* Genode includes */
#include <base/stdint.h>
namespace Genode { class Cstring; }
namespace Net {
Genode::Cstring const &tcp_name();
Genode::Cstring const &udp_name();
Genode::Cstring const &protocol_name(Genode::uint8_t protocol);
}
#endif /* _PROTOCOL_NAME_H_ */

View File

@ -1,120 +0,0 @@
/*
* \brief UDP/TCP proxy session
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <net/tcp.h>
#include <net/udp.h>
#include <base/log.h>
/* local includes */
#include <proxy.h>
using namespace Net;
using namespace Genode;
Tcp_proxy::Tcp_proxy(uint16_t client_port, uint16_t proxy_port,
Ipv4_address client_ip, Ipv4_address proxy_ip,
Interface &client, Genode::Entrypoint &ep,
unsigned const rtt_sec)
:
_client_port(client_port), _proxy_port(proxy_port), _client_ip(client_ip),
_proxy_ip(proxy_ip), _client(client),
_del_timeout(ep, *this, &Tcp_proxy::_del_timeout_handle),
_del_timeout_us(rtt_sec * 2 * 1000 * 1000)
{ }
bool Tcp_proxy::matches_client(Ipv4_address client_ip, uint16_t client_port)
{
return client_ip == _client_ip && client_port == _client_port;
}
bool Tcp_proxy::matches_proxy(Ipv4_address proxy_ip, uint16_t proxy_port)
{
return proxy_ip == _proxy_ip && proxy_port == _proxy_port;
}
void Tcp_proxy::tcp_packet(Ipv4_packet * const ip, Tcp_packet * const tcp)
{
/* find out which side sent the packet */
bool from_client;
if (tcp->src_port() == _client_port) { from_client = true; }
else { from_client = false; }
/* Remember FIN packets and which side sent them */
if (tcp->fin()) {
if (from_client) { _client_fin = true; }
else { _other_fin = true; }
}
/* look for packets that ACK a previous FIN and remember those ACKs */
if (tcp->ack()) {
if (from_client && _other_fin) { _other_fin_acked = true; }
if (!from_client && _client_fin) { _client_fin_acked = true; }
/* if both sides sent a FIN and got ACKed, init delayed destruction */
if (_other_fin_acked && _client_fin_acked) {
_timer.sigh(_del_timeout);
_timer.trigger_once(_del_timeout_us);
}
}
}
void Udp_proxy::print(Output &out) const
{
Genode::print(out, _client_ip, ":", _client_port, " -> ",
_proxy_ip, ":", _proxy_port);
}
void Tcp_proxy::print(Output &out) const
{
Genode::print(out, _client_ip, ":", _client_port, " -> ",
_proxy_ip, ":", _proxy_port);
}
Udp_proxy::Udp_proxy(uint16_t client_port, uint16_t proxy_port,
Ipv4_address client_ip, Ipv4_address proxy_ip,
Interface &client, Genode::Entrypoint &ep,
unsigned const rtt_sec)
:
_client_port(client_port), _proxy_port(proxy_port), _client_ip(client_ip),
_proxy_ip(proxy_ip), _client(client),
_del_timeout(ep, *this, &Udp_proxy::_del_timeout_handle),
_del_timeout_us(rtt_sec * 2 * 1000 * 1000)
{
_timer.sigh(_del_timeout);
_timer.trigger_once(_del_timeout_us);
}
bool Udp_proxy::matches_client(Ipv4_address client_ip, uint16_t client_port)
{
return client_ip == _client_ip && client_port == _client_port;
}
bool Udp_proxy::matches_proxy(Ipv4_address proxy_ip, uint16_t proxy_port)
{
return proxy_ip == _proxy_ip && proxy_port == _proxy_port;
}
void Udp_proxy::udp_packet(Ipv4_packet * const ip, Udp_packet * const udp)
{
_timer.trigger_once(_del_timeout_us);
}

View File

@ -1,134 +0,0 @@
/*
* \brief UDP/TCP proxy session
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PROXY_H_
#define _PROXY_H_
/* Genode includes */
#include <timer_session/connection.h>
#include <net/ipv4.h>
#include <util/list.h>
namespace Net {
class Tcp_packet;
class Udp_packet;
class Interface;
class Tcp_proxy;
class Udp_proxy;
using Tcp_proxy_list = Genode::List<Tcp_proxy>;
using Udp_proxy_list = Genode::List<Udp_proxy>;
}
class Net::Tcp_proxy : public Genode::List<Tcp_proxy>::Element
{
private:
using Signal_handler = Genode::Signal_handler<Tcp_proxy>;
Genode::uint16_t const _client_port;
Genode::uint16_t const _proxy_port;
Ipv4_address const _client_ip;
Ipv4_address const _proxy_ip;
Interface &_client;
Timer::Connection _timer;
bool _client_fin = false;
bool _other_fin = false;
bool _client_fin_acked = false;
bool _other_fin_acked = false;
bool _del = false;
Signal_handler _del_timeout;
unsigned const _del_timeout_us;
void _del_timeout_handle() { _del = true; }
public:
Tcp_proxy(Genode::uint16_t client_port, Genode::uint16_t proxy_port,
Ipv4_address client_ip, Ipv4_address proxy_ip,
Interface &client, Genode::Entrypoint &ep,
unsigned const rtt_sec);
void print(Genode::Output &out) const;
bool matches_client(Ipv4_address client_ip,
Genode::uint16_t client_port);
bool matches_proxy(Ipv4_address proxy_ip,
Genode::uint16_t proxy_port);
void tcp_packet(Ipv4_packet *const ip, Tcp_packet *const tcp);
/***************
** Accessors **
***************/
Genode::uint16_t client_port() const { return _client_port; }
Genode::uint16_t proxy_port() const { return _proxy_port; }
Ipv4_address client_ip() const { return _client_ip; }
Ipv4_address proxy_ip() const { return _proxy_ip; }
Interface &client() const { return _client; }
bool del() const { return _del; }
};
class Net::Udp_proxy : public Genode::List<Udp_proxy>::Element
{
private:
using Signal_handler = Genode::Signal_handler<Udp_proxy>;
Genode::uint16_t const _client_port;
Genode::uint16_t const _proxy_port;
Ipv4_address const _client_ip;
Ipv4_address const _proxy_ip;
Interface &_client;
Timer::Connection _timer;
bool _del = false;
Signal_handler _del_timeout;
unsigned const _del_timeout_us;
void _del_timeout_handle() { _del = true; }
public:
Udp_proxy(Genode::uint16_t client_port, Genode::uint16_t proxy_port,
Ipv4_address client_ip, Ipv4_address proxy_ip,
Interface &client, Genode::Entrypoint &ep,
unsigned const rtt_sec);
void print(Genode::Output &out) const;
bool matches_client(Ipv4_address client_ip,
Genode::uint16_t client_port);
bool matches_proxy(Ipv4_address proxy_ip,
Genode::uint16_t proxy_port);
void udp_packet(Ipv4_packet *const ip, Udp_packet *const udp);
/***************
** Accessors **
***************/
Genode::uint16_t client_port() const { return _client_port; }
Genode::uint16_t proxy_port() const { return _proxy_port; }
Ipv4_address client_ip() const { return _client_ip; }
Ipv4_address proxy_ip() const { return _proxy_ip; }
Interface &client() const { return _client; }
bool del() const { return _del; }
};
#endif /* _PROXY_H_ */

View File

@ -0,0 +1,22 @@
/*
* \brief Base of each routing rule
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _RULE_H_
#define _RULE_H_
/* Genode includes */
#include <base/exception.h>
namespace Net { struct Rule { struct Invalid : Genode::Exception { }; }; }
#endif /* _RULE_H_ */

View File

@ -1,11 +1,12 @@
TARGET = nic_router
LIBS += base net config server
LIBS += base net config timeout
SRC_CC += arp_waiter.cc ip_route.cc proxy.cc
SRC_CC += port_route.cc component.cc
SRC_CC += mac_allocator.cc main.cc uplink.cc interface.cc arp_cache.cc
SRC_CC += arp_waiter.cc ip_rule.cc
SRC_CC += component.cc port_allocator.cc forward_rule.cc
SRC_CC += nat_rule.cc mac_allocator.cc main.cc
SRC_CC += uplink.cc interface.cc arp_cache.cc configuration.cc
SRC_CC += domain.cc protocol_name.cc direct_rule.cc link.cc
SRC_CC += transport_rule.cc leaf_rule.cc permit_rule.cc
INC_DIR += $(PRG_DIR)
vpath *.cc $(REP_DIR)/src/server/proxy_arp

View File

@ -0,0 +1,79 @@
/*
* \brief Rule for allowing direct TCP/UDP traffic between two interfaces
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <util/xml_node.h>
#include <base/allocator.h>
#include <base/log.h>
/* local includes */
#include <transport_rule.h>
#include <configuration.h>
using namespace Net;
using namespace Genode;
Permit_any_rule *Transport_rule::_read_permit_any(Domain_tree &domains,
Xml_node const &node,
Allocator &alloc)
{
try {
Xml_node sub_node = node.sub_node("permit-any");
return new (alloc) Permit_any_rule(domains, sub_node);
}
catch (Xml_node::Nonexistent_sub_node) { }
catch (Rule::Invalid) { warning("invalid permit-any rule"); }
return nullptr;
}
Transport_rule::Transport_rule(Domain_tree &domains,
Xml_node const &node,
Allocator &alloc,
Cstring const &protocol,
Configuration &config)
:
Direct_rule(node), _permit_any(_read_permit_any(domains, node, alloc))
{
/* skip specific permit rules if all ports are permitted anyway */
if (_permit_any) {
if (config.verbose()) {
log(" ", protocol, " rule: ", _dst, " ", *_permit_any); }
return;
}
/* read specific permit rules */
node.for_each_sub_node("permit", [&] (Xml_node const &node) {
try {
Permit_single_rule &rule = *new (alloc)
Permit_single_rule(domains, node);
_permit_single_rules.insert(&rule);
if (config.verbose()) {
log(protocol, " rule: ", _dst, " ", rule); }
}
catch (Rule::Invalid) { warning("invalid permit rule"); }
});
/* drop the transport rule if it has no permitted ports */
if (!_permit_single_rules.first()) {
throw Invalid(); }
}
Permit_rule const &Transport_rule::permit_rule(uint16_t const port) const
{
if (_permit_any) { return *_permit_any; }
return _permit_single_rules.find_by_port(port);
}

View File

@ -0,0 +1,54 @@
/*
* \brief Rule for allowing direct TCP/UDP traffic between two interfaces
* \author Martin Stein
* \date 2016-08-19
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _TRANSPORT_RULE_H_
#define _TRANSPORT_RULE_H_
/* local includes */
#include <direct_rule.h>
#include <permit_rule.h>
namespace Genode { class Allocator; }
namespace Net {
class Configuration;
class Transport_rule;
struct Transport_rule_list : Direct_rule_list<Transport_rule> { };
}
class Net::Transport_rule : public Direct_rule<Transport_rule>
{
private:
Permit_any_rule *const _permit_any;
Permit_single_rule_tree _permit_single_rules;
static Permit_any_rule *
_read_permit_any(Domain_tree &domains,
Genode::Xml_node const &node,
Genode::Allocator &alloc);
public:
Transport_rule(Domain_tree &domains,
Genode::Xml_node const &node,
Genode::Allocator &alloc,
Genode::Cstring const &protocol,
Configuration &config);
Permit_rule const &permit_rule(Genode::uint16_t const port) const;
};
#endif /* _TRANSPORT_RULE_H_ */

View File

@ -1,11 +1,11 @@
/*
* \brief NIC handler
* \author Stefan Kalkowski
* \date 2013-05-24
* \brief Uplink interface in form of a NIC session component
* \author Martin Stein
* \date 2016-08-23
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
@ -14,48 +14,24 @@
/* Genode includes */
#include <base/env.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
#include <net/udp.h>
#include <net/tcp.h>
#include <net/dhcp.h>
#include <os/config.h>
/* local includes */
#include <component.h>
#include <uplink.h>
#include <configuration.h>
using namespace Net;
using namespace Genode;
Ipv4_address Net::Uplink::_read_src() {
Session_policy policy(label_from_args("label=\"uplink\""));
Ipv4_address const src = policy.attribute_value("src", Ipv4_address());
if (src == Ipv4_address()) {
warning("missing 'src' attribute in policy"); }
return src;
}
Net::Uplink::Uplink(Server::Entrypoint &ep,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Tcp_proxy_list &tcp_proxys,
Udp_proxy_list &udp_proxys,
unsigned rtt_sec,
Interface_tree &interface_tree,
Arp_cache &arp_cache,
Arp_waiter_list &arp_waiters,
bool verbose)
Net::Uplink::Uplink(Entrypoint &ep,
Genode::Timer &timer,
Genode::Allocator &alloc,
Configuration &config)
:
Nic::Packet_allocator(env()->heap()),
Nic::Packet_allocator(&alloc),
Nic::Connection(this, BUF_SIZE, BUF_SIZE),
Interface(ep, mac_address(), _read_src(), *env()->heap(),
"label=\"uplink\"", tcp_port_alloc, udp_port_alloc,
Mac_address(), tcp_proxys, udp_proxys,
rtt_sec, interface_tree, arp_cache, arp_waiters, verbose)
Interface(ep, timer, mac_address(), alloc, Mac_address(),
config.domains().find_by_name(Cstring("uplink")))
{
rx_channel()->sigh_ready_to_ack(_sink_ack);
rx_channel()->sigh_packet_avail(_sink_submit);

View File

@ -16,18 +16,16 @@
/* Genode includes */
#include <nic_session/connection.h>
#include <nic/packet_allocator.h>
/* local includes */
#include <interface.h>
namespace Net {
class Port_allocator;
class Uplink;
}
namespace Net { class Uplink; }
class Net::Uplink : public Nic::Packet_allocator,
public Nic::Connection, public Net::Interface
public Nic::Connection,
public Interface
{
private:
@ -36,28 +34,29 @@ class Net::Uplink : public Nic::Packet_allocator,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PKT_SIZE,
};
Ipv4_address _read_src();
public:
Uplink(Server::Entrypoint &ep,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Tcp_proxy_list &tcp_proxys,
Udp_proxy_list &udp_proxys,
unsigned rtt_sec,
Interface_tree &interface_tree,
Arp_cache &arp_cache,
Arp_waiter_list &arp_waiters,
bool verbose);
Ipv4_address_prefix _read_interface();
/********************
** Net::Interface **
********************/
Packet_stream_sink<Nic::Session::Policy> *sink() { return rx(); }
Packet_stream_source<Nic::Session::Policy> *source() { return tx(); }
Packet_stream_sink &_sink() { return *rx(); }
Packet_stream_source &_source() { return *tx(); }
public:
Uplink(Genode::Entrypoint &ep,
Genode::Timer &timer,
Genode::Allocator &alloc,
Configuration &config);
/***************
** Accessors **
***************/
Mac_address const &router_mac() const { return _router_mac; }
};
#endif /* _UPLINK_H_ */

View File

@ -76,3 +76,4 @@ usb_hid
smartcard
new_delete
timeout
nic_router