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_ */