os: NIC router

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.

Fixes #114
This commit is contained in:
Martin Stein 2016-08-25 17:48:53 +02:00 committed by Christian Helmuth
parent ada334705c
commit 3c25d989f3
24 changed files with 3228 additions and 0 deletions

View File

@ -0,0 +1,394 @@
#
# Build
#
set tcp_up_to_down_1 1
set tcp_up_to_down_2 1
set tcp_down_to_up_1 1
set udp_down_to_down_1 1
set udp_up_to_down_1 1
set udp_down_to_up_1 1
set build_components {
core init drivers/timer drivers/nic server/nic_router server/nic_bridge
test/lwip/http_srv_static test/lwip/http_clnt test/lxip/udp_echo
test/lxip/udp_client
}
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
build $build_components
create_boot_directory
#
# Generate config
#
append config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>}
append_platform_drv_config
append config {
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="nic_drv">
<resource name="RAM" quantum="2M"/>
<provides><service name="Nic"/></provides>
</start>
<start name="nic_router">
<resource name="RAM" quantum="10M"/>
<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" />
<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" />
<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>
<policy label="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>
<policy label="http_srv_2" src="192.168.1.1" nat="yes" nat-tcp-ports="2">
<ip dst="10.0.2.0/24" label="uplink" />
</policy>
<policy label="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="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="udp_srv_2" src="18.17.15.14">
<ip dst="10.0.2.0/24" label="uplink" />
</policy>
<policy label="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="udp_clnt_3" src="217.13.192.1" nat="yes" nat-udp-ports="2">
<ip dst="10.0.0.0/18" label="uplink" />
</policy>
</config>
<route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="nic_bridge">
<resource name="RAM" quantum="10M"/>
<provides><service name="Nic"/></provides>
<config>
<policy label="nic_router" ip_addr="10.0.2.55"/>
<policy label="http_srv_3" ip_addr="10.0.2.11"/>
<policy label="udp_clnt_2" ip_addr="10.0.2.123"/>
<policy label="udp_srv_3" ip_addr="10.0.2.70"/>
</config>
<route>
<service name="Nic"> <child name="nic_drv"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>}
append_if $udp_down_to_down_1 config {
<start name="udp_clnt_1">
<binary name="test-lxip_udp_client" />
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config ld_verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="10.0.98.55"
gateway="10.0.98.33" netmask="255.255.255.0"
server_ip="10.0.98.33" server_port="1337">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
<start name="udp_srv_1">
<binary name="test-lxip_udp_echo" />
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config ld_verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="10.0.99.55"
gateway="10.0.99.33" netmask="255.255.255.0" port="1337">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>}
append_if $udp_up_to_down_1 config {
<start name="udp_clnt_2">
<binary name="test-lxip_udp_client" />
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config ld_verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="10.0.2.123"
gateway="10.0.2.55" netmask="255.255.255.0"
server_ip="10.0.2.55" server_port="1">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
<start name="udp_srv_2">
<binary name="test-lxip_udp_echo" />
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</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">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>}
append_if $udp_down_to_up_1 config {
<start name="udp_clnt_3">
<binary name="test-lxip_udp_client" />
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config ld_verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="217.13.192.2"
gateway="217.13.192.1" netmask="255.255.192.0"
server_ip="10.0.2.70" server_port="65535">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
<start name="udp_srv_3">
<binary name="test-lxip_udp_echo" />
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</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">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>}
append_if $tcp_up_to_down_1 config {
<start name="http_srv_1">
<binary name="test-lwip_httpsrv_static"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config ld_verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="192.168.1.18"
netmask="255.255.255.0" gateway="192.168.1.1" http_port="80">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
<start name="http_clnt_1">
<binary name="test-http_clnt"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<libc stdout="/dev/log" stderr="/dev/log" server_ip="10.0.2.55"
http_port="80">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>}
append_if $tcp_up_to_down_2 config {
<start name="http_srv_2">
<binary name="test-lwip_httpsrv_static"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<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"
http_port="8080">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
<start name="http_clnt_2">
<binary name="test-http_clnt"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<libc stdout="/dev/log" stderr="/dev/log" server_ip="10.0.2.55"
http_port="8080">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>}
append_if $tcp_down_to_up_1 config {
<start name="http_srv_3">
<binary name="test-lwip_httpsrv_static"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="10.0.2.11"
netmask="255.255.255.0" gateway="10.0.2.1" http_port="2345">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
<start name="http_clnt_3">
<binary name="test-http_clnt"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<libc stdout="/dev/log" stderr="/dev/log" ip_addr="100.200.0.128"
netmask="255.255.0.0" gateway="100.200.0.1"
server_ip="10.0.2.11" http_port="2345">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>}
append config { </config> }
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core init timer nic_drv nic_router nic_bridge ld.lib.so libc.lib.so
libc_resolv.lib.so lwip.lib.so lxip.lib.so test-http_clnt
test-lwip_httpsrv_static test-lxip_udp_echo test-lxip_udp_client
}
# platform-specific modules
lappend_if [have_spec linux] boot_modules fb_sdl
append_platform_drv_boot_modules
build_boot_image $boot_modules
# qemu config
append qemu_args " -m 128 -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

View File

@ -0,0 +1,208 @@
=================================
Component for routing NIC packets
=================================
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.
Basic routing
#############
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:
! <policy label="uplink" src="192.168.1.3" />
! <policy label="http_servers" src="10.0.2.1" />
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:
! <ip dst="192.168.1.0/24" label="uplink" />
! <ip dst="10.0.0.0/16" label="http_servers" />
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:
! <udp dst="443" label="udp_servers" />
! <tcp dst="80" label="http_servers" />
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.
Modify destination and gateway
##############################
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.
! <udp ... via="192.168.2.1" />
! <ip ... to="10.0.2.234" via="192.168.2.1" />
! <tcp ... to="10.0.2.234" />
Network address translation
###########################
The nic_router component can translate between different IPv4 subnets of clients,
and "uplink". When enabled within the policy of a client:
! <policy ... nat="yes" />
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.
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:
! <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" />
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.
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:
! <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.
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' .
Accessing a private server network
==================================
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:
! <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="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" />
!
! <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>
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.
Limitations
###########
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.

View File

@ -0,0 +1,57 @@
/*
* \brief Cache for received ARP information
* \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 <arp_cache.h>
using namespace Net;
using namespace Genode;
Arp_cache_entry::Arp_cache_entry(Ipv4_address ip_addr, Mac_address mac_addr)
:
_ip_addr(ip_addr), _mac_addr(mac_addr)
{ }
bool Arp_cache_entry::higher(Arp_cache_entry *entry)
{
return (memcmp(&entry->_ip_addr.addr, &_ip_addr.addr,
sizeof(_ip_addr.addr)) > 0);
}
Arp_cache_entry &Arp_cache_entry::find_by_ip_addr(Ipv4_address ip_addr)
{
if (ip_addr == _ip_addr) {
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);
if (!entry) {
throw Arp_cache::No_matching_entry(); }
return entry->find_by_ip_addr(ip_addr);
}
Arp_cache_entry &Arp_cache::find_by_ip_addr(Ipv4_address ip_addr)
{
Arp_cache_entry * const entry = first();
if (!entry) {
throw No_matching_entry(); }
return entry->find_by_ip_addr(ip_addr);
}

View File

@ -0,0 +1,64 @@
/*
* \brief Cache for received ARP information
* \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 <net/ethernet.h>
#include <util/avl_tree.h>
#ifndef _ARP_CACHE_H_
#define _ARP_CACHE_H_
namespace Net {
class Arp_cache;
class 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;
public:
Arp_cache_entry(Ipv4_address ip_addr, Mac_address mac_addr);
Arp_cache_entry &find_by_ip_addr(Ipv4_address ip_addr);
/**************
** Avl_node **
**************/
bool higher(Arp_cache_entry *entry);
/***************
** Accessors **
***************/
Ipv4_address ip_addr() const { return _ip_addr; }
Mac_address mac_addr() const { return _mac_addr; }
};
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);
};
#endif /* _ARP_CACHE_H_ */

View File

@ -0,0 +1,37 @@
/*
* \brief Aspect of waiting for an ARP reply
* \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 <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)
:
_interface(interface), _ip_addr(ip_addr), _eth(eth), _eth_size(eth_size),
_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;
}

View File

@ -0,0 +1,60 @@
/*
* \brief Aspect of waiting for an ARP reply
* \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 <nic_session/nic_session.h>
#include <util/list.h>
#ifndef _ARP_WAITER_H_
#define _ARP_WAITER_H_
namespace Net {
using ::Nic::Packet_descriptor;
class Interface;
class Ethernet_frame;
class Arp_waiter;
class Arp_cache_entry;
using Arp_waiter_list = Genode::List<Arp_waiter>;
}
class Net::Arp_waiter : public Genode::List<Arp_waiter>::Element
{
private:
Interface &_interface;
Ipv4_address _ip_addr;
Ethernet_frame &_eth;
Genode::size_t const _eth_size;
Packet_descriptor &_packet;
public:
Arp_waiter(Interface &interface, Ipv4_address ip_addr,
Ethernet_frame &eth, Genode::size_t const eth_size,
Packet_descriptor &packet);
bool new_arp_cache_entry(Arp_cache_entry &entry);
/***************
** Accessors **
***************/
Interface &interface() const { return _interface; }
Ethernet_frame &eth() const { return _eth; }
Genode::size_t eth_size() const { return _eth_size; }
};
#endif /* _ARP_WAITER_H_ */

View File

@ -0,0 +1,177 @@
/*
* \brief Downlink interface in form of a NIC session component
* \author Martin Stein
* \date 2016-08-23
*/
/*
* 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 <component.h>
#include <arp_cache.h>
#include <port_allocator.h>
using namespace Net;
using namespace Genode;
bool Session_component::link_state()
{
warning(__func__, " not implemented");
return false;
}
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)
:
Guarded_range_allocator(allocator, amount),
Tx_rx_communication_buffers(tx_buf_size, rx_buf_size),
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)
{
_tx.sigh_ready_to_ack(_sink_ack);
_tx.sigh_packet_avail(_sink_submit);
_rx.sigh_ack_avail(_source_ack);
_rx.sigh_ready_to_submit(_source_submit);
}
Session_component::~Session_component() { }
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)
:
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)
{ }
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());
} catch (Session_policy::No_policy_defined) {
error("No matching policy");
throw Root::Unavailable();
}
if (src == Ipv4_address()) {
error("Missing 'src' attribute in 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();
}
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);
}
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

@ -0,0 +1,178 @@
/*
* \brief Downlink interface in form of a NIC session component
* \author Martin Stein
* \date 2016-08-23
*/
/*
* 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 _COMPONENT_H_
#define _COMPONENT_H_
/* Genode includes */
#include <root/component.h>
#include <nic/packet_allocator.h>
#include <nic_session/rpc_object.h>
#include <net/ipv4.h>
#include <base/allocator_guard.h>
/* local includes */
#include <mac_allocator.h>
#include <interface.h>
namespace Net {
class Port_allocator;
class Guarded_range_allocator;
class Communication_buffer;
class Tx_rx_communication_buffers;
class Session_component;
class Root;
}
class Net::Guarded_range_allocator
{
private:
Genode::Allocator_guard _guarded_alloc;
Nic::Packet_allocator _range_alloc;
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();
};
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
{
private:
Communication_buffer _tx_buf, _rx_buf;
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();
/******************
** Nic::Session **
******************/
Mac_address mac_address() { return Interface::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(); }
};
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;
/********************
** Root_component **
********************/
Session_component *_create_session(const char *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);
};
#endif /* _COMPONENT_H_ */

View File

@ -0,0 +1,802 @@
/*
* \brief A net interface in form of a signal-driven NIC-packet handler
* \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.
*/
/* Genode includes */
#include <net/arp.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
#include <net/udp.h>
#include <net/dump.h>
/* local includes */
#include <interface.h>
#include <port_allocator.h>
#include <proxy.h>
#include <arp_cache.h>
#include <arp_waiter.h>
using namespace Net;
using namespace Genode;
static void tlp_update_checksum(uint8_t tlp, void *ptr, Ipv4_address src,
Ipv4_address dst, size_t size)
{
switch (tlp) {
case Tcp_packet::IP_ID:
((Tcp_packet *)ptr)->update_checksum(src, dst, size);
return;
case Udp_packet::IP_ID:
((Udp_packet *)ptr)->update_checksum(src, dst);
return;
default: error("unknown transport protocol"); }
}
static uint16_t tlp_dst_port(uint8_t tlp, void *ptr)
{
switch (tlp) {
case Tcp_packet::IP_ID: return ((Tcp_packet *)ptr)->dst_port();
case Udp_packet::IP_ID: return ((Udp_packet *)ptr)->dst_port();
default: error("unknown transport protocol"); }
return 0;
}
static void tlp_dst_port(uint8_t tlp, void *ptr, uint16_t port)
{
switch (tlp) {
case Tcp_packet::IP_ID: ((Tcp_packet *)ptr)->dst_port(port); return;
case Udp_packet::IP_ID: ((Udp_packet *)ptr)->dst_port(port); return;
default: error("unknown transport protocol"); }
}
static uint16_t tlp_src_port(uint8_t tlp, void *ptr)
{
switch (tlp) {
case Tcp_packet::IP_ID: return ((Tcp_packet *)ptr)->src_port();
case Udp_packet::IP_ID: return ((Udp_packet *)ptr)->src_port();
default: error("unknown transport protocol"); }
return 0;
}
static void *tlp_packet(uint8_t tlp, Ipv4_packet *ip, size_t size)
{
switch (tlp) {
case Tcp_packet::IP_ID: return new (ip->data<void>()) Tcp_packet(size);
case Udp_packet::IP_ID: return new (ip->data<void>()) Udp_packet(size);
default: error("unknown transport protocol"); }
return nullptr;
}
static Port_route_tree *tlp_port_tree(uint8_t tlp, Ip_route *route)
{
switch (tlp) {
case Tcp_packet::IP_ID: return route->tcp_port_tree();
case Udp_packet::IP_ID: return route->udp_port_tree();
default: error("unknown transport protocol"); }
return nullptr;
}
static Port_route_list *tlp_port_list(uint8_t tlp, Ip_route *route)
{
switch (tlp) {
case Tcp_packet::IP_ID: return route->tcp_port_list();
case Udp_packet::IP_ID: return route->udp_port_list();
default: error("unknown transport protocol"); }
return nullptr;
}
void Interface::_tlp_apply_port_proxy(uint8_t tlp, void *ptr, Ipv4_packet *ip,
Ipv4_address client_ip,
uint16_t client_port)
{
switch (tlp) {
case Tcp_packet::IP_ID:
{
Tcp_packet *tcp = (Tcp_packet *)ptr;
Tcp_proxy *proxy =
_find_tcp_proxy_by_client(client_ip, client_port);
if (!proxy) {
proxy = _new_tcp_proxy(client_port, client_ip, ip->src()); }
proxy->tcp_packet(ip, tcp);
tcp->src_port(proxy->proxy_port());
return;
}
case Udp_packet::IP_ID:
{
Udp_packet *udp = (Udp_packet *)ptr;
Udp_proxy *proxy =
_find_udp_proxy_by_client(client_ip, client_port);
if (!proxy) {
proxy = _new_udp_proxy(client_port, client_ip, ip->src()); }
proxy->udp_packet(ip, udp);
udp->src_port(proxy->proxy_port());
return;
}
default: error("unknown transport protocol"); }
}
Interface *Interface::_tlp_proxy_route(uint8_t tlp, void *ptr,
uint16_t &dst_port, Ipv4_packet *ip,
Ipv4_address &to, Ipv4_address &via)
{
switch (tlp) {
case Tcp_packet::IP_ID:
{
Tcp_packet *tcp = (Tcp_packet *)ptr;
Tcp_proxy *proxy = _find_tcp_proxy_by_proxy(ip->dst(), dst_port);
if (!proxy) {
return nullptr; }
proxy->tcp_packet(ip, tcp);
dst_port = proxy->client_port();
to = proxy->client_ip();
via = to;
if(_verbose) {
log("Matching TCP NAT link: ", *proxy); }
return &proxy->client();
}
case Udp_packet::IP_ID:
{
Udp_packet *udp = (Udp_packet *)ptr;
Udp_proxy *proxy = _find_udp_proxy_by_proxy(ip->dst(), dst_port);
if (!proxy) {
return nullptr; }
proxy->udp_packet(ip, udp);
dst_port = proxy->client_port();
to = proxy->client_ip();
via = to;
if (_verbose) {
log("Matching UDP NAT link: ", *proxy); }
return &proxy->client();
}
default: error("unknown transport protocol"); }
return nullptr;
}
void Interface::_delete_tcp_proxy(Tcp_proxy * const proxy)
{
_tcp_proxies.remove(proxy);
unsigned const proxy_port = proxy->proxy_port();
destroy(_allocator, proxy);
_tcp_port_alloc.free(proxy_port);
_tcp_proxy_used--;
if (_verbose) {
log("Delete TCP NAT link: ", *proxy); }
}
void Interface::_delete_udp_proxy(Udp_proxy * const proxy)
{
_udp_proxies.remove(proxy);
unsigned const proxy_port = proxy->proxy_port();
destroy(_allocator, proxy);
_udp_port_alloc.free(proxy_port);
_udp_proxy_used--;
if (_verbose) {
log("Delete UDP NAT link: ", *proxy); }
}
Tcp_proxy *Interface::_new_tcp_proxy(unsigned const client_port,
Ipv4_address client_ip,
Ipv4_address proxy_ip)
{
if (_tcp_proxy_used == _tcp_proxy) {
throw Too_many_tcp_proxies(); }
unsigned const proxy_port = _tcp_port_alloc.alloc();
Tcp_proxy * const proxy =
new (_allocator) Tcp_proxy(client_port, proxy_port, client_ip,
proxy_ip, *this, _ep, _rtt_sec);
_tcp_proxies.insert(proxy);
_tcp_proxy_used++;
if (_verbose) {
log("New TCP NAT link: ", *proxy); }
return proxy;
}
Udp_proxy *Interface::_new_udp_proxy(unsigned const client_port,
Ipv4_address client_ip,
Ipv4_address proxy_ip)
{
if (_udp_proxy_used == _udp_proxy) {
throw Too_many_udp_proxies(); }
unsigned const proxy_port = _udp_port_alloc.alloc();
Udp_proxy * const proxy =
new (_allocator) Udp_proxy(client_port, proxy_port, client_ip,
proxy_ip, *this, _ep, _rtt_sec);
_udp_proxies.insert(proxy);
_udp_proxy_used++;
if (_verbose) {
log("New UDP NAT link: ", *proxy); }
return proxy;
}
bool Interface::_chk_delete_tcp_proxy(Tcp_proxy * &proxy)
{
if (!proxy->del()) {
return false; }
Tcp_proxy * const next_proxy = proxy->next();
_delete_tcp_proxy(proxy);
proxy = next_proxy;
return true;
}
bool Interface::_chk_delete_udp_proxy(Udp_proxy * &proxy)
{
if (!proxy->del()) {
return false; }
Udp_proxy * const next_proxy = proxy->next();
_delete_udp_proxy(proxy);
proxy = next_proxy;
return true;
}
void Interface::_handle_ip(Ethernet_frame *eth, Genode::size_t eth_size,
bool &ack_packet, Packet_descriptor *packet)
{
/* prepare routing information */
size_t ip_size = eth_size - sizeof(Ethernet_frame);
Ipv4_packet *ip;
try { ip = new (eth->data<void>()) Ipv4_packet(ip_size); }
catch (Ipv4_packet::No_ip_packet) { log("Invalid IP packet"); return; }
uint8_t tlp = ip->protocol();
size_t tlp_size = ip->total_length() - ip->header_length() * 4;
void *tlp_ptr = tlp_packet(tlp, ip, tlp_size);
uint16_t dst_port = tlp_dst_port(tlp, tlp_ptr);
Interface *interface = nullptr;
Ipv4_address to = ip->dst();
Ipv4_address via = ip->dst();
/* ... first try to find a matching proxy route ... */
interface = _tlp_proxy_route(tlp, tlp_ptr, dst_port, ip, to, via);
/* ... if that fails go through all matching IP routes ... */
if (!interface) {
Ip_route * route = _ip_routes.first();
for (; route; route = route->next()) {
/* ... try all port routes of the current IP route ... */
if (!route->matches(ip->dst())) {
continue; }
Port_route *port = tlp_port_list(tlp, route)->first();
for (; port; port = port->next()) {
if (port->dst() != dst_port) {
continue; }
interface = _interface_tree.find_by_label(port->label().string());
if (interface) {
bool const to_set = port->to() != Ipv4_address();
bool const via_set = port->via() != Ipv4_address();
if (to_set && !via_set) {
to = port->to();
via = port->to();
break;
}
if (via_set) {
via = port->via(); }
if (to_set) {
to = port->to(); }
break;
}
}
if (interface) {
break; }
/* ... then try the IP route itself ... */
interface = _interface_tree.find_by_label(route->label().string());
if (interface) {
bool const to_set = route->to() != Ipv4_address();
bool const via_set = route->via() != Ipv4_address();
if (to_set && !via_set) {
to = route->to();
via = route->to();
break;
}
if (via_set) {
via = route->via(); }
if (to_set) {
to = route->to(); }
break;
}
}
}
/* ... and give up if no IP and port route matches */
if (!interface) {
if (_verbose) { log("Unroutable packet"); }
return;
}
/* send ARP request if there is no ARP entry for the destination IP */
try {
Arp_cache_entry &arp_entry = _arp_cache.find_by_ip_addr(via);
eth->dst(arp_entry.mac_addr().addr);
} catch (Arp_cache::No_matching_entry) {
interface->arp_broadcast(via);
_arp_waiters.insert(new (_allocator) Arp_waiter(*this, via, *eth,
eth_size, *packet));
ack_packet = false;
return;
}
/* adapt packet to the collected info */
eth->src(interface->router_mac());
ip->dst(to);
tlp_dst_port(tlp, tlp_ptr, dst_port);
/* if configured, use proxy source IP */
if (_proxy) {
Ipv4_address client_ip = ip->src();
ip->src(interface->router_ip());
/* if also the source port doesn't match port routes, use proxy port */
uint16_t src_port = tlp_src_port(tlp, tlp_ptr);
Ip_route *dst_route = interface->ip_routes().first();
for (; dst_route; dst_route = dst_route->next()) {
if (tlp_port_tree(tlp, dst_route)->find_by_dst(src_port)) {
break; }
}
if (!dst_route) {
_tlp_apply_port_proxy(tlp, tlp_ptr, ip, client_ip, src_port); }
}
/* update checksums and deliver packet */
tlp_update_checksum(tlp, tlp_ptr, ip->src(), ip->dst(), tlp_size);
ip->checksum(Ipv4_packet::calculate_checksum(*ip));
interface->send(eth, eth_size);
}
Tcp_proxy *Interface::_find_tcp_proxy_by_client(Ipv4_address ip, uint16_t port)
{
Tcp_proxy *proxy = _tcp_proxies.first();
while (proxy) {
if (_chk_delete_tcp_proxy(proxy)) {
continue; }
if (proxy->matches_client(ip, port)) {
break; }
proxy = proxy->next();
}
return proxy;
}
Tcp_proxy *Interface::_find_tcp_proxy_by_proxy(Ipv4_address ip, uint16_t port)
{
Tcp_proxy *proxy = _tcp_proxies.first();
while (proxy) {
if (_chk_delete_tcp_proxy(proxy)) {
continue; }
if (proxy->matches_proxy(ip, port)) {
break; }
proxy = proxy->next();
}
return proxy;
}
Udp_proxy *Interface::_find_udp_proxy_by_client(Ipv4_address ip, uint16_t port)
{
Udp_proxy *proxy = _udp_proxies.first();
while (proxy) {
if (_chk_delete_udp_proxy(proxy)) {
continue; }
if (proxy->matches_client(ip, port)) {
break; }
proxy = proxy->next();
}
return proxy;
}
Udp_proxy *Interface::_find_udp_proxy_by_proxy(Ipv4_address ip, uint16_t port)
{
Udp_proxy *proxy = _udp_proxies.first();
while (proxy) {
if (_chk_delete_udp_proxy(proxy)) {
continue; }
if (proxy->matches_proxy(ip, port)) {
break; }
proxy = proxy->next();
}
return proxy;
}
void Interface::arp_broadcast(Ipv4_address ip_addr)
{
using Ethernet_arp = Ethernet_frame_sized<sizeof(Arp_packet)>;
Ethernet_arp eth_arp(Mac_address(0xff), _router_mac, Ethernet_frame::ARP);
void * const eth_data = eth_arp.data<void>();
size_t const arp_size = sizeof(eth_arp) - sizeof(Ethernet_frame);
Arp_packet * const arp = new (eth_data) Arp_packet(arp_size);
arp->hardware_address_type(Arp_packet::ETHERNET);
arp->protocol_address_type(Arp_packet::IPV4);
arp->hardware_address_size(sizeof(Mac_address));
arp->protocol_address_size(sizeof(Ipv4_address));
arp->opcode(Arp_packet::REQUEST);
arp->src_mac(_router_mac);
arp->src_ip(_router_ip);
arp->dst_mac(Mac_address(0xff));
arp->dst_ip(ip_addr);
send(&eth_arp, sizeof(eth_arp));
}
void Interface::_remove_arp_waiter(Arp_waiter *arp_waiter)
{
_arp_waiters.remove(arp_waiter);
destroy(arp_waiter->interface().allocator(), arp_waiter);
}
Arp_waiter *Interface::_new_arp_entry(Arp_waiter *arp_waiter,
Arp_cache_entry *arp_entry)
{
Arp_waiter *next_arp_waiter = arp_waiter->next();
if (arp_waiter->new_arp_cache_entry(*arp_entry)) {
_remove_arp_waiter(arp_waiter); }
return next_arp_waiter;
}
void Interface::_handle_arp_reply(Arp_packet * const arp)
{
try {
_arp_cache.find_by_ip_addr(arp->src_ip());
if (_verbose) {
log("ARP entry already exists"); }
return;
} catch (Arp_cache::No_matching_entry) {
try {
Arp_cache_entry *arp_entry =
new (env()->heap()) Arp_cache_entry(arp->src_ip(),
arp->src_mac());
_arp_cache.insert(arp_entry);
Arp_waiter *arp_waiter = _arp_waiters.first();
for (; arp_waiter; arp_waiter = _new_arp_entry(arp_waiter, arp_entry)) { }
} catch (Allocator::Out_of_memory) {
if (_verbose) {
error("failed to allocate new ARP cache entry"); }
return;
}
}
}
void Interface::_handle_arp_request(Ethernet_frame * const eth,
size_t const eth_size,
Arp_packet * const arp)
{
/* ignore packets that do not target the router */
if (arp->dst_ip() != router_ip()) {
if (_verbose) {
log("ARP does not target router"); }
return;
}
/* interchange source and destination MAC and IP addresses */
arp->dst_ip(arp->src_ip());
arp->dst_mac(arp->src_mac());
eth->dst(eth->src());
arp->src_ip(router_ip());
arp->src_mac(router_mac());
eth->src(router_mac());
/* mark packet as reply and send it back to its sender */
arp->opcode(Arp_packet::REPLY);
send(eth, eth_size);
}
void Interface::_handle_arp(Ethernet_frame *eth, size_t eth_size)
{
/* ignore ARP regarding protocols other than IPv4 via ethernet */
size_t arp_size = eth_size - sizeof(Ethernet_frame);
Arp_packet *arp = new (eth->data<void>()) Arp_packet(arp_size);
if (!arp->ethernet_ipv4()) {
if (_verbose) {
log("ARP for unknown protocol");
return;
}
}
switch (arp->opcode()) {
case Arp_packet::REPLY: _handle_arp_reply(arp); break;
case Arp_packet::REQUEST: _handle_arp_request(eth, eth_size, arp); break;
default: if (_verbose) { log("unknown ARP operation"); } }
}
void Interface::_ready_to_submit(unsigned)
{
while (sink()->packet_avail()) {
_packet = sink()->get_packet();
if (!_packet.size()) {
continue; }
if (_verbose) {
Genode::printf("<< %s ", Interface::string());
dump_eth(sink()->packet_content(_packet), _packet.size());
Genode::printf("\n");
}
bool ack = true;
handle_ethernet(sink()->packet_content(_packet), _packet.size(), ack,
&_packet);
if (!ack) {
continue; }
if (!sink()->ready_to_ack()) {
if (_verbose) {
log("Ack state FULL"); }
return;
}
sink()->acknowledge_packet(_packet);
}
}
void Interface::continue_handle_ethernet(void *src, Genode::size_t size,
Packet_descriptor *packet)
{
bool ack = true;
handle_ethernet(src, size, ack, packet);
if (!ack) {
if (_verbose) { log("Failed to continue eth handling"); }
return;
}
if (!sink()->ready_to_ack()) {
if (_verbose) { log("Ack state FULL"); }
return;
}
sink()->acknowledge_packet(*packet);
}
void Interface::_ready_to_ack(unsigned)
{
while (source()->ack_avail()) {
source()->release_packet(source()->get_acked_packet()); }
}
void Interface::handle_ethernet(void *src, size_t size, bool &ack,
Packet_descriptor *packet)
{
try {
Ethernet_frame * const eth = new (src) Ethernet_frame(size);
switch (eth->type()) {
case Ethernet_frame::ARP: _handle_arp(eth, size); break;
case Ethernet_frame::IPV4: _handle_ip(eth, size, ack, packet); break;
default: ; }
}
catch (Ethernet_frame::No_ethernet_frame) {
error("Invalid ethernet frame at ", label()); }
catch (Too_many_tcp_proxies) {
error("Too many TCP NAT links requested by ", label()); }
}
void Interface::send(Ethernet_frame *eth, Genode::size_t size)
{
if (_verbose) {
Genode::printf(">> %s ", Interface::string());
dump_eth(eth, size);
Genode::printf("\n");
}
try {
/* copy and submit packet */
Packet_descriptor packet = source()->alloc_packet(size);
char *content = source()->packet_content(packet);
Genode::memcpy((void *)content, (void *)eth, size);
source()->submit_packet(packet);
} catch (Packet_stream_source< ::Nic::Session::Policy>::Packet_alloc_failed) {
if (_verbose) {
log("Failed to allocate packet"); }
}
}
void Interface::_read_route(Xml_node &route_xn)
{
Ipv4_address_prefix dst =
route_xn.attribute_value("dst", Ipv4_address_prefix());
Ipv4_address const via = route_xn.attribute_value("via", Ipv4_address());
Ipv4_address const to = route_xn.attribute_value("to", Ipv4_address());
Ip_route *route;
try {
char const *in = route_xn.attribute("label").value_base();
size_t in_sz = route_xn.attribute("label").value_size();
route = new (_allocator) Ip_route(dst.address, dst.prefix, via, to, in,
in_sz, _allocator, route_xn,
_verbose);
} catch (Xml_attribute::Nonexistent_attribute) {
route = new (_allocator) Ip_route(dst.address, dst.prefix, via, to,
"", 0, _allocator, route_xn,
_verbose);
}
_ip_routes.insert(route);
if (_verbose) {
log(" IP route: ", *route); }
}
Interface::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)
:
Session_label(label_from_args(args)),
Avl_string_base(Session_label::string()),
_sink_ack(ep, *this, &Interface::_ack_avail),
_sink_submit(ep, *this, &Interface::_ready_to_submit),
_source_ack(ep, *this, &Interface::_ready_to_ack),
_source_submit(ep, *this, &Interface::_packet_avail), _ep(ep),
_router_mac(router_mac), _router_ip(router_ip), _mac(mac),
_allocator(allocator), _policy(*static_cast<Session_label *>(this)),
_proxy(_policy.attribute_value("nat", false)), _tcp_proxies(tcp_proxies),
_tcp_port_alloc(tcp_port_alloc), _udp_proxies(udp_proxies),
_udp_port_alloc(udp_port_alloc), _rtt_sec(rtt_sec),
_interface_tree(interface_tree), _arp_cache(arp_cache),
_arp_waiters(arp_waiters), _verbose(verbose)
{
if (_proxy) {
_tcp_proxy = _policy.attribute_value("nat-tcp-ports", 0UL);
_tcp_proxy_used = 0;
_udp_proxy = _policy.attribute_value("nat-udp-ports", 0UL);
_udp_proxy_used = 0;
}
if (_verbose) {
log("Interface \"", *static_cast<Session_label *>(this), "\"");
log(" MAC ", _mac);
log(" Router identity: MAC ", _router_mac, " IP ", _router_ip);
if (_proxy) {
log(" NAT TCP ports: ", _tcp_proxy, " UDP ports: ", _udp_proxy);
} else {
log(" NAT off");
}
}
try {
Xml_node route = _policy.sub_node("ip");
for (; ; route = route.next("ip")) { _read_route(route); }
} catch (Xml_node::Nonexistent_sub_node) { }
_interface_tree.insert(this);
}
Interface::~Interface()
{
/* make interface unfindable */
_interface_tree.remove(this);
/* delete all ARP requests of this interface */
Arp_waiter *arp_waiter = _arp_waiters.first();
while (arp_waiter) {
Arp_waiter *next_arp_waiter = arp_waiter->next();
if (&arp_waiter->interface() != this) {
_remove_arp_waiter(arp_waiter); }
arp_waiter = next_arp_waiter;
}
/* delete all UDP proxies of this interface */
Udp_proxy *udp_proxy = _udp_proxies.first();
while (udp_proxy) {
Udp_proxy *next_udp_proxy = udp_proxy->next();
if (&udp_proxy->client() == this) {
_delete_udp_proxy(udp_proxy); }
udp_proxy = next_udp_proxy;
}
/* delete all TCP proxies of this interface */
Tcp_proxy *tcp_proxy = _tcp_proxies.first();
while (tcp_proxy) {
Tcp_proxy *next_tcp_proxy = tcp_proxy->next();
if (&tcp_proxy->client() == this) {
_delete_tcp_proxy(tcp_proxy); }
tcp_proxy = next_tcp_proxy;
}
}
Interface *Interface_tree::find_by_label(char const *label)
{
if (!strcmp(label, "")) {
return nullptr; }
Interface *interface = static_cast<Interface *>(first());
if (!interface) {
return nullptr; }
interface = static_cast<Interface *>(interface->find_by_name(label));
return interface;
}

View File

@ -0,0 +1,205 @@
/*
* \brief A net interface in form of a signal-driven NIC-packet handler
* \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 _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>
namespace Net {
using ::Nic::Packet_stream_sink;
using ::Nic::Packet_stream_source;
using ::Nic::Packet_descriptor;
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 Net::Interface : public Genode::Session_label, public Genode::Avl_string_base
{
protected:
Signal_rpc_member _sink_ack;
Signal_rpc_member _sink_submit;
Signal_rpc_member _source_ack;
Signal_rpc_member _source_submit;
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;
void _read_route(Genode::Xml_node &route_xn);
Tcp_proxy *_find_tcp_proxy_by_client(Ipv4_address ip,
Genode::uint16_t port);
Udp_proxy *_find_udp_proxy_by_client(Ipv4_address ip,
Genode::uint16_t port);
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 _tlp_apply_port_proxy(Genode::uint8_t tlp, void *tlp_ptr,
Ipv4_packet *ip, Ipv4_address client_ip,
Genode::uint16_t src_port);
Tcp_proxy *_find_tcp_proxy_by_proxy(Ipv4_address ip,
Genode::uint16_t port);
Udp_proxy *_find_udp_proxy_by_proxy(Ipv4_address ip,
Genode::uint16_t port);
void _handle_arp_reply(Arp_packet * const arp);
void _handle_arp_request(Ethernet_frame * const eth,
Genode::size_t const eth_size,
Arp_packet * const arp);
Arp_waiter *_new_arp_entry(Arp_waiter *arp_waiter,
Arp_cache_entry *entry);
void _remove_arp_waiter(Arp_waiter *arp_waiter);
void _handle_arp(Ethernet_frame *eth, Genode::size_t size);
void _handle_ip(Ethernet_frame *eth, Genode::size_t eth_size,
bool &ack_packet, Packet_descriptor *packet);
Tcp_proxy *_new_tcp_proxy(unsigned const client_port,
Ipv4_address client_ip,
Ipv4_address proxy_ip);
Udp_proxy *_new_udp_proxy(unsigned const client_port,
Ipv4_address client_ip,
Ipv4_address proxy_ip);
void _delete_tcp_proxy(Tcp_proxy * const proxy);
bool _chk_delete_tcp_proxy(Tcp_proxy * &proxy);
void _delete_udp_proxy(Udp_proxy * const proxy);
bool _chk_delete_udp_proxy(Udp_proxy * &proxy);
/***********************************
** Packet-stream signal handlers **
***********************************/
void _ready_to_submit(unsigned);
void _ack_avail(unsigned) { }
void _ready_to_ack(unsigned);
void _packet_avail(unsigned) { }
public:
struct Too_many_tcp_proxies : Genode::Exception { };
struct Too_many_udp_proxies : 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();
void arp_broadcast(Ipv4_address ip_addr);
void send(Ethernet_frame *eth, Genode::size_t eth_size);
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);
/***************
** 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);
};
#endif /* _INTERFACE_H_ */

View File

@ -0,0 +1,141 @@
/*
* \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

@ -0,0 +1,89 @@
/*
* \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,21 @@
/*
* \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.
*/
#include <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

@ -0,0 +1,80 @@
/*
* \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:
struct 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

@ -0,0 +1,129 @@
/*
* \brief Server component for Network Address Translation on NIC sessions
* \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.
*/
/* Genode */
#include <nic/xml_node.h>
#include <os/server.h>
/* local includes */
#include <component.h>
#include <arp_cache.h>
#include <uplink.h>
#include <port_allocator.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);
public:
Main(Server::Entrypoint &ep);
};
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)
:
_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)
{
/* 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));
}
/************
** Server **
************/
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); }
}

View File

@ -0,0 +1,25 @@
/*
* \brief Allocator for UDP/TCP ports
* \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_allocator.h>
using namespace Net;
using namespace Genode;
Port_allocator::Port_allocator()
{
try { alloc_index(0); }
catch (Already_allocated) { throw Failed_to_reserve_port_0(); }
}

View File

@ -0,0 +1,40 @@
/*
* \brief Allocator for UDP/TCP ports
* \author Martin Stein
* \author Stefan Kalkowski
* \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 _PORT_ALLOCATOR_H_
#define _PORT_ALLOCATOR_H_
/* Genode includes */
#include <util/bit_allocator.h>
namespace Net { class Port_allocator; }
class Net::Port_allocator
{
public:
enum { FIRST = 49152, COUNT = 16384 };
private:
Genode::Bit_allocator<COUNT> _alloc;
public:
Genode::uint16_t alloc() { return _alloc.alloc() + FIRST; }
void free(Genode::uint16_t port) { _alloc.free(port - FIRST); }
};
#endif /* _PORT_ALLOCATOR_H_ */

View File

@ -0,0 +1,54 @@
/*
* \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

@ -0,0 +1,75 @@
/*
* \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,120 @@
/*
* \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

@ -0,0 +1,134 @@
/*
* \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,11 @@
TARGET = nic_router
LIBS += base net config server
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
INC_DIR += $(PRG_DIR)
vpath *.cc $(REP_DIR)/src/server/proxy_arp

View File

@ -0,0 +1,64 @@
/*
* \brief NIC handler
* \author Stefan Kalkowski
* \date 2013-05-24
*/
/*
* Copyright (C) 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.
*/
/* 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>
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)
:
Nic::Packet_allocator(env()->heap()),
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)
{
rx_channel()->sigh_ready_to_ack(_sink_ack);
rx_channel()->sigh_packet_avail(_sink_submit);
tx_channel()->sigh_ack_avail(_source_ack);
tx_channel()->sigh_ready_to_submit(_source_submit);
}

View File

@ -0,0 +1,63 @@
/*
* \brief Uplink interface in form of a NIC session component
* \author Martin Stein
* \date 2016-08-23
*/
/*
* 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 _UPLINK_H_
#define _UPLINK_H_
/* Genode includes */
#include <nic_session/connection.h>
/* local includes */
#include <interface.h>
namespace Net {
class Port_allocator;
class Uplink;
}
class Net::Uplink : public Nic::Packet_allocator,
public Nic::Connection, public Net::Interface
{
private:
enum {
PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
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);
/********************
** Net::Interface **
********************/
Packet_stream_sink<Nic::Session::Policy> *sink() { return rx(); }
Packet_stream_source<Nic::Session::Policy> *source() { return tx(); }
};
#endif /* _UPLINK_H_ */