diff --git a/repos/libports/run/nic_router.run b/repos/libports/run/nic_router.run
new file mode 100644
index 0000000000..3e5a7abc09
--- /dev/null
+++ b/repos/libports/run/nic_router.run
@@ -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 {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_platform_drv_config
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if $udp_down_to_down_1 config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if $udp_up_to_down_1 config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if $udp_down_to_up_1 config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if $tcp_up_to_down_1 config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if $tcp_up_to_down_2 config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if $tcp_down_to_up_1 config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append 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
diff --git a/repos/os/src/server/nic_router/README b/repos/os/src/server/nic_router/README
new file mode 100644
index 0000000000..f81f789e35
--- /dev/null
+++ b/repos/os/src/server/nic_router/README
@@ -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:
+
+!
+!
+
+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:
+
+!
+!
+
+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:
+
+!
+!
+
+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.
+
+!
+!
+!
+
+
+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:
+
+!
+
+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:
+
+!
+!
+!
+
+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:
+
+! ...
+
+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:
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+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:
+
+!
+!
+!
+!
+!
+
+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.
diff --git a/repos/os/src/server/nic_router/arp_cache.cc b/repos/os/src/server/nic_router/arp_cache.cc
new file mode 100644
index 0000000000..e42a3fd949
--- /dev/null
+++ b/repos/os/src/server/nic_router/arp_cache.cc
@@ -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
+
+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::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);
+}
diff --git a/repos/os/src/server/nic_router/arp_cache.h b/repos/os/src/server/nic_router/arp_cache.h
new file mode 100644
index 0000000000..9a1f9604e3
--- /dev/null
+++ b/repos/os/src/server/nic_router/arp_cache.h
@@ -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
+#include
+#include
+
+#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
+{
+ 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
+{
+ struct No_matching_entry : Genode::Exception { };
+
+ Arp_cache_entry &find_by_ip_addr(Ipv4_address ip_addr);
+};
+
+#endif /* _ARP_CACHE_H_ */
diff --git a/repos/os/src/server/nic_router/arp_waiter.cc b/repos/os/src/server/nic_router/arp_waiter.cc
new file mode 100644
index 0000000000..49ddd2a1b6
--- /dev/null
+++ b/repos/os/src/server/nic_router/arp_waiter.cc
@@ -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
+#include
+#include
+
+using namespace Net;
+using namespace Genode;
+
+
+Arp_waiter::Arp_waiter(Interface &interface, Ipv4_address ip_addr,
+ Ethernet_frame ð, 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;
+}
diff --git a/repos/os/src/server/nic_router/arp_waiter.h b/repos/os/src/server/nic_router/arp_waiter.h
new file mode 100644
index 0000000000..409fca501d
--- /dev/null
+++ b/repos/os/src/server/nic_router/arp_waiter.h
@@ -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
+#include
+#include
+
+#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;
+}
+
+class Net::Arp_waiter : public Genode::List::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 ð, 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 ð() const { return _eth; }
+ Genode::size_t eth_size() const { return _eth_size; }
+};
+
+#endif /* _ARP_WAITER_H_ */
diff --git a/repos/os/src/server/nic_router/component.cc b/repos/os/src/server/nic_router/component.cc
new file mode 100644
index 0000000000..67f241d7cd
--- /dev/null
+++ b/repos/os/src/server/nic_router/component.cc
@@ -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
+#include
+#include
+
+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(&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_alloc);
+}
+
+
+Guarded_range_allocator::
+Guarded_range_allocator(Allocator &backing_store, size_t const amount)
+:
+ _guarded_alloc(&backing_store, amount), _range_alloc(&_guarded_alloc)
+{ }
diff --git a/repos/os/src/server/nic_router/component.h b/repos/os/src/server/nic_router/component.h
new file mode 100644
index 0000000000..9d95c1fecb
--- /dev/null
+++ b/repos/os/src/server/nic_router/component.h
@@ -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
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+#include
+
+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 *sink() { return _tx.sink(); }
+ Packet_stream_source *source() { return _rx.source(); }
+};
+
+
+class Net::Root : public Genode::Root_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_ */
diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc
new file mode 100644
index 0000000000..cef37cbf82
--- /dev/null
+++ b/repos/os/src/server/nic_router/interface.cc
@@ -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
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+#include
+#include
+#include
+#include
+
+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()) Tcp_packet(size);
+ case Udp_packet::IP_ID: return new (ip->data()) 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()) 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;
+ Ethernet_arp eth_arp(Mac_address(0xff), _router_mac, Ethernet_frame::ARP);
+
+ void * const eth_data = eth_arp.data();
+ 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(ð_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()) 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(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(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(first());
+ if (!interface) {
+ return nullptr; }
+
+ interface = static_cast(interface->find_by_name(label));
+ return interface;
+}
diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h
new file mode 100644
index 0000000000..22ba8cd8ea
--- /dev/null
+++ b/repos/os/src/server/nic_router/interface.h
@@ -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
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+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;
+ using Arp_waiter_list = Genode::List;
+ using Tcp_proxy_list = Genode::List;
+ using Udp_proxy_list = Genode::List;
+ using Signal_rpc_member = Genode::Signal_rpc_member;
+}
+
+
+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
+{
+ Interface *find_by_label(char const *label);
+};
+
+#endif /* _INTERFACE_H_ */
diff --git a/repos/os/src/server/nic_router/ip_route.cc b/repos/os/src/server/nic_router/ip_route.cc
new file mode 100644
index 0000000000..8eda1b85e7
--- /dev/null
+++ b/repos/os/src/server/nic_router/ip_route.cc
@@ -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
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+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::insert(route, behind);
+}
diff --git a/repos/os/src/server/nic_router/ip_route.h b/repos/os/src/server/nic_router/ip_route.h
new file mode 100644
index 0000000000..b148a91b01
--- /dev/null
+++ b/repos/os/src/server/nic_router/ip_route.h
@@ -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
+
+#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::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
+{
+ public:
+
+ Ip_route *longest_prefix_match(Ipv4_address ip_addr);
+
+ void insert(Ip_route *route);
+};
+
+#endif /* _IP_ROUTE_H_ */
diff --git a/repos/os/src/server/nic_router/mac_allocator.cc b/repos/os/src/server/nic_router/mac_allocator.cc
new file mode 100644
index 0000000000..7e5ac058a3
--- /dev/null
+++ b/repos/os/src/server/nic_router/mac_allocator.cc
@@ -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
+
+/**
+ * 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);
diff --git a/repos/os/src/server/nic_router/mac_allocator.h b/repos/os/src/server/nic_router/mac_allocator.h
new file mode 100644
index 0000000000..a8458dbd80
--- /dev/null
+++ b/repos/os/src/server/nic_router/mac_allocator.h
@@ -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
+#include
+
+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_ */
diff --git a/repos/os/src/server/nic_router/main.cc b/repos/os/src/server/nic_router/main.cc
new file mode 100644
index 0000000000..830ecbfea4
--- /dev/null
+++ b/repos/os/src/server/nic_router/main.cc
@@ -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
+#include
+
+/* local includes */
+#include
+#include
+#include
+#include
+
+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); }
+}
diff --git a/repos/os/src/server/nic_router/port_allocator.cc b/repos/os/src/server/nic_router/port_allocator.cc
new file mode 100644
index 0000000000..a12e3d26bf
--- /dev/null
+++ b/repos/os/src/server/nic_router/port_allocator.cc
@@ -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
+
+using namespace Net;
+using namespace Genode;
+
+
+Port_allocator::Port_allocator()
+{
+ try { alloc_index(0); }
+ catch (Already_allocated) { throw Failed_to_reserve_port_0(); }
+}
diff --git a/repos/os/src/server/nic_router/port_allocator.h b/repos/os/src/server/nic_router/port_allocator.h
new file mode 100644
index 0000000000..12d3e987a9
--- /dev/null
+++ b/repos/os/src/server/nic_router/port_allocator.h
@@ -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
+
+namespace Net { class Port_allocator; }
+
+class Net::Port_allocator
+{
+ public:
+
+ enum { FIRST = 49152, COUNT = 16384 };
+
+ private:
+
+ Genode::Bit_allocator _alloc;
+
+ public:
+
+ Genode::uint16_t alloc() { return _alloc.alloc() + FIRST; }
+
+ void free(Genode::uint16_t port) { _alloc.free(port - FIRST); }
+};
+
+#endif /* _PORT_ALLOCATOR_H_ */
diff --git a/repos/os/src/server/nic_router/port_route.cc b/repos/os/src/server/nic_router/port_route.cc
new file mode 100644
index 0000000000..bf906c04dd
--- /dev/null
+++ b/repos/os/src/server/nic_router/port_route.cc
@@ -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
+
+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::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;
+}
diff --git a/repos/os/src/server/nic_router/port_route.h b/repos/os/src/server/nic_router/port_route.h
new file mode 100644
index 0000000000..2ca3aadd72
--- /dev/null
+++ b/repos/os/src/server/nic_router/port_route.h
@@ -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
+#include
+#include
+#include
+
+#ifndef _PORT_ROUTE_H_
+#define _PORT_ROUTE_H_
+
+namespace Net {
+
+ class Port_route;
+ class Port_route_tree;
+ using Port_route_list = Genode::List;
+}
+
+
+class Net::Port_route : public Genode::Avl_node,
+ 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 *find_by_dst(Genode::uint16_t dst);
+};
+
+#endif /* _PORT_ROUTE_H_ */
diff --git a/repos/os/src/server/nic_router/proxy.cc b/repos/os/src/server/nic_router/proxy.cc
new file mode 100644
index 0000000000..8fe38881a5
--- /dev/null
+++ b/repos/os/src/server/nic_router/proxy.cc
@@ -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
+#include
+#include
+
+/* local includes */
+#include
+
+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);
+}
diff --git a/repos/os/src/server/nic_router/proxy.h b/repos/os/src/server/nic_router/proxy.h
new file mode 100644
index 0000000000..556f2bfdf5
--- /dev/null
+++ b/repos/os/src/server/nic_router/proxy.h
@@ -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
+#include
+#include
+
+namespace Net {
+
+ class Tcp_packet;
+ class Udp_packet;
+ class Interface;
+ class Tcp_proxy;
+ class Udp_proxy;
+
+ using Tcp_proxy_list = Genode::List;
+ using Udp_proxy_list = Genode::List;
+}
+
+class Net::Tcp_proxy : public Genode::List::Element
+{
+ private:
+
+ using Signal_handler = Genode::Signal_handler;
+
+ 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::Element
+{
+ private:
+
+ using Signal_handler = Genode::Signal_handler;
+
+ 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_ */
diff --git a/repos/os/src/server/nic_router/target.mk b/repos/os/src/server/nic_router/target.mk
new file mode 100644
index 0000000000..7fdcbd49ef
--- /dev/null
+++ b/repos/os/src/server/nic_router/target.mk
@@ -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
diff --git a/repos/os/src/server/nic_router/uplink.cc b/repos/os/src/server/nic_router/uplink.cc
new file mode 100644
index 0000000000..1aceb8e01d
--- /dev/null
+++ b/repos/os/src/server/nic_router/uplink.cc
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+#include
+
+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);
+}
diff --git a/repos/os/src/server/nic_router/uplink.h b/repos/os/src/server/nic_router/uplink.h
new file mode 100644
index 0000000000..1d866b4c04
--- /dev/null
+++ b/repos/os/src/server/nic_router/uplink.h
@@ -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
+
+/* local includes */
+#include
+
+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 *sink() { return rx(); }
+ Packet_stream_source *source() { return tx(); }
+};
+
+#endif /* _UPLINK_H_ */