From 89085096d27f4feb812f32e5172325ab7dd7f042 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Mon, 12 Sep 2016 12:55:12 +0200 Subject: [PATCH] nic_router: new user interface and optimizations Fixes #2139 --- repos/libports/run/nic_router.run | 138 +-- .../nic_bridge}/mac_allocator.h | 12 +- repos/os/src/server/nic_bridge/component.h | 2 +- repos/os/src/server/nic_bridge/mac.h | 80 -- .../nic_bridge/{mac.cc => mac_allocator.cc} | 3 +- repos/os/src/server/nic_bridge/target.mk | 2 +- repos/os/src/server/nic_router/README | 498 +++++--- repos/os/src/server/nic_router/arp_cache.cc | 56 +- repos/os/src/server/nic_router/arp_cache.h | 48 +- repos/os/src/server/nic_router/arp_waiter.cc | 26 +- repos/os/src/server/nic_router/arp_waiter.h | 48 +- repos/os/src/server/nic_router/component.cc | 239 ++-- repos/os/src/server/nic_router/component.h | 162 +-- .../os/src/server/nic_router/configuration.cc | 62 + .../os/src/server/nic_router/configuration.h | 50 + repos/os/src/server/nic_router/direct_rule.cc | 36 + repos/os/src/server/nic_router/direct_rule.h | 99 ++ repos/os/src/server/nic_router/domain.cc | 165 +++ repos/os/src/server/nic_router/domain.h | 165 +++ .../os/src/server/nic_router/forward_rule.cc | 70 ++ repos/os/src/server/nic_router/forward_rule.h | 77 ++ repos/os/src/server/nic_router/interface.cc | 1066 +++++++---------- repos/os/src/server/nic_router/interface.h | 234 ++-- repos/os/src/server/nic_router/ip_route.cc | 141 --- repos/os/src/server/nic_router/ip_route.h | 89 -- repos/os/src/server/nic_router/ip_rule.cc | 25 + repos/os/src/server/nic_router/ip_rule.h | 35 + repos/os/src/server/nic_router/leaf_rule.cc | 37 + repos/os/src/server/nic_router/leaf_rule.h | 50 + repos/os/src/server/nic_router/link.cc | 237 ++++ repos/os/src/server/nic_router/link.h | 213 ++++ .../os/src/server/nic_router/mac_allocator.cc | 14 +- repos/os/src/server/nic_router/main.cc | 113 +- repos/os/src/server/nic_router/nat_rule.cc | 84 ++ repos/os/src/server/nic_router/nat_rule.h | 83 ++ repos/os/src/server/nic_router/permit_rule.cc | 106 ++ repos/os/src/server/nic_router/permit_rule.h | 105 ++ repos/os/src/server/nic_router/pointer.h | 55 + .../src/server/nic_router/port_allocator.cc | 31 +- .../os/src/server/nic_router/port_allocator.h | 31 +- repos/os/src/server/nic_router/port_route.cc | 54 - repos/os/src/server/nic_router/port_route.h | 75 -- .../os/src/server/nic_router/protocol_name.cc | 39 + .../os/src/server/nic_router/protocol_name.h | 29 + repos/os/src/server/nic_router/proxy.cc | 120 -- repos/os/src/server/nic_router/proxy.h | 134 --- repos/os/src/server/nic_router/rule.h | 22 + repos/os/src/server/nic_router/target.mk | 13 +- .../src/server/nic_router/transport_rule.cc | 79 ++ .../os/src/server/nic_router/transport_rule.h | 54 + repos/os/src/server/nic_router/uplink.cc | 48 +- repos/os/src/server/nic_router/uplink.h | 43 +- tool/autopilot.list | 1 + 53 files changed, 3352 insertions(+), 2146 deletions(-) rename repos/os/{src/server/nic_router => include/nic_bridge}/mac_allocator.h (88%) delete mode 100644 repos/os/src/server/nic_bridge/mac.h rename repos/os/src/server/nic_bridge/{mac.cc => mac_allocator.cc} (89%) create mode 100644 repos/os/src/server/nic_router/configuration.cc create mode 100644 repos/os/src/server/nic_router/configuration.h create mode 100644 repos/os/src/server/nic_router/direct_rule.cc create mode 100644 repos/os/src/server/nic_router/direct_rule.h create mode 100644 repos/os/src/server/nic_router/domain.cc create mode 100644 repos/os/src/server/nic_router/domain.h create mode 100644 repos/os/src/server/nic_router/forward_rule.cc create mode 100644 repos/os/src/server/nic_router/forward_rule.h delete mode 100644 repos/os/src/server/nic_router/ip_route.cc delete mode 100644 repos/os/src/server/nic_router/ip_route.h create mode 100644 repos/os/src/server/nic_router/ip_rule.cc create mode 100644 repos/os/src/server/nic_router/ip_rule.h create mode 100644 repos/os/src/server/nic_router/leaf_rule.cc create mode 100644 repos/os/src/server/nic_router/leaf_rule.h create mode 100644 repos/os/src/server/nic_router/link.cc create mode 100644 repos/os/src/server/nic_router/link.h create mode 100644 repos/os/src/server/nic_router/nat_rule.cc create mode 100644 repos/os/src/server/nic_router/nat_rule.h create mode 100644 repos/os/src/server/nic_router/permit_rule.cc create mode 100644 repos/os/src/server/nic_router/permit_rule.h create mode 100644 repos/os/src/server/nic_router/pointer.h delete mode 100644 repos/os/src/server/nic_router/port_route.cc delete mode 100644 repos/os/src/server/nic_router/port_route.h create mode 100644 repos/os/src/server/nic_router/protocol_name.cc create mode 100644 repos/os/src/server/nic_router/protocol_name.h delete mode 100644 repos/os/src/server/nic_router/proxy.cc delete mode 100644 repos/os/src/server/nic_router/proxy.h create mode 100644 repos/os/src/server/nic_router/rule.h create mode 100644 repos/os/src/server/nic_router/transport_rule.cc create mode 100644 repos/os/src/server/nic_router/transport_rule.h diff --git a/repos/libports/run/nic_router.run b/repos/libports/run/nic_router.run index 154229400..d7d6c0c94 100644 --- a/repos/libports/run/nic_router.run +++ b/repos/libports/run/nic_router.run @@ -2,6 +2,9 @@ # Build # +# the lwip/http_clnt requires FOC +assert_spec foc + set tcp_up_to_down_1 1 set tcp_up_to_down_2 1 set tcp_down_to_up_1 1 @@ -63,67 +66,40 @@ append config { - - - - - - - - - - - - - + + + + + + + - - - - + + + + + + + - - - - + + + - - - - - + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -136,10 +112,10 @@ append config { - - - - + + + + @@ -149,7 +125,7 @@ append config { append_if $udp_down_to_down_1 config { - + @@ -166,7 +142,7 @@ append_if $udp_down_to_down_1 config { - + @@ -184,7 +160,7 @@ append_if $udp_down_to_down_1 config { append_if $udp_up_to_down_1 config { - + @@ -201,7 +177,7 @@ append_if $udp_up_to_down_1 config { - + @@ -210,7 +186,7 @@ append_if $udp_up_to_down_1 config { + gateway="18.17.16.14" netmask="255.255.0.0" port="1"> @@ -219,7 +195,7 @@ append_if $udp_up_to_down_1 config { append_if $udp_down_to_up_1 config { - + @@ -236,7 +212,7 @@ append_if $udp_down_to_up_1 config { - + @@ -245,7 +221,7 @@ append_if $udp_down_to_up_1 config { + netmask="255.255.255.0" gateway="10.0.2.55" port="65535"> @@ -254,7 +230,7 @@ append_if $udp_down_to_up_1 config { append_if $tcp_up_to_down_1 config { - + @@ -269,7 +245,7 @@ append_if $tcp_up_to_down_1 config { - + @@ -287,7 +263,7 @@ append_if $tcp_up_to_down_1 config { append_if $tcp_up_to_down_2 config { - + @@ -295,15 +271,15 @@ append_if $tcp_up_to_down_2 config { - - + @@ -321,7 +297,7 @@ append_if $tcp_up_to_down_2 config { append_if $tcp_down_to_up_1 config { - + @@ -336,7 +312,7 @@ append_if $tcp_down_to_up_1 config { - + @@ -378,17 +354,11 @@ append_platform_drv_boot_modules build_boot_image $boot_modules # qemu config -append qemu_args " -m 128 -nographic " +append qemu_args " -m 256 -nographic " append_if [have_spec x86] qemu_args " -net nic,model=e1000 " append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 " append qemu_args " -net user -redir udp:5555::1337 " -# -# FIXME: For unknown reasons, there seems to be a problem in the NIC router -# with the destruction of NIC sessions that had TCP connections running. -# This is why sometimes not all HTTP clients exit properly. Thus, look -# merely for 4 client exits (at least 3xUDP 1xTCP) by now. -# -run_genode_until ".*child \".*_clnt_.\" exited with exit value 0.*\n.*child \".*_clnt_.\" exited with exit value 0.*\n.*child \".*_clnt_.\" exited with exit value 0.*\n.*child \".*_clnt_.\" exited with exit value 0.*\n" 100 +run_genode_until ".*Test done.*\n.*Test done.*\n.*Test done.*\n.*Test done.*\n.*Test done.*\n.*Test done.*\n" 60 diff --git a/repos/os/src/server/nic_router/mac_allocator.h b/repos/os/include/nic_bridge/mac_allocator.h similarity index 88% rename from repos/os/src/server/nic_router/mac_allocator.h rename to repos/os/include/nic_bridge/mac_allocator.h index a8458dbd8..3fe14a69f 100644 --- a/repos/os/src/server/nic_router/mac_allocator.h +++ b/repos/os/include/nic_bridge/mac_allocator.h @@ -11,10 +11,10 @@ * under the terms of the GNU General Public License version 2. */ -#ifndef _MAC_H_ -#define _MAC_H_ +#ifndef _MAC_ALLOCATOR_H_ +#define _MAC_ALLOCATOR_H_ -/* Genode */ +/* Genode includes */ #include #include @@ -22,7 +22,7 @@ namespace Net { /** * The MAC allocator is used to administer MAC addresses for - * the proxy-ARP's client's. + * NIC session clients. */ class Mac_allocator { @@ -41,7 +41,7 @@ namespace Net { public: - struct Alloc_failed : Genode::Exception {}; + class Alloc_failed : Genode::Exception {}; /* reference MAC address */ @@ -77,4 +77,4 @@ namespace Net { }; } -#endif /* _MAC_H_ */ +#endif /* _MAC_ALLOCATOR_H_ */ diff --git a/repos/os/src/server/nic_bridge/component.h b/repos/os/src/server/nic_bridge/component.h index 1cee8eb61..a77f24ce6 100644 --- a/repos/os/src/server/nic_bridge/component.h +++ b/repos/os/src/server/nic_bridge/component.h @@ -20,13 +20,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include diff --git a/repos/os/src/server/nic_bridge/mac.h b/repos/os/src/server/nic_bridge/mac.h deleted file mode 100644 index 04df21988..000000000 --- a/repos/os/src/server/nic_bridge/mac.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * \brief MAC-address allocator - * \author Stefan Kalkowski - * \date 2010-08-25 - */ - -/* - * Copyright (C) 2010-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _MAC_H_ -#define _MAC_H_ - -/* Genode */ -#include -#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: - - class Alloc_failed : Genode::Exception {}; - - - /* reference MAC address */ - static Mac_address mac_addr_base; - - Mac_allocator() { Genode::memset(&_msbs, 0, sizeof(_msbs)); } - - - /** - * Allocates a new MAC address. - * - * \throws Alloc_failed if no more MAC addresses are available. - * \return MAC address - */ - Mac_address alloc() - { - for (int i=0; i < MSB_MAX; i++) { - if (!_msbs[i].used) { - _msbs[i].used = 1; - Mac_address mac = mac_addr_base; - mac.addr[5] = i; - return mac; - } - } - throw Alloc_failed(); - } - - /** - * Frees a formerly allocated MAC address. - */ - void free(Mac_address mac) { - _msbs[(unsigned)mac.addr[5]].used = 0; } - }; -} - -#endif /* _MAC_H_ */ diff --git a/repos/os/src/server/nic_bridge/mac.cc b/repos/os/src/server/nic_bridge/mac_allocator.cc similarity index 89% rename from repos/os/src/server/nic_bridge/mac.cc rename to repos/os/src/server/nic_bridge/mac_allocator.cc index 9beaef30d..41a310998 100644 --- a/repos/os/src/server/nic_bridge/mac.cc +++ b/repos/os/src/server/nic_bridge/mac_allocator.cc @@ -11,7 +11,8 @@ * under the terms of the GNU General Public License version 2. */ -#include +/* Genode includes */ +#include /** * We take the range 02:02:02:02:02:XX for our MAC address allocator, diff --git a/repos/os/src/server/nic_bridge/target.mk b/repos/os/src/server/nic_bridge/target.mk index f095e7136..f486fcb24 100644 --- a/repos/os/src/server/nic_bridge/target.mk +++ b/repos/os/src/server/nic_bridge/target.mk @@ -1,4 +1,4 @@ TARGET = nic_bridge LIBS = base net -SRC_CC = component.cc mac.cc main.cc nic.cc packet_handler.cc +SRC_CC = component.cc mac_allocator.cc main.cc nic.cc packet_handler.cc INC_DIR += $(PRG_DIR) diff --git a/repos/os/src/server/nic_router/README b/repos/os/src/server/nic_router/README index f81f789e3..b275aec20 100644 --- a/repos/os/src/server/nic_router/README +++ b/repos/os/src/server/nic_router/README @@ -7,202 +7,390 @@ Brief ##### -The nic_router component can be used to individually route IPv4 packets between -multiple NIC sessions. Thereby, it can translate between different IP subnets. -The component supports port forwarding, as well as the partitioning of the TCP -and UDP port spaces. +The 'nic_router' component can be used to individually route IPv4 packets +between multiple NIC sessions. Thereby, it can translate between different +subnets. The component supports IP routing, TCP and UDP routing, the +partitioning of the TCP and UDP port spaces, port forwarding, and NAT. -Basic routing -############# +Basics +###### -The nic_router component provides multiple sessions of the 'NIC' service +The 'nic_router' component provides multiple sessions of the 'NIC' service (downlinks) while requesting one 'NIC' session (the uplink) itself. Through common Genode session routing, the uplink can be connected to any other NIC server. Inside the component, uplink and downlinks are treated the same. Its -routing algorithm is ultimately controlled through the configuration. For each -NIC session there must be a policy: +routing algorithm is ultimately controlled through the configuration. NIC +sessions are assigned to domains. Each domain represents one subnet and a +corresponding routing configuration. Currently, each domain can contain +only one NIC session at a time. The assigment of sessions to domains is +controlled through the the common Genode session-policy tag: -! -! +! +! -The 'label' attribute must match the session label respectively 'uplink' for the -uplink session. The 'src' attribute contains a static IPv4 identity that -represents the nic_router with respect to that session. This identity is used -when doing Network-Address-Translation to the session. Moreover, a policy tag -may contain multiple 'ip' sub-tags: +The domain name can be freely choosen but must be unique. There is no need +to have a policy for the uplink. It is automatically assigned to the domain +named "uplink". For each domain there must be a domain tag: -! -! +! +! +! -Each 'ip' tag defines a routing rule for IPv4 packets that are sent by the -session of the surrounding policy. Such a rule needs at least a 'dst' attribute -that contains an IPv4 address prefix. When the nic_router receives an IPv4 -packet, it goes top-down through all the rules of the session, checking whether -the packet destination matches the 'dst' attribute. The first rule that matches -is inspected deeper. The next thing to be read is the 'label' attribute that -names the target session of the rule. If the label points to a valid session, -the packet could now be routed via this rule. But the IP rule is only remembered -as fallback yet because it may contain sub-tags for preferred UDP and TCP -routing: +The 'interface' attribute defines two things at once. First, it tells the +router which subnet can be found behind this domain, and second, which IP +identity the router shall use in case it has to communicate as itself with +the subnet. -! -! +Additionaly, the optional 'gateway' attribute can be set for a domain: -Those tags work pretty similar to the IP rules. The 'dst' attribute defines -the port that a packet must be addressed to for a match. The 'label' attribute -again points to the target session. If no matching UDP or TCP rule with a -valid target session is found inside the matching IP rule, the nic_router -falls back to the IP rule. If the target session of the matching IP rule isn't -valid, it continues with the next matching IP rule, and so on. +! + +It defines the standard gateway of the subnet behind this domain. If a packet +shall be routed to this domain and its final IP destination does not match +the subnet, its Ethernet destination is set to the MAC address of the gateway. +If a gateway isn't given for a domain, such packets get dropped. + +For each domain, the routing of packets from this domain can be configured +individually by adding subtags to the corresponding domain tag. There are +multiple types of subtags expressing different types of routing rules. The +following table gives a brief overview over the different subtags and their +meaning: + + Subtag | Description +--------------------------------------------------------------- + | Port forwarding for TCP port X * +--------------------------------------------------------------- + | Port forwarding for UDP port X * +--------------------------------------------------------------- + | Routing TCP packets that target + | IP range X * + | +--------------------------------------------------------------- + | Routing UDP packets that target + | IP range X * + | +--------------------------------------------------------------- + | Routing TCP packets that target + | IP range X and port Y or Z * + | + | +--------------------------------------------------------------- + | Routing UDP packets that target + | IP range X and port Y or Z * + | + | +--------------------------------------------------------------- + | Routing IP packets that target + | IP range X + +A detailed explanation of the different routing rules is given in the +following sections of this document. For all rules marked with a star, the +router also keeps track of corresponding TCP connections and UDP +pseudo-connections. With these so-called link states, corresponding reply +packets are automatically routed back. The user doesn't have to add an +additional back-routing rule for that. + +Now having this variety of ways of routing a packet, it is absolutely legal +that for one packet the domain may contain multiple rules that are applicable. +And additionally, there may even be a link state that fits. The router's +choice, however, is always deterministic. It follows a simple priority scheme: + +1) Link states +2) Port forwarding rules +3) Longest prefix match amongst TCP respectively UDP rules + 3.1) Subrule that permits any port + 3.2) Subrules that permit specific ports +4) Longest prefix match amongst IP rules -Modify destination and gateway -############################## +IP rules +######## -All three, the 'ip', 'udp', and 'tcp' tag may have further attributes 'to' -and/or 'via'. Both attributes define an IPv4 address. The address in the 'to' -attribute replaces the IPv4 destination of an affected packet. The address in -the 'via' attribute is the gateway for that rule. This means, when searching -for the next Ethernet destination of an affected packet, the nic_router uses the -'via' address as criterion. If not set, the 'via' address is always the IPv4 -destination of the packet. Hence, if only the 'to' attribute is set for a -rule, the value is also used as 'via' address. +These are examples for IP rules: -! -! -! +! +! +! + +IP rules only apply to IPv4 packets from the session of the surrounding +domain. The 'dst' attribute is compared with the IP destination of the packet. +The rule with the longest prefix match is taken. The packet is then routed to +the domain given in the rule. + +IP rules work pretty simple. They merely affect the Ethernet header of a +packet and they don't imply link-state tracking. This has consequences. First, +IP rules do not automatically route back reply packets from the remote side. +If you like to enable bidirectional communication via IP rules, both domains +must have an appropriate rule in their domain tag. And second, IP rules do not +consider a NAT configuration (Section [Configuring NAT]). As this could lead +to unexpected leakage of local IP addresses and ports, you should use the +combination of IP rules and NAT only with great care. -Network address translation -########################### +TCP and UDP rules +################# -The nic_router component can translate between different IPv4 subnets of clients, -and "uplink". When enabled within the policy of a client: +TCP and UDP rules must always be accompanied by one or more port permission +rules to get effective: -! +! +! +! +! +! +! +! -the source address of all IPv4 packets of that client is replaced by the 'src' -value of the applied target session. And the TCP/UDP source port is replaced -by a dynamically allocated source port of the applied target session. There is -an exception from the latter. If the source port of the packet matches a -TCP/UDP rule of the applied target session, the packet is assumed to be a -reply to a port-forwarded request from the counterside. In this case, the -source port remains unchanged to enable the receiver to correlate the packet -correctly. +TCP rules only apply to TCP packets and UDP rules only to UDP packets from the +session of the surrounding domain. The 'dst' attribute is compared with the IP +destination of the packet. The rule with the longest prefix match is taken. +If the rule contains a 'permit-any' subrule or a 'permit' subrule whose 'port' +attribute matches the destination port of the packet, the packet is routed to +the domain given in the subrule. -For partitioning the TCP/UDP port space, e.g., the source ports regarding the -'uplink' session between different client sessions, one can configure how many -ports may be used concurrently per client session: +For bidirectional traffic, you'll need only one TCP or UDP rule describing the +client-to-server direction. The server-sided domain doesn't need a rule as the +router correlates replies to the client-sided rule (and only those) via a link +state (Section [Link states]) that was created at the clients initial request. -! -! -! +TCP and UDP rules consider whether the router shall apply NAT +(Section [Configuring NAT]) for the client side. If this is the case, source +IP and port are replaced by the router's IP identity and a free port at the +server-sided domain. Also the corresponding link state takes this in account +to change back the destination of the replies. -The ports attribute contains the maximum number of ports that the nic_router -assigns to the given session. This is necessary to avoid that a NIC session -can run Denial-of-Service attacks against the nic_router by occupying all of -its ports. -For both, UDP and TCP activities, the nic_router holds link states. The link -states enable the nic_router to re-direct related packets from the counterside -correctly without the need for additional routing rules. Such link state rules -are the most preferred way of routing. Before the nic_router starts looking -for IP, UDP, or TCP rules for a packet, it tries to find a matching rule -amongst the link states. +Port-forwarding rules +##################### -As link state rules are created on demand and are bound to an active -connection between two peers, it is desirable to clear them away as soon as -they are not needed anymore. A precise algorithm for that enables the NIC -sessions to get the maximum out of their resources (ports, RAM). A TCP state -rule corresponding is held until the nic_router observes the four-way -termination handshake of TCP and two times the estimated round-trip time has -passed. The nic_router currently doesn't estimate any round-trip times by -itself. Instead it expects an attribute 'rtt_sec' in its 'config' tag: +These are examples for port-forwarding rules: + +! +! + +Port-forwarding rules only apply to packets that come from the session of the +surrounding domain and are addressed to the router's IP identity at this +domain (Section [Basics]). Amongst those, 'tcp-forward' rules only apply to +the TCP packets and 'udp-forward' rules only to the UDP packets. The 'port' +attribute is compared with the packet's destination port. If a matching rule +is found, the IP destination of the packet is changed to the value of the 'to' +attribute. Then, the packet is routed to the domain given in the rule. Note +that the router accepts only system and registered ports (0 to 49151) for port +forwarding. + +For bidirectional traffic, you'll need only one port-forwarding rule +describing the client-to-server direction. The server-sided domain doesn't +need a rule as the router correlates replies to the client-sided rule (and +only those) via a link state (Section [Link states]) that was created at the +clients initial request. + +It's in the nature of port forwarding that it comes along with NAT for the +server side. However, the router only translates the server IP. The port +remains unchanged. For the client side, port-forwarding rules apply NAT only +when configured (Section [Configuring NAT]). If this is the case, client IP +and port are translated. + + +Link states +########### + +Each time a packet gets routed by using a TCP, UDP, or port-forwarding rule, +the router creates a link state. From then on, all packets that belong +to the exchange this first packet initiated and come from one of the two +involved domains are routed by the link state and not by a rule. The costs +for the link state are paid by the session that sent the first packet. + +If a link state exists for a packet, it is unambiguously correlated through +the source IP and port as well as the destination IP and port. This is also +the case if the transfer includes NAT no matter of what kind or for which +side. + +It is desirable to discard a link state as soon as it is not needed anymore. +The more precise the algorithm for that, the more efficient can NIC sessions +use their resources (ports, RAM), and the less is the risk for DoS attacks. +In order to meet this requirement, the router needs to know the round-trip +time of the exchange behind a link state. This value is given through the +attribute 'rtt_sec' in the router's configuration: ! ... -This would set the round-trip time to three seconds which means that link -state rules wait six seconds after a termination handshake before they close -themselves. As UDP has no notion of connections, UDP state rules are simply -held for a duration of two times the round-trip time after the last packet. -This way, the peers can keep alive a UDP pseudo-connection by frequently -sending empty packets. +This would set the round-trip time to three seconds. The value is used for all +link states so you should choose it with care. If it is too low, replies that +normally need no routing rule may get lost. If it is too high, link states are +held longer than necessary. + +In general, each link state is discarded after a duration of two times the +round-trip time without a matching packet. For UDP link states, this is the +only rule and better known as hole punching. It allows peers to keep alive a +UDP pseudo-connection through the router by frequently sending empty packets. +The need for such a pseudo-connection arises from the router's demand to +support NAT for UDP transfers and the consequence of keeping the corresponding +mapping information. + +The lifetime management of TCP link states, in contrast, is more complex. In +addition to the common timeout, they may be discarded even if they still +receive packets. This is the case when the router observed the four-way +termination handshake of TCP and two times the round-trip time has passed. + + +Configuring NAT +############### + +In contrast to routing rules that affect packets coming from their domain, +NAT rules affect packets that go to their domain: + +! +! +! + +This would tell the router to apply NAT for the HTTP client when it speaks to +the uplink. This means, it affects all packets from the HTTP client that get +routed to the uplink by using a UDP, TCP, or port-forwarding rule respectively +a corresponding link state. If this is the case, the packet's source IP +address is changed to "10.0.2.55" and the source port is replaced by a free +source port of the router. When saying "free source port" this actually means +a port that the router currently doesn't use at the destination domain. So, +at each domain, the router has two complete port spaces for source NAT +available. One for UDP and one for TCP. Each port space contains the IANA +dynamic port range 49152 to 65535. + +As you can see, the NAT rule also has a 'tcp-ports' attribute. It restricts +how many TCP source ports of the uplink the HTTP client may use at a time. The +same goes also for UDP: + +! + +And even combined: + +! + +If one of the two attributes is not set, this means that no port shall be used +for this protocol which effectively disables it. Thus, at least one of the two +attributes must be set for the NAT rule to be sensible. Restricting the port +usage is necessary to avoid that a client can run Denial-of-Service attacks +against the destination domain by occupying all of its ports. Examples ######## -This paragraph will list and explain some interesting configuration snippets. -A comprehensive example of how to use the nic_router can be found in the test -script 'libports/run/nic_router.run' . +This section will list and explain some interesting configuration snippets. A +comprehensive example of how to use the router can be found in the test script +'libports/run/nic_router.run'. The environment for the examples shall be as +follows. There are two virtual subnets 192.168.1.0/24 and 192.168.2.0/24 that +connect as Virtnet A and B to the router. The standard gateway of the virtual +networks is the NIC router with IP 192.168.*.1 . The router's uplink leads to +the NIC driver that connects the machine with your home network 10.0.2.0/24. +Your home network is connected to the internet through its standard gateway +10.0.2.1 . -Accessing a private server network -================================== +Connecting local networks +========================= -In this example, we assume that there is a HTTP server "behind" the nic_router -(downlink) listening at port 80. Through the uplink session, several clients -may ask the nic_router for HTTP. The nic_router has the following -configuration: +Let's assume we simply want the virtual networks and the home network to be +able to talk to each other. Furthermore, the virtual networks shall be able to +use the internet connection of your home network. The router would have the +following configuration: -! -! -! -! -! +! +! ! -! -! -! - -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. +IP packets from Virtnet A and uplink that target an IP address 192.168.2.* are +routed to Virtnet B. IP packets from Virtnet B and uplink that target an IP +address 192.168.1.* are routed to Virtnet A. Packets that are addressed to +hosts in the same local network should never reach the router as they can be +transmitted directly. If there's a packet from one of the virtual networks +that doesn't target 192.168.1.* or 192.168.2.*, the IP 0.0.0.0/0 rules route +them to the uplink. If these packets target an IP 10.0.2.*, the router sends +them directly to the host in your home network. Otherwise, the router sends +them to your gateway 10.0.2.1 . Note that none of the packets is modified on +layer 2 or higher, so, no NAT is done by the router to hide the virtual +networks. -Limitations -########### +Clients in a private network +============================ -Currently, different client sessions should not share the same subnet to be able -to communicate with each other, because forwarding broadcast packets (e.g., ARP -packets) between different clients of the same subnet is not supported yet. +Now we have some clients in Virtnet A that like to talk to the internet as +well as to the home network. We want them to be hidden via NAT when they do so +and to be limited to HTTP+TLS/SSL and IMAP+TLS/SSL when talking to the +internet. The router would have the following configuration: + +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! + +From the packets that come from Virtnet A, those that target an IP 10.0.2.* +are routed to the uplink without inspecting the port. At the uplink, the +router notices that it shall apply NAT for Virtnet A. It replaces the source +IP with 10.0.2.55 and allocates one of its uplink source ports for the +exchange. On replies to Virtnet-A packets from the home network, the router +translates IP and port back using the corresponding link state. For packets +from Virtnet A that target other IPs, only the 0.0.0.0/0 rule applies and only +if the packet targets TCP port 443 or 993. Both ports route the packet to the +uplink where, again, NAT is applied and the packets are sent to the gateway +10.0.2.1 . + + +Servers in a private network +============================ + +In this example, we assume that there are three servers in Virtnet A. An HTTP +server at port 80 with IP 192.168.1.2, a GOPHER server at port 70 with IP +192.168.1.3, and a TFTP server at port 69 with IP 192.168.1.4 . Now you want +the servers (and only them) to be reachable to the home network via the +router's IP and to the internet via your gateway. The router would have the +following configuration: + +! +! +! +! +! +! +! +! +! +! +! + +Amongst the packets that come from the uplink, only those that are addressed +to 10.0.2.55 and TCP port 80, TCP port 70, or UDP port 69 are forwarded. +All these packets are forwarded to Virtnet A. But beforehand, their IP +destination is adapted. TCP-port-80 packets are redirected to 192.168.1.2, +TCP-port-70 packets to 192.168.1.3, and UDP-port-69 packets to 192.168.1.4. + +Amongst the packets that come from Virtnet A, only those that match a link +state at the uplink are forwarded, because the Virtnet-A domain contains no +rules. Thus, Virtnet A can only talk to the uplink in the context of +TCP-connections or UDP pseudo-connections that were opened by clients behind +the uplink. The servers IP addresses never leave Virtnet A. diff --git a/repos/os/src/server/nic_router/arp_cache.cc b/repos/os/src/server/nic_router/arp_cache.cc index e42a3fd94..1e796b2e6 100644 --- a/repos/os/src/server/nic_router/arp_cache.cc +++ b/repos/os/src/server/nic_router/arp_cache.cc @@ -18,40 +18,58 @@ using namespace Net; using namespace Genode; -Arp_cache_entry::Arp_cache_entry(Ipv4_address ip_addr, Mac_address mac_addr) +/********************* + ** Arp_cache_entry ** + *********************/ + +Arp_cache_entry::Arp_cache_entry(Ipv4_address const &ip, + Mac_address const &mac) : - _ip_addr(ip_addr), _mac_addr(mac_addr) + _ip(ip), _mac(mac) { } -bool Arp_cache_entry::higher(Arp_cache_entry *entry) +bool Arp_cache_entry::_higher(Ipv4_address const &ip) const { - return (memcmp(&entry->_ip_addr.addr, &_ip_addr.addr, - sizeof(_ip_addr.addr)) > 0); + return memcmp(ip.addr, _ip.addr, sizeof(_ip.addr)) > 0; } -Arp_cache_entry &Arp_cache_entry::find_by_ip_addr(Ipv4_address ip_addr) +Arp_cache_entry const & +Arp_cache_entry::find_by_ip(Ipv4_address const &ip) const { - if (ip_addr == _ip_addr) { + if (ip == _ip) { return *this; } - bool const side = - memcmp(&ip_addr.addr, _ip_addr.addr, sizeof(_ip_addr.addr)) > 0; - - Arp_cache_entry * entry = Avl_node::child(side); + Arp_cache_entry const *const entry = child(_higher(ip)); if (!entry) { - throw Arp_cache::No_matching_entry(); } + throw Arp_cache::No_match(); } - return entry->find_by_ip_addr(ip_addr); + return entry->find_by_ip(ip); } -Arp_cache_entry &Arp_cache::find_by_ip_addr(Ipv4_address ip_addr) +/*************** + ** Arp_cache ** + ***************/ + +void Arp_cache::new_entry(Ipv4_address const &ip, Mac_address const &mac) { - Arp_cache_entry * const entry = first(); - if (!entry) { - throw No_matching_entry(); } - - return entry->find_by_ip_addr(ip_addr); + if (_entries[_curr].constructed()) { remove(&(*_entries[_curr])); } + _entries[_curr].construct(ip, mac); + insert(&(*_entries[_curr])); + if (_curr < NR_OF_ENTRIES - 1) { + _curr++; + } else { + _curr = 0; + }; +} + + +Arp_cache_entry const &Arp_cache::find_by_ip(Ipv4_address const &ip) const +{ + if (!first()) { + throw No_match(); } + + return first()->find_by_ip(ip); } diff --git a/repos/os/src/server/nic_router/arp_cache.h b/repos/os/src/server/nic_router/arp_cache.h index 9a1f9604e..10e7924b6 100644 --- a/repos/os/src/server/nic_router/arp_cache.h +++ b/repos/os/src/server/nic_router/arp_cache.h @@ -11,54 +11,74 @@ * under the terms of the GNU General Public License version 2. */ +#ifndef _ARP_CACHE_H_ +#define _ARP_CACHE_H_ + /* Genode includes */ #include #include #include - -#ifndef _ARP_CACHE_H_ -#define _ARP_CACHE_H_ +#include namespace Net { class Arp_cache; class Arp_cache_entry; + using Arp_cache_entry_slot = Genode::Lazy_volatile_object; } + class Net::Arp_cache_entry : public Genode::Avl_node { private: - Ipv4_address const _ip_addr; - Mac_address const _mac_addr; + Ipv4_address const _ip; + Mac_address const _mac; + + bool _higher(Ipv4_address const &ip) const; public: - Arp_cache_entry(Ipv4_address ip_addr, Mac_address mac_addr); + Arp_cache_entry(Ipv4_address const &ip, Mac_address const &mac); - Arp_cache_entry &find_by_ip_addr(Ipv4_address ip_addr); + Arp_cache_entry const &find_by_ip(Ipv4_address const &ip) const; /************** ** Avl_node ** **************/ - bool higher(Arp_cache_entry *entry); + bool higher(Arp_cache_entry *entry) { return _higher(entry->_ip); } /*************** ** Accessors ** ***************/ - Ipv4_address ip_addr() const { return _ip_addr; } - Mac_address mac_addr() const { return _mac_addr; } + Mac_address const &mac() const { return _mac; } }; -struct Net::Arp_cache : Genode::Avl_tree -{ - struct No_matching_entry : Genode::Exception { }; - Arp_cache_entry &find_by_ip_addr(Ipv4_address ip_addr); +class Net::Arp_cache : public Genode::Avl_tree +{ + private: + + enum { + ENTRIES_SIZE = 1024 * sizeof(Genode::addr_t), + NR_OF_ENTRIES = ENTRIES_SIZE / sizeof(Arp_cache_entry), + }; + + Arp_cache_entry_slot _entries[NR_OF_ENTRIES]; + bool _init = true; + unsigned _curr = 0; + + public: + + struct No_match : Genode::Exception { }; + + void new_entry(Ipv4_address const &ip, Mac_address const &mac); + + Arp_cache_entry const &find_by_ip(Ipv4_address const &ip) const; }; #endif /* _ARP_CACHE_H_ */ diff --git a/repos/os/src/server/nic_router/arp_waiter.cc b/repos/os/src/server/nic_router/arp_waiter.cc index 49ddd2a1b..ca399a9e0 100644 --- a/repos/os/src/server/nic_router/arp_waiter.cc +++ b/repos/os/src/server/nic_router/arp_waiter.cc @@ -13,25 +13,27 @@ /* 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) +Arp_waiter::Arp_waiter(Interface &src, + Interface &dst, + Ipv4_address const &ip, + Packet_descriptor const &packet) : - _interface(interface), _ip_addr(ip_addr), _eth(eth), _eth_size(eth_size), + _src_le(this), _src(src), _dst_le(this), _dst(dst), _ip(ip), _packet(packet) -{ } - - -bool Arp_waiter::new_arp_cache_entry(Arp_cache_entry &entry) { - if (!(entry.ip_addr() == _ip_addr)) { return false; } - _interface.continue_handle_ethernet(&_eth, _eth_size, &_packet); - return true; + _src.own_arp_waiters().insert(&_src_le); + _dst.foreign_arp_waiters().insert(&_dst_le); +} + + +Arp_waiter::~Arp_waiter() +{ + _src.own_arp_waiters().remove(&_src_le); + _dst.foreign_arp_waiters().remove(&_dst_le); } diff --git a/repos/os/src/server/nic_router/arp_waiter.h b/repos/os/src/server/nic_router/arp_waiter.h index 409fca501..0ff551cb2 100644 --- a/repos/os/src/server/nic_router/arp_waiter.h +++ b/repos/os/src/server/nic_router/arp_waiter.h @@ -1,5 +1,5 @@ /* - * \brief Aspect of waiting for an ARP reply + * \brief Remember packets that wait for ARP replies at different interfaces * \author Martin Stein * \date 2016-08-19 */ @@ -11,50 +11,52 @@ * under the terms of the GNU General Public License version 2. */ -/* Genode includes */ -#include -#include -#include - #ifndef _ARP_WAITER_H_ #define _ARP_WAITER_H_ +/* Genode includes */ +#include +#include +#include + namespace Net { - using ::Nic::Packet_descriptor; + using Packet_descriptor = ::Nic::Packet_descriptor; class Interface; - class Ethernet_frame; class Arp_waiter; - class Arp_cache_entry; - using Arp_waiter_list = Genode::List; + using Arp_waiter_list_element = Genode::List_element; + using Arp_waiter_list = Genode::List; } -class Net::Arp_waiter : public Genode::List::Element + +class Net::Arp_waiter { private: - Interface &_interface; - Ipv4_address _ip_addr; - Ethernet_frame &_eth; - Genode::size_t const _eth_size; - Packet_descriptor &_packet; + Arp_waiter_list_element _src_le; + Interface &_src; + Arp_waiter_list_element _dst_le; + Interface &_dst; + Ipv4_address const _ip; + Packet_descriptor const _packet; public: - Arp_waiter(Interface &interface, Ipv4_address ip_addr, - Ethernet_frame ð, Genode::size_t const eth_size, - Packet_descriptor &packet); + Arp_waiter(Interface &src, + Interface &dst, + Ipv4_address const &ip, + Packet_descriptor const &packet); - bool new_arp_cache_entry(Arp_cache_entry &entry); + ~Arp_waiter(); /*************** ** Accessors ** ***************/ - Interface &interface() const { return _interface; } - Ethernet_frame ð() const { return _eth; } - Genode::size_t eth_size() const { return _eth_size; } + Interface &src() const { return _src; } + Ipv4_address const &ip() const { return _ip; } + Packet_descriptor const &packet() const { return _packet; } }; #endif /* _ARP_WAITER_H_ */ diff --git a/repos/os/src/server/nic_router/component.cc b/repos/os/src/server/nic_router/component.cc index 67f241d7c..9d5a529b4 100644 --- a/repos/os/src/server/nic_router/component.cc +++ b/repos/os/src/server/nic_router/component.cc @@ -11,59 +11,63 @@ * under the terms of the GNU General Public License version 2. */ +/* Genode includes */ +#include + /* local includes */ #include -#include -#include +#include using namespace Net; using namespace Genode; -bool Session_component::link_state() -{ - warning(__func__, " not implemented"); - return false; -} +/************************** + ** Communication_buffer ** + **************************/ - -void Session_component::link_state_sigh(Signal_context_capability sigh) -{ - warning(__func__, " not implemented"); -} - - -Net::Session_component::Session_component(Allocator &allocator, - size_t const amount, - size_t const tx_buf_size, - size_t const rx_buf_size, - Mac_address mac, - Server::Entrypoint &ep, - Mac_address router_mac, - Ipv4_address router_ip, - char const *args, - Port_allocator &tcp_port_alloc, - Port_allocator &udp_port_alloc, - Tcp_proxy_list &tcp_proxys, - Udp_proxy_list &udp_proxys, - unsigned const rtt_sec, - Interface_tree &interface_tree, - Arp_cache &arp_cache, - Arp_waiter_list &arp_waiters, - bool verbose) +Communication_buffer::Communication_buffer(Ram_session &ram, + Genode::size_t const size) : - Guarded_range_allocator(allocator, amount), - Tx_rx_communication_buffers(tx_buf_size, rx_buf_size), + Ram_dataspace_capability(ram.alloc(size)), _ram(ram) +{ } - Session_rpc_object( - Tx_rx_communication_buffers::tx_ds(), - Tx_rx_communication_buffers::rx_ds(), - &range_allocator(), ep.rpc_ep()), - Interface( - ep, router_mac, router_ip, guarded_allocator(), args, tcp_port_alloc, - udp_port_alloc, mac, tcp_proxys, udp_proxys, rtt_sec, interface_tree, - arp_cache, arp_waiters, verbose) +/**************************** + ** Session_component_base ** + ****************************/ + +Session_component_base:: +Session_component_base(Allocator &guarded_alloc_backing, + size_t const guarded_alloc_amount, + Ram_session &buf_ram, + size_t const tx_buf_size, + size_t const rx_buf_size) +: + _guarded_alloc(&guarded_alloc_backing, guarded_alloc_amount), + _range_alloc(&_guarded_alloc), _tx_buf(buf_ram, tx_buf_size), + _rx_buf(buf_ram, rx_buf_size) +{ } + + +/*********************** + ** Session_component ** + ***********************/ + +Net::Session_component::Session_component(Allocator &alloc, + Genode::Timer &timer, + size_t const amount, + Ram_session &buf_ram, + size_t const tx_buf_size, + size_t const rx_buf_size, + Mac_address const mac, + Entrypoint &ep, + Mac_address const &router_mac, + Domain &domain) +: + Session_component_base(alloc, amount, buf_ram, tx_buf_size, rx_buf_size), + Session_rpc_object(_tx_buf, _rx_buf, &_range_alloc, ep.rpc_ep()), + Interface(ep, timer, router_mac, _guarded_alloc, mac, domain) { _tx.sigh_ready_to_ack(_sink_ack); _tx.sigh_packet_avail(_sink_submit); @@ -72,106 +76,87 @@ Net::Session_component::Session_component(Allocator &allocator, } -Session_component::~Session_component() { } +bool Session_component::link_state() +{ + warning("Session_component::link_state not implemented"); + return false; +} -Net::Root::Root(Server::Entrypoint &ep, - Allocator &md_alloc, - Mac_address router_mac, - Port_allocator &tcp_port_alloc, - Port_allocator &udp_port_alloc, - Tcp_proxy_list &tcp_proxys, - Udp_proxy_list &udp_proxys, - unsigned rtt_sec, - Interface_tree &interface_tree, - Arp_cache &arp_cache, - Arp_waiter_list &arp_waiters, - bool verbose) +void Session_component::link_state_sigh(Signal_context_capability sigh) +{ + warning("Session_component::link_state_sigh not implemented"); +} + + +/********** + ** Root ** + **********/ + +Net::Root::Root(Entrypoint &ep, + Genode::Timer &timer, + Allocator &alloc, + Mac_address const &router_mac, + Configuration &config, + Ram_session &buf_ram) : - Root_component(&ep.rpc_ep(), &md_alloc), - _ep(ep), _router_mac(router_mac), _tcp_port_alloc(tcp_port_alloc), - _udp_port_alloc(udp_port_alloc), _tcp_proxys(tcp_proxys), - _udp_proxys(udp_proxys), _rtt_sec(rtt_sec), - _interface_tree(interface_tree), _arp_cache(arp_cache), - _arp_waiters(arp_waiters), _verbose(verbose) + Root_component(&ep.rpc_ep(), &alloc), _timer(timer), + _ep(ep), _router_mac(router_mac), _config(config), _buf_ram(buf_ram) { } Session_component *Net::Root::_create_session(char const *args) { - Ipv4_address src; try { - Session_policy policy(label_from_args(args)); - src = policy.attribute_value("src", Ipv4_address()); + Session_label const label = label_from_args(args); + Session_policy policy(label, _config.node()); + Domain_name domain_name(Cstring(policy.attribute("domain").value_base(), + policy.attribute("domain").value_size())); - } catch (Session_policy::No_policy_defined) { + Domain &domain = _config.domains().find_by_name(domain_name); - error("No matching policy"); - throw Root::Unavailable(); + size_t const ram_quota = + Arg_string::find_arg(args, "ram_quota").ulong_value(0); + + size_t const tx_buf_size = + Arg_string::find_arg(args, "tx_buf_size").ulong_value(0); + + size_t const rx_buf_size = + Arg_string::find_arg(args, "rx_buf_size").ulong_value(0); + + size_t const session_size = + max((size_t)4096, sizeof(Session_component)); + + if (ram_quota < session_size) { + throw Root::Quota_exceeded(); } + + if (tx_buf_size > ram_quota - session_size || + rx_buf_size > ram_quota - session_size || + tx_buf_size + rx_buf_size > ram_quota - session_size) + { + error("insufficient 'ram_quota' for session creation"); + throw Root::Quota_exceeded(); + } + return new (md_alloc()) + Session_component(*md_alloc(), _timer, ram_quota - session_size, + _buf_ram, tx_buf_size, rx_buf_size, + _mac_alloc.alloc(), _ep, _router_mac, + domain); } - if (src == Ipv4_address()) { - - error("Missing 'src' attribute in policy"); - throw Root::Unavailable(); + catch (Session_policy::No_policy_defined) { + error("no matching policy"); } - size_t const ram_quota = - Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); - - size_t const tx_buf_size = - Arg_string::find_arg(args, "tx_buf_size").ulong_value(0); - - size_t const rx_buf_size = - Arg_string::find_arg(args, "rx_buf_size").ulong_value(0); - - size_t const session_size = max((size_t)4096, sizeof(Session_component)); - if (ram_quota < session_size) { - throw Root::Quota_exceeded(); } - - if (tx_buf_size > ram_quota - session_size || - rx_buf_size > ram_quota - session_size || - tx_buf_size + rx_buf_size > ram_quota - session_size) - { - error("insufficient 'ram_quota' for session creation"); - throw Root::Quota_exceeded(); + catch (Domain_tree::No_match) { + error("no matching domain"); } - Mac_address mac; - try { mac = _mac_alloc.alloc(); } catch (Mac_allocator::Alloc_failed) { - error("failed to allocate MAC address"); - throw Root::Unavailable(); } - return new (md_alloc()) - Session_component(*env()->heap(), ram_quota - session_size, - tx_buf_size, rx_buf_size, mac, _ep, _router_mac, - src, args, _tcp_port_alloc, _udp_port_alloc, - _tcp_proxys, _udp_proxys, _rtt_sec, _interface_tree, - _arp_cache, _arp_waiters, _verbose); + catch (Xml_node::Nonexistent_attribute) { + error("missing domain attribute in policy"); + } + catch (Pointer::Valid) { + error("one session per domain only"); + } + throw Root::Unavailable(); } - - -Tx_rx_communication_buffers:: -Tx_rx_communication_buffers(Genode::size_t const tx_size, - Genode::size_t const rx_size) -: - _tx_buf(tx_size), _rx_buf(rx_size) -{ } - - -Communication_buffer::Communication_buffer(Genode::size_t size) -: - Ram_dataspace_capability(Genode::env()->ram_session()->alloc(size)) -{ } - - -Range_allocator &Guarded_range_allocator::range_allocator() -{ - return *static_cast(&_range_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 index 9d95c1fec..976f206c1 100644 --- a/repos/os/src/server/nic_router/component.h +++ b/repos/os/src/server/nic_router/component.h @@ -15,123 +15,93 @@ #define _COMPONENT_H_ /* Genode includes */ +#include #include #include #include -#include -#include +#include /* local includes */ -#include #include namespace Net { - class Port_allocator; - class Guarded_range_allocator; + class Domain; class Communication_buffer; - class Tx_rx_communication_buffers; + class Session_component_base; class Session_component; class Root; } -class Net::Guarded_range_allocator +class Net::Communication_buffer : public Genode::Ram_dataspace_capability { private: + Genode::Ram_session &_ram; + + public: + + Communication_buffer(Genode::Ram_session &ram, + Genode::size_t const size); + + ~Communication_buffer() { _ram.free(*this); } +}; + + +class Net::Session_component_base +{ + protected: + Genode::Allocator_guard _guarded_alloc; Nic::Packet_allocator _range_alloc; + Communication_buffer _tx_buf; + Communication_buffer _rx_buf; public: - Guarded_range_allocator(Genode::Allocator &backing_store, - Genode::size_t const amount); - - - /*************** - ** Accessors ** - ***************/ - - Genode::Allocator_guard &guarded_allocator() { return _guarded_alloc; } - Genode::Range_allocator &range_allocator(); + Session_component_base(Genode::Allocator &guarded_alloc_backing, + Genode::size_t const guarded_alloc_amount, + Genode::Ram_session &buf_ram, + Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size); }; -struct Net::Communication_buffer : Genode::Ram_dataspace_capability -{ - Communication_buffer(Genode::size_t const size); - - ~Communication_buffer() { Genode::env()->ram_session()->free(*this); } - - Genode::Dataspace_capability dataspace() { return *this; } -}; - - -class Net::Tx_rx_communication_buffers +class Net::Session_component : public Session_component_base, + public ::Nic::Session_rpc_object, + public Interface { private: - Communication_buffer _tx_buf, _rx_buf; + /******************** + ** Net::Interface ** + ********************/ + + Packet_stream_sink &_sink() { return *_tx.sink(); } + Packet_stream_source &_source() { return *_rx.source(); } public: - Tx_rx_communication_buffers(Genode::size_t const tx_size, - Genode::size_t const rx_size); - - Genode::Dataspace_capability tx_ds() { return _tx_buf.dataspace(); } - Genode::Dataspace_capability rx_ds() { return _rx_buf.dataspace(); } -}; - - -class Net::Session_component : public Guarded_range_allocator, - private Tx_rx_communication_buffers, - public ::Nic::Session_rpc_object, - public Interface -{ - private: - - void _arp_broadcast(Interface &interface, Ipv4_address ip_addr); - - public: - - Session_component(Genode::Allocator &allocator, - Genode::size_t amount, - Genode::size_t tx_buf_size, - Genode::size_t rx_buf_size, - Mac_address vmac, - Server::Entrypoint &ep, - Mac_address router_mac, - Ipv4_address router_ip, - char const *args, - Port_allocator &tcp_port_alloc, - Port_allocator &udp_port_alloc, - Tcp_proxy_list &tcp_proxys, - Udp_proxy_list &udp_proxys, - unsigned rtt_sec, - Interface_tree &interface_tree, - Arp_cache &arp_cache, - Arp_waiter_list &arp_waiters, - bool verbose); - - ~Session_component(); + Session_component(Genode::Allocator &alloc, + Genode::Timer &timer, + Genode::size_t const amount, + Genode::Ram_session &buf_ram, + Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Mac_address const mac, + Genode::Entrypoint &ep, + Mac_address const &router_mac, + Domain &domain); /****************** ** Nic::Session ** ******************/ - Mac_address mac_address() { return Interface::mac(); } + Mac_address mac_address() { return _mac; } bool link_state(); void link_state_sigh(Genode::Signal_context_capability sigh); - - - /******************** - ** Net::Interface ** - ********************/ - - Packet_stream_sink *sink() { return _tx.sink(); } - Packet_stream_source *source() { return _rx.source(); } }; @@ -139,40 +109,28 @@ 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; + Genode::Timer &_timer; + Mac_allocator _mac_alloc; + Genode::Entrypoint &_ep; + Mac_address const _router_mac; + Configuration &_config; + Genode::Ram_session &_buf_ram; /******************** ** Root_component ** ********************/ - Session_component *_create_session(const char *args); + Session_component *_create_session(char const *args); public: - Root(Server::Entrypoint &ep, - Genode::Allocator &md_alloc, - Mac_address router_mac, - Port_allocator &tcp_port_alloc, - Port_allocator &udp_port_alloc, - Tcp_proxy_list &tcp_proxys, - Udp_proxy_list &udp_proxys, - unsigned rtt_sec, - Interface_tree &interface_tree, - Arp_cache &arp_cache, - Arp_waiter_list &arp_waiters, - bool verbose); + Root(Genode::Entrypoint &ep, + Genode::Timer &timer, + Genode::Allocator &alloc, + Mac_address const &router_mac, + Configuration &config, + Genode::Ram_session &buf_ram); }; #endif /* _COMPONENT_H_ */ diff --git a/repos/os/src/server/nic_router/configuration.cc b/repos/os/src/server/nic_router/configuration.cc new file mode 100644 index 000000000..08ecddb07 --- /dev/null +++ b/repos/os/src/server/nic_router/configuration.cc @@ -0,0 +1,62 @@ +/* + * \brief Reflects the current router configuration through objects + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include + +/* Genode includes */ +#include +#include +#include + +using namespace Net; +using namespace Genode; + + +/*************** + ** Utilities ** + ***************/ + +static unsigned read_rtt_sec(Xml_node const &node) +{ + unsigned const rtt_sec = node.attribute_value("rtt_sec", 0UL); + if (!rtt_sec) { + warning("fall back to default rtt_sec=\"3\""); + return 3; + } + return rtt_sec; +} + + +/******************* + ** Configuration ** + *******************/ + +Configuration::Configuration(Xml_node const node, Allocator &alloc) +: + _alloc(alloc), _verbose(node.attribute_value("verbose", false)), + _rtt_sec(read_rtt_sec(node)), _node(node) +{ + /* read domains */ + node.for_each_sub_node("domain", [&] (Xml_node const &node) { + try { _domains.insert(*new (_alloc) Domain(*this, node, _alloc)); } + catch (Domain::Invalid) { warning("invalid domain"); } + }); + /* as they must resolve domain names, create rules after domains */ + _domains.for_each([&] (Domain &domain) { + if (_verbose) { + log("Domain: ", domain); } + + domain.create_rules(_domains); + }); +} diff --git a/repos/os/src/server/nic_router/configuration.h b/repos/os/src/server/nic_router/configuration.h new file mode 100644 index 000000000..396b8323b --- /dev/null +++ b/repos/os/src/server/nic_router/configuration.h @@ -0,0 +1,50 @@ +/* + * \brief Reflects the current router configuration through objects + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CONFIGURATION_H_ +#define _CONFIGURATION_H_ + +/* local includes */ +#include + +namespace Genode { class Allocator; } + +namespace Net { class Configuration; } + + +class Net::Configuration +{ + private: + + Genode::Allocator &_alloc; + bool const _verbose; + unsigned const _rtt_sec; + Domain_tree _domains; + Genode::Xml_node const _node; + + public: + + Configuration(Genode::Xml_node const node, Genode::Allocator &alloc); + + + /*************** + ** Accessors ** + ***************/ + + bool verbose() const { return _verbose; } + unsigned rtt_sec() const { return _rtt_sec; } + Domain_tree &domains() { return _domains; } + Genode::Xml_node node() const { return _node; } +}; + +#endif /* _CONFIGURATION_H_ */ diff --git a/repos/os/src/server/nic_router/direct_rule.cc b/repos/os/src/server/nic_router/direct_rule.cc new file mode 100644 index 000000000..abc653299 --- /dev/null +++ b/repos/os/src/server/nic_router/direct_rule.cc @@ -0,0 +1,36 @@ +/* + * \brief Routing rule for direct traffic between two interfaces + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include + +/* Genode includes */ +#include + +using namespace Net; +using namespace Genode; + + +Direct_rule_base::Direct_rule_base(Xml_node const &node) +: + _dst(node.attribute_value("dst", Ipv4_address_prefix())) +{ + if (!_dst.valid()) { + throw Invalid(); } +} + + +void Direct_rule_base::print(Output &output) const +{ + Genode::print(output, _dst); +} diff --git a/repos/os/src/server/nic_router/direct_rule.h b/repos/os/src/server/nic_router/direct_rule.h new file mode 100644 index 000000000..52121fd68 --- /dev/null +++ b/repos/os/src/server/nic_router/direct_rule.h @@ -0,0 +1,99 @@ +/* + * \brief Routing rule for direct traffic between two interfaces + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _DIRECT_RULE_H_ +#define _DIRECT_RULE_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include + +namespace Genode { class Xml_node; } + +namespace Net { + + class Direct_rule_base; + template class Direct_rule; + template class Direct_rule_list; +} + + +class Net::Direct_rule_base : public Rule +{ + protected: + + Ipv4_address_prefix const _dst; + + public: + + Direct_rule_base(Genode::Xml_node const &node); + + + /********* + ** log ** + *********/ + + void print(Genode::Output &output) const; + + + /*************** + ** Accessors ** + ***************/ + + Ipv4_address_prefix const &dst() const { return _dst; } +}; + + +template +struct Net::Direct_rule : Direct_rule_base, + Direct_rule_list::Element +{ + Direct_rule(Genode::Xml_node const &node) : Direct_rule_base(node) { } +}; + + +template +struct Net::Direct_rule_list : Genode::List +{ + using List = Genode::List; + + struct No_match : Genode::Exception { }; + + T const &longest_prefix_match(Ipv4_address const &ip) const + { + /* first match is sufficient as the list is prefix-size-sorted */ + for (T const *curr = List::first(); curr; curr = curr->next()) { + if (curr->dst().prefix_matches(ip)) { + return *curr; } + } + throw No_match(); + } + + void insert(T &rule) + { + /* ensure that the list stays prefix-size-sorted (descending) */ + T *behind = nullptr; + for (T *curr = List::first(); curr; curr = curr->next()) { + if (rule.dst().prefix >= curr->dst().prefix) { + break; } + + behind = curr; + } + List::insert(&rule, behind); + } +}; + +#endif /* _RULE_H_ */ diff --git a/repos/os/src/server/nic_router/domain.cc b/repos/os/src/server/nic_router/domain.cc new file mode 100644 index 000000000..73616007a --- /dev/null +++ b/repos/os/src/server/nic_router/domain.cc @@ -0,0 +1,165 @@ +/* + * \brief Reflects an effective domain configuration node + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include +#include + +/* Genode includes */ +#include +#include +#include + +using namespace Net; +using namespace Genode; + + +/*********************** + ** Domain_avl_member ** + ***********************/ + +Domain_avl_member::Domain_avl_member(Domain_name const &name, + Domain &domain) +: + Avl_string_base(name.string()), _domain(domain) +{ } + + +/***************** + ** Domain_base ** + *****************/ + +Domain_base::Domain_base(Xml_node const &node) +: + _name(Cstring(node.attribute("name").value_base(), + node.attribute("name").value_size())) +{ } + + +/************ + ** Domain ** + ************/ + +void Domain::_read_forward_rules(Cstring const &protocol, + Domain_tree &domains, + Xml_node const &node, + char const *type, + Forward_rule_tree &rules) +{ + node.for_each_sub_node(type, [&] (Xml_node const &node) { + try { + Forward_rule &rule = *new (_alloc) Forward_rule(domains, node); + rules.insert(&rule); + if (_config.verbose()) { + log(" Forward rule: ", protocol, " ", rule); } + } + catch (Rule::Invalid) { warning("invalid forward rule"); } + }); +} + + +void Domain::_read_transport_rules(Cstring const &protocol, + Domain_tree &domains, + Xml_node const &node, + char const *type, + Transport_rule_list &rules) +{ + node.for_each_sub_node(type, [&] (Xml_node const &node) { + try { + rules.insert(*new (_alloc) Transport_rule(domains, node, _alloc, + protocol, _config)); + } + catch (Rule::Invalid) { warning("invalid transport rule"); } + }); +} + + +void Domain::print(Output &output) const +{ + Genode::print(output, "\"", _name, "\""); +} + + +Domain::Domain(Configuration &config, Xml_node const &node, Allocator &alloc) +: + Domain_base(node), _avl_member(_name, *this), _config(config), + _node(node), _alloc(alloc), + _interface_attr(node.attribute_value("interface", Ipv4_address_prefix())), + _gateway(node.attribute_value("gateway", Ipv4_address())), + _gateway_valid(_gateway.valid()) +{ + if (_name == Domain_name() || !_interface_attr.valid() || + (_gateway_valid && !_interface_attr.prefix_matches(_gateway))) + { + throw Invalid(); + } +} + + +void Domain::create_rules(Domain_tree &domains) +{ + /* read forward rules */ + _read_forward_rules(tcp_name(), domains, _node, "tcp-forward", + _tcp_forward_rules); + _read_forward_rules(udp_name(), domains, _node, "udp-forward", + _udp_forward_rules); + + /* read UDP and TCP rules */ + _read_transport_rules(tcp_name(), domains, _node, "tcp", _tcp_rules); + _read_transport_rules(udp_name(), domains, _node, "udp", _udp_rules); + + /* read NAT rules */ + _node.for_each_sub_node("nat", [&] (Xml_node const &node) { + try { + _nat_rules.insert( + new (_alloc) Nat_rule(domains, _tcp_port_alloc, + _udp_port_alloc, node)); + } + catch (Rule::Invalid) { warning("invalid NAT rule"); } + }); + /* read IP rules */ + _node.for_each_sub_node("ip", [&] (Xml_node const &node) { + try { _ip_rules.insert(*new (_alloc) Ip_rule(domains, node)); } + catch (Rule::Invalid) { warning("invalid IP rule"); } + }); +} + + +Ipv4_address const &Domain::next_hop(Ipv4_address const &ip) const +{ + if (_interface_attr.prefix_matches(ip)) { return ip; } + if (_gateway_valid) { return _gateway; } + throw No_next_hop(); +} + + +/***************** + ** Domain_tree ** + *****************/ + +Domain &Domain_tree::domain(Avl_string_base const &node) +{ + return static_cast(&node)->domain(); +} + +Domain &Domain_tree::find_by_name(Domain_name name) +{ + if (name == Domain_name() || !first()) { + throw No_match(); } + + Avl_string_base *node = first()->find_by_name(name.string()); + if (!node) { + throw No_match(); } + + return domain(*node); +} diff --git a/repos/os/src/server/nic_router/domain.h b/repos/os/src/server/nic_router/domain.h new file mode 100644 index 000000000..435c7720c --- /dev/null +++ b/repos/os/src/server/nic_router/domain.h @@ -0,0 +1,165 @@ +/* + * \brief Reflects an effective domain configuration node + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _DOMAIN_H_ +#define _DOMAIN_H_ + +/* local includes */ +#include +#include +#include +#include +#include +#include + +/* Genode includes */ +#include +#include + +namespace Genode { class Allocator; } + +namespace Net { + + class Interface; + class Configuration; + class Domain_avl_member; + class Domain_base; + class Domain; + class Domain_tree; + using Domain_name = Genode::String<160>; +} + + +class Net::Domain_avl_member : public Genode::Avl_string_base +{ + private: + + Domain &_domain; + + public: + + Domain_avl_member(Domain_name const &name, + Domain &domain); + + + /*************** + ** Accessors ** + ***************/ + + Domain &domain() const { return _domain; } +}; + + +class Net::Domain_base +{ + protected: + + Domain_name const _name; + + Domain_base(Genode::Xml_node const &node); +}; + + +class Net::Domain : public Domain_base +{ + private: + + Domain_avl_member _avl_member; + Configuration &_config; + Genode::Xml_node _node; + Genode::Allocator &_alloc; + Ipv4_address_prefix _interface_attr; + Ipv4_address const _gateway; + bool const _gateway_valid; + Ip_rule_list _ip_rules; + Forward_rule_tree _tcp_forward_rules; + Forward_rule_tree _udp_forward_rules; + Transport_rule_list _tcp_rules; + Transport_rule_list _udp_rules; + Port_allocator _tcp_port_alloc; + Port_allocator _udp_port_alloc; + Nat_rule_tree _nat_rules; + Pointer _interface; + + void _read_forward_rules(Genode::Cstring const &protocol, + Domain_tree &domains, + Genode::Xml_node const &node, + char const *type, + Forward_rule_tree &rules); + + void _read_transport_rules(Genode::Cstring const &protocol, + Domain_tree &domains, + Genode::Xml_node const &node, + char const *type, + Transport_rule_list &rules); + + public: + + struct Invalid : Genode::Exception { }; + struct No_next_hop : Genode::Exception { }; + + Domain(Configuration &config, + Genode::Xml_node const &node, + Genode::Allocator &alloc); + + void create_rules(Domain_tree &domains); + + Ipv4_address const &next_hop(Ipv4_address const &ip) const; + + + /********* + ** log ** + *********/ + + void print(Genode::Output &output) const; + + + /*************** + ** Accessors ** + ***************/ + + Domain_name const &name() { return _name; } + Ip_rule_list &ip_rules() { return _ip_rules; } + Forward_rule_tree &tcp_forward_rules() { return _tcp_forward_rules; } + Forward_rule_tree &udp_forward_rules() { return _udp_forward_rules; } + Transport_rule_list &tcp_rules() { return _tcp_rules; } + Transport_rule_list &udp_rules() { return _udp_rules; } + Nat_rule_tree &nat_rules() { return _nat_rules; } + Ipv4_address_prefix &interface_attr() { return _interface_attr; } + Pointer &interface() { return _interface; } + Configuration &config() const { return _config; } + Domain_avl_member &avl_member() { return _avl_member; } +}; + + +struct Net::Domain_tree : Genode::Avl_tree +{ + using Avl_tree = Genode::Avl_tree; + + struct No_match : Genode::Exception { }; + + static Domain &domain(Genode::Avl_string_base const &node); + + Domain &find_by_name(Domain_name name); + + template + void for_each(FUNC && functor) const { + Avl_tree::for_each([&] (Genode::Avl_string_base const &node) { + functor(domain(node)); + }); + } + + void insert(Domain &domain) { Avl_tree::insert(&domain.avl_member()); } +}; + +#endif /* _DOMAIN_H_ */ diff --git a/repos/os/src/server/nic_router/forward_rule.cc b/repos/os/src/server/nic_router/forward_rule.cc new file mode 100644 index 000000000..fa1ca8028 --- /dev/null +++ b/repos/os/src/server/nic_router/forward_rule.cc @@ -0,0 +1,70 @@ +/* + * \brief Rule for forwarding a TCP/UDP port of the router to an interface + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include +#include + +/* Genode includes */ +#include + +using namespace Net; +using namespace Genode; + + +/****************** + ** Forward_rule ** + ******************/ + +void Forward_rule::print(Output &output) const +{ + Genode::print(output, "port ", _port, " requests to ", _to, + " at ", _domain); +} + + +Forward_rule::Forward_rule(Domain_tree &domains, Xml_node const &node) +: + Leaf_rule(domains, node), + _port(node.attribute_value("port", 0UL)), + _to(node.attribute_value("to", Ipv4_address())) +{ + if (!_port || !_to.valid() || dynamic_port(_port)) { + throw Invalid(); } +} + + +Forward_rule const &Forward_rule::find_by_port(uint8_t const port) const +{ + if (port == _port) { + return *this; } + + Forward_rule *const rule = Avl_node::child(port > _port); + if (!rule) { + throw Forward_rule_tree::No_match(); } + + return rule->find_by_port(port); +} + + +/*********************** + ** Forward_rule_tree ** + ***********************/ + +Forward_rule const &Forward_rule_tree::find_by_port(uint8_t const port) const +{ + if (!first()) { + throw No_match(); } + + return first()->find_by_port(port); +} diff --git a/repos/os/src/server/nic_router/forward_rule.h b/repos/os/src/server/nic_router/forward_rule.h new file mode 100644 index 000000000..8e83e925e --- /dev/null +++ b/repos/os/src/server/nic_router/forward_rule.h @@ -0,0 +1,77 @@ +/* + * \brief Rule for forwarding a TCP/UDP port of the router to an interface + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _FORWARD_RULE_H_ +#define _FORWARD_RULE_H_ + +/* local includes */ +#include + +/* Genode includes */ +#include +#include + +namespace Net { + + class Forward_rule; + class Forward_rule_tree; + class Forward_link; + class Forward_link_tree; +} + + +class Net::Forward_rule : public Leaf_rule, + public Genode::Avl_node +{ + private: + + Genode::uint8_t const _port; + Ipv4_address const _to; + + public: + + Forward_rule(Domain_tree &domains, Genode::Xml_node const &node); + + Forward_rule const &find_by_port(Genode::uint8_t const port) const; + + + /********* + ** log ** + *********/ + + void print(Genode::Output &output) const; + + + /************** + ** Avl_node ** + **************/ + + bool higher(Forward_rule *rule) { return rule->_port > _port; } + + + /*************** + ** Accessors ** + ***************/ + + Ipv4_address const &to() const { return _to; } +}; + + +struct Net::Forward_rule_tree : Genode::Avl_tree +{ + struct No_match : Genode::Exception { }; + + Forward_rule const &find_by_port(Genode::uint8_t const port) const; +}; + +#endif /* _FORWARD_RULE_H_ */ diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index cef37cbf8..1ace557f1 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -12,791 +12,631 @@ */ /* Genode includes */ -#include -#include -#include +#include #include -#include +#include /* local includes */ #include -#include -#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) +/*************** + ** Utilities ** + ***************/ + +template +static void _destroy_closed_links(Link_list &closed_links, + Deallocator &dealloc) { - switch (tlp) { + while (Link *link = closed_links.first()) { + closed_links.remove(link); + destroy(dealloc, static_cast(link)); + } +} + + +template +static void _destroy_links(Link_side_tree &links, + Link_list &closed_links, + Deallocator &dealloc) +{ + _destroy_closed_links(closed_links, dealloc); + while (Link_side *link_side = links.first()) { + Link &link = link_side->link(); + link.dissolve(); + destroy(dealloc, static_cast(&link)); + } +} + + +static void _link_packet(uint8_t const prot, + void *const prot_base, + Link &link, + bool const client) +{ + switch (prot) { case Tcp_packet::IP_ID: - ((Tcp_packet *)ptr)->update_checksum(src, dst, size); + if (client) { + static_cast(&link)->client_packet(*(Tcp_packet *)(prot_base)); + return; + } else { + static_cast(&link)->server_packet(*(Tcp_packet *)(prot_base)); + return; + } + case Udp_packet::IP_ID: + static_cast(&link)->packet(); + return; + default: throw Interface::Bad_transport_protocol(); } +} + + +static void _update_checksum(uint8_t const prot, + void *const prot_base, + size_t const prot_size, + Ipv4_address const src, + Ipv4_address const dst) +{ + switch (prot) { + case Tcp_packet::IP_ID: + ((Tcp_packet *)prot_base)->update_checksum(src, dst, prot_size); return; case Udp_packet::IP_ID: - ((Udp_packet *)ptr)->update_checksum(src, dst); + ((Udp_packet *)prot_base)->update_checksum(src, dst); return; - default: error("unknown transport protocol"); } + default: throw Interface::Bad_transport_protocol(); } } -static uint16_t tlp_dst_port(uint8_t tlp, void *ptr) +static uint16_t _dst_port(uint8_t const prot, + void *const prot_base) { - 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; + switch (prot) { + case Tcp_packet::IP_ID: return (*(Tcp_packet *)prot_base).dst_port(); + case Udp_packet::IP_ID: return (*(Udp_packet *)prot_base).dst_port(); + default: throw Interface::Bad_transport_protocol(); } } -static void tlp_dst_port(uint8_t tlp, void *ptr, uint16_t port) +static void _dst_port(uint8_t const prot, + void *const prot_base, + uint16_t const 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"); } + switch (prot) { + case Tcp_packet::IP_ID: (*(Tcp_packet *)prot_base).dst_port(port); return; + case Udp_packet::IP_ID: (*(Udp_packet *)prot_base).dst_port(port); return; + default: throw Interface::Bad_transport_protocol(); } } -static uint16_t tlp_src_port(uint8_t tlp, void *ptr) +static uint16_t _src_port(uint8_t const prot, + void *const prot_base) { - 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; + switch (prot) { + case Tcp_packet::IP_ID: return (*(Tcp_packet *)prot_base).src_port(); + case Udp_packet::IP_ID: return (*(Udp_packet *)prot_base).src_port(); + default: throw Interface::Bad_transport_protocol(); } } -static void *tlp_packet(uint8_t tlp, Ipv4_packet *ip, size_t size) +static void _src_port(uint8_t const prot, + void *const prot_base, + uint16_t const port) { - 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; + switch (prot) { + case Tcp_packet::IP_ID: ((Tcp_packet *)prot_base)->src_port(port); return; + case Udp_packet::IP_ID: ((Udp_packet *)prot_base)->src_port(port); return; + default: throw Interface::Bad_transport_protocol(); } } -static Port_route_tree *tlp_port_tree(uint8_t tlp, Ip_route *route) +static void *_prot_base(uint8_t const prot, + size_t const prot_size, + Ipv4_packet &ip) { - 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; + switch (prot) { + case Tcp_packet::IP_ID: return new (ip.data()) Tcp_packet(prot_size); + case Udp_packet::IP_ID: return new (ip.data()) Udp_packet(prot_size); + default: throw Interface::Bad_transport_protocol(); } } -static Port_route_list *tlp_port_list(uint8_t tlp, Ip_route *route) +/*************** + ** Interface ** + ***************/ + +void Interface::_pass_ip(Ethernet_frame ð, + size_t const eth_size, + Ipv4_packet &ip, + uint8_t const prot, + void *const prot_base, + size_t const prot_size) { - 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; + _update_checksum(prot, prot_base, prot_size, ip.src(), ip.dst()); + ip.checksum(Ipv4_packet::calculate_checksum(ip)); + _send(eth, eth_size); } -void Interface::_tlp_apply_port_proxy(uint8_t tlp, void *ptr, Ipv4_packet *ip, - Ipv4_address client_ip, - uint16_t client_port) +Forward_rule_tree &Interface::_forward_rules(uint8_t const prot) const { - switch (tlp) { + switch (prot) { + case Tcp_packet::IP_ID: return _domain.tcp_forward_rules(); + case Udp_packet::IP_ID: return _domain.udp_forward_rules(); + default: throw Bad_transport_protocol(); } +} + + +Transport_rule_list &Interface::_transport_rules(uint8_t const prot) const +{ + switch (prot) { + case Tcp_packet::IP_ID: return _domain.tcp_rules(); + case Udp_packet::IP_ID: return _domain.udp_rules(); + default: throw Bad_transport_protocol(); } +} + + +void +Interface::_new_link(uint8_t const protocol, + Link_side_id const &local, + Pointer const remote_port_alloc, + Interface &remote_interface, + Link_side_id const &remote) +{ + switch (protocol) { 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()); + Tcp_link &link = *new (_alloc) + Tcp_link(*this, local, remote_port_alloc, remote_interface, + remote, _timer, _config(), protocol); + _tcp_links.insert(&link.client()); + remote_interface._tcp_links.insert(&link.server()); + if (_config().verbose()) { + log("New TCP client link: ", link.client(), " at ", *this); + log("New TCP server link: ", link.server(), + " at ", remote_interface._domain); + } 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()); + Udp_link &link = *new (_alloc) + Udp_link(*this, local, remote_port_alloc, remote_interface, + remote, _timer, _config(), protocol); + _udp_links.insert(&link.client()); + remote_interface._udp_links.insert(&link.server()); + if (_config().verbose()) { + log("New UDP client link: ", link.client(), " at ", *this); + log("New UDP server link: ", link.server(), + " at ", remote_interface._domain); + } return; } - default: error("unknown transport protocol"); } + default: throw Bad_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) +Link_side_tree &Interface::_links(uint8_t const protocol) { - 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; + switch (protocol) { + case Tcp_packet::IP_ID: return _tcp_links; + case Udp_packet::IP_ID: return _udp_links; + default: throw Bad_transport_protocol(); } } -void Interface::_delete_tcp_proxy(Tcp_proxy * const proxy) +void Interface::link_closed(Link &link, uint8_t const prot) { - _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); } + _closed_links(prot).insert(&link); } -void Interface::_delete_udp_proxy(Udp_proxy * const proxy) +void Interface::dissolve_link(Link_side &link_side, uint8_t const prot) { - _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); } + _links(prot).remove(&link_side); } -Tcp_proxy *Interface::_new_tcp_proxy(unsigned const client_port, - Ipv4_address client_ip, - Ipv4_address proxy_ip) +Link_list &Interface::_closed_links(uint8_t const protocol) { - 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; + switch (protocol) { + case Tcp_packet::IP_ID: return _closed_tcp_links; + case Udp_packet::IP_ID: return _closed_udp_links; + default: throw Bad_transport_protocol(); } } -Udp_proxy *Interface::_new_udp_proxy(unsigned const client_port, - Ipv4_address client_ip, - Ipv4_address proxy_ip) +void Interface::_adapt_eth(Ethernet_frame ð, + size_t const eth_size, + Ipv4_address const &ip, + Packet_descriptor const &pkt, + Interface &interface) { - 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; - } - } + Ipv4_address const &hop_ip = interface._domain.next_hop(ip); + try { eth.dst(interface._arp_cache.find_by_ip(hop_ip).mac()); } + catch (Arp_cache::No_match) { + interface._broadcast_arp_request(hop_ip); + new (_alloc) Arp_waiter(*this, interface, hop_ip, pkt); + throw Packet_postponed(); } + eth.src(_router_mac); +} - /* ... 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 */ +void Interface::_nat_link_and_pass(Ethernet_frame ð, + size_t const eth_size, + Ipv4_packet &ip, + uint8_t const prot, + void *const prot_base, + size_t const prot_size, + Link_side_id const &local, + Interface &interface) +{ + Pointer remote_port_alloc; try { - Arp_cache_entry &arp_entry = _arp_cache.find_by_ip_addr(via); - eth->dst(arp_entry.mac_addr().addr); + Nat_rule &nat = interface._domain.nat_rules().find_by_domain(_domain); + if(_config().verbose()) { + log("Using NAT rule: ", nat); } - } catch (Arp_cache::No_matching_entry) { + _src_port(prot, prot_base, nat.port_alloc(prot).alloc()); + ip.src(interface._router_ip()); + remote_port_alloc.set(nat.port_alloc(prot)); + } + catch (Nat_rule_tree::No_match) { } + Link_side_id const remote = { ip.dst(), _dst_port(prot, prot_base), + ip.src(), _src_port(prot, prot_base) }; + _new_link(prot, local, remote_port_alloc, interface, remote); + interface._pass_ip(eth, eth_size, ip, prot, prot_base, prot_size); +} - interface->arp_broadcast(via); - _arp_waiters.insert(new (_allocator) Arp_waiter(*this, via, *eth, - eth_size, *packet)); - ack_packet = false; +void Interface::_handle_ip(Ethernet_frame ð, + Genode::size_t const eth_size, + Packet_descriptor const &pkt) +{ + _destroy_closed_links(_closed_udp_links, _alloc); + _destroy_closed_links(_closed_tcp_links, _alloc); + + /* read packet information */ + Ipv4_packet &ip = *new (eth.data()) + Ipv4_packet(eth_size - sizeof(Ethernet_frame)); + + uint8_t const prot = ip.protocol(); + size_t const prot_size = ip.total_length() - ip.header_length() * 4; + void *const prot_base = _prot_base(prot, prot_size, ip); + Link_side_id const local = { ip.src(), _src_port(prot, prot_base), + ip.dst(), _dst_port(prot, prot_base) }; + + /* try to route via existing UDP/TCP links */ + try { + Link_side const &local_side = _links(prot).find_by_id(local); + Link &link = local_side.link(); + bool const client = local_side.is_client(); + Link_side &remote_side = client ? link.server() : link.client(); + Interface &interface = remote_side.interface(); + if(_config().verbose()) { + log("Using ", protocol_name(prot), " link: ", link); } + + _adapt_eth(eth, eth_size, remote_side.src_ip(), pkt, interface); + ip.src(remote_side.dst_ip()); + ip.dst(remote_side.src_ip()); + _src_port(prot, prot_base, remote_side.dst_port()); + _dst_port(prot, prot_base, remote_side.src_port()); + + interface._pass_ip(eth, eth_size, ip, prot, prot_base, prot_size); + _link_packet(prot, prot_base, link, client); return; } - /* adapt packet to the collected info */ - eth->src(interface->router_mac()); - ip->dst(to); - tlp_dst_port(tlp, tlp_ptr, dst_port); + catch (Link_side_tree::No_match) { } - /* if configured, use proxy source IP */ - if (_proxy) { - Ipv4_address client_ip = ip->src(); - ip->src(interface->router_ip()); + /* try to route via forward rules */ + if (local.dst_ip == _router_ip()) { + try { + Forward_rule const &rule = + _forward_rules(prot).find_by_port(local.dst_port); - /* 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; } + Interface &interface = rule.domain().interface().deref(); + if(_config().verbose()) { + log("Using forward rule: ", protocol_name(prot), " ", rule); } + + _adapt_eth(eth, eth_size, rule.to(), pkt, interface); + ip.dst(rule.to()); + _nat_link_and_pass(eth, eth_size, ip, prot, prot_base, prot_size, + local, interface); + return; } - if (!dst_route) { - _tlp_apply_port_proxy(tlp, tlp_ptr, ip, client_ip, src_port); } + catch (Forward_rule_tree::No_match) { } + catch (Pointer::Invalid) { } } - /* 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); + /* try to route via transport and permit rules */ + try { + Transport_rule const &transport_rule = + _transport_rules(prot).longest_prefix_match(local.dst_ip); + + Permit_rule const &permit_rule = + transport_rule.permit_rule(local.dst_port); + + Interface &interface = permit_rule.domain().interface().deref(); + if(_config().verbose()) { + log("Using ", protocol_name(prot), " rule: ", transport_rule, + " ", permit_rule); } + + _adapt_eth(eth, eth_size, local.dst_ip, pkt, interface); + _nat_link_and_pass(eth, eth_size, ip, prot, prot_base, prot_size, + local, interface); + return; + } + catch (Transport_rule_list::No_match) { } + catch (Permit_single_rule_tree::No_match) { } + catch (Pointer::Invalid) { } + + /* try to route via IP rules */ + try { + Ip_rule const &rule = + _domain.ip_rules().longest_prefix_match(local.dst_ip); + + Interface &interface = rule.domain().interface().deref(); + if(_config().verbose()) { + log("Using IP rule: ", rule); } + + _adapt_eth(eth, eth_size, local.dst_ip, pkt, interface); + interface._pass_ip(eth, eth_size, ip, prot, prot_base, prot_size); + return; + } + catch (Ip_rule_list::No_match) { } + catch (Pointer::Invalid) { } + + /* give up and drop packet */ + if (_config().verbose()) { + log("Unroutable packet"); } } -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) +void Interface::_broadcast_arp_request(Ipv4_address const &ip) { 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(); + 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)); + Arp_packet &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); + _send(eth_arp, sizeof(eth_arp)); } -void Interface::_remove_arp_waiter(Arp_waiter *arp_waiter) -{ - _arp_waiters.remove(arp_waiter); - destroy(arp_waiter->interface().allocator(), arp_waiter); -} - - -Arp_waiter *Interface::_new_arp_entry(Arp_waiter *arp_waiter, - Arp_cache_entry *arp_entry) -{ - Arp_waiter *next_arp_waiter = arp_waiter->next(); - if (arp_waiter->new_arp_cache_entry(*arp_entry)) { - _remove_arp_waiter(arp_waiter); } - - return next_arp_waiter; -} - - -void Interface::_handle_arp_reply(Arp_packet * const arp) +void Interface::_handle_arp_reply(Arp_packet &arp) { + /* do nothing if ARP info already exists */ try { - _arp_cache.find_by_ip_addr(arp->src_ip()); - if (_verbose) { + _arp_cache.find_by_ip(arp.src_ip()); + if (_config().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; + } + /* create cache entry and continue handling of matching packets */ + catch (Arp_cache::No_match) { + Ipv4_address const ip = arp.src_ip(); + _arp_cache.new_entry(ip, arp.src_mac()); + for (Arp_waiter_list_element *waiter_le = _foreign_arp_waiters.first(); + waiter_le; ) + { + Arp_waiter &waiter = *waiter_le->object(); + waiter_le = waiter_le->next(); + if (ip != waiter.ip()) { continue; } + waiter.src()._continue_handle_eth(waiter.packet()); + destroy(waiter.src()._alloc, &waiter); } } } -void Interface::_handle_arp_request(Ethernet_frame * const eth, - size_t const eth_size, - Arp_packet * const arp) +Ipv4_address const &Interface::_router_ip() const +{ + return _domain.interface_attr().address; +} + + +void Interface::_handle_arp_request(Ethernet_frame ð, + size_t const eth_size, + Arp_packet &arp) { /* ignore packets that do not target the router */ - if (arp->dst_ip() != router_ip()) { - if (_verbose) { - log("ARP does not target router"); } + if (arp.dst_ip() != _router_ip()) { + if (_config().verbose()) { + log("ARP request for unknown IP"); } 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()); + 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); + arp.opcode(Arp_packet::REPLY); + _send(eth, eth_size); } -void Interface::_handle_arp(Ethernet_frame *eth, size_t eth_size) +void Interface::_handle_arp(Ethernet_frame ð, size_t const 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()) { + size_t const arp_size = eth_size - sizeof(Ethernet_frame); + Arp_packet &arp = *new (eth.data()) Arp_packet(arp_size); + if (!arp.ethernet_ipv4()) { + error("ARP for unknown protocol"); } + + 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"); } } + default: error("unknown ARP operation"); } } -void Interface::_ready_to_submit(unsigned) +void Interface::_ready_to_submit() { - while (sink()->packet_avail()) { + while (_sink().packet_avail()) { - _packet = sink()->get_packet(); - if (!_packet.size()) { + Packet_descriptor const pkt = _sink().get_packet(); + if (!pkt.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); + try { _handle_eth(_sink().packet_content(pkt), pkt.size(), pkt); } + catch (Packet_postponed) { continue; } + _ack_packet(pkt); } } -void Interface::continue_handle_ethernet(void *src, Genode::size_t size, - Packet_descriptor *packet) +void Interface::_continue_handle_eth(Packet_descriptor const &pkt) { - 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); + try { _handle_eth(_sink().packet_content(pkt), pkt.size(), pkt); } + catch (Packet_postponed) { error("failed twice to handle packet"); } + _ack_packet(pkt); } -void Interface::_ready_to_ack(unsigned) +void Interface::_ready_to_ack() { - while (source()->ack_avail()) { - source()->release_packet(source()->get_acked_packet()); } + 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) +void Interface::_handle_eth(void *const eth_base, + size_t const eth_size, + Packet_descriptor const &pkt) { try { - Ethernet_frame * const eth = new (src) Ethernet_frame(size); + Ethernet_frame * const eth = new (eth_base) Ethernet_frame(eth_size); + if (_config().verbose()) { + log("at ", _domain, " handle ", *eth); } + switch (eth->type()) { - case Ethernet_frame::ARP: _handle_arp(eth, size); break; - case Ethernet_frame::IPV4: _handle_ip(eth, size, ack, packet); break; - default: ; } + case Ethernet_frame::ARP: _handle_arp(*eth, eth_size); break; + case Ethernet_frame::IPV4: _handle_ip(*eth, eth_size, pkt); break; + default: throw Bad_network_protocol(); } } catch (Ethernet_frame::No_ethernet_frame) { - error("Invalid ethernet frame at ", label()); } + error("invalid ethernet frame"); } - catch (Too_many_tcp_proxies) { - error("Too many TCP NAT links requested by ", label()); } + catch (Interface::Bad_transport_protocol) { + error("unknown transport layer protocol"); } + + catch (Interface::Bad_network_protocol) { + error("unknown network layer protocol"); } + + catch (Ipv4_packet::No_ip_packet) { + error("invalid IP packet"); } + + catch (Port_allocator_guard::Out_of_indices) { + error("no available NAT ports"); } + + catch (Domain::No_next_hop) { + error("can not find next hop"); } } -void Interface::send(Ethernet_frame *eth, Genode::size_t size) +void Interface::_send(Ethernet_frame ð, Genode::size_t const size) { - if (_verbose) { - Genode::printf(">> %s ", Interface::string()); - dump_eth(eth, size); - Genode::printf("\n"); - } + if (_config().verbose()) { + log("at ", _domain, " send ", eth); } 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) { + Packet_descriptor const pkt = _source().alloc_packet(size); + char *content = _source().packet_content(pkt); + Genode::memcpy((void *)content, (void *)ð, size); + _source().submit_packet(pkt); + } + catch (Packet_stream_source::Packet_alloc_failed) { + if (_config().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) +Interface::Interface(Entrypoint &ep, + Genode::Timer &timer, + Mac_address const router_mac, + Genode::Allocator &alloc, + Mac_address const mac, + Domain &domain) : - 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) + _source_submit(ep, *this, &Interface::_packet_avail), + _router_mac(router_mac), _mac(mac), _timer(timer), _alloc(alloc), + _domain(domain) { - 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), "\""); + if (_config().verbose()) { + log("Interface connected ", *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"); - } + log(" Router identity: MAC ", _router_mac, " IP ", + _router_ip(), "/", _domain.interface_attr().prefix); } - try { - Xml_node route = _policy.sub_node("ip"); - for (; ; route = route.next("ip")) { _read_route(route); } + _domain.interface().set(*this); +} - } catch (Xml_node::Nonexistent_sub_node) { } - _interface_tree.insert(this); + +void Interface::_ack_packet(Packet_descriptor const &pkt) +{ + if (!_sink().ready_to_ack()) { + error("ack state FULL"); + return; + } + _sink().acknowledge_packet(pkt); +} + + +void Interface::_cancel_arp_waiting(Arp_waiter &waiter) +{ + warning("waiting for ARP cancelled"); + _ack_packet(waiter.packet()); + destroy(_alloc, &waiter); } Interface::~Interface() { - /* make interface unfindable */ - _interface_tree.remove(this); + _domain.interface().unset(); + if (_config().verbose()) { + log("Interface disconnected ", *this); } - /* delete all ARP requests of this interface */ - Arp_waiter *arp_waiter = _arp_waiters.first(); - while (arp_waiter) { + /* destroy ARP waiters */ + while (_own_arp_waiters.first()) { + _cancel_arp_waiting(*_foreign_arp_waiters.first()->object()); } - Arp_waiter *next_arp_waiter = arp_waiter->next(); - if (&arp_waiter->interface() != this) { - _remove_arp_waiter(arp_waiter); } + while (_foreign_arp_waiters.first()) { + Arp_waiter &waiter = *_foreign_arp_waiters.first()->object(); + waiter.src()._cancel_arp_waiting(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; - } + /* destroy links */ + _destroy_links(_tcp_links, _closed_tcp_links, _alloc); + _destroy_links(_udp_links, _closed_udp_links, _alloc); } -Interface *Interface_tree::find_by_label(char const *label) +Configuration &Interface::_config() const { return _domain.config(); } + + +void Interface::print(Output &output) const { - if (!strcmp(label, "")) { - return nullptr; } - - Interface *interface = static_cast(first()); - if (!interface) { - return nullptr; } - - interface = static_cast(interface->find_by_name(label)); - return interface; + Genode::print(output, "\"", _domain.name(), "\""); } diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index 22ba8cd8e..a853c30a3 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -14,192 +14,168 @@ #ifndef _INTERFACE_H_ #define _INTERFACE_H_ -/* Genode includes */ -#include -#include -#include -#include - /* local includes */ -#include +#include +#include +#include + +/* Genode includes */ +#include namespace Net { - using ::Nic::Packet_stream_sink; - using ::Nic::Packet_stream_source; - using ::Nic::Packet_descriptor; - + using Packet_descriptor = ::Nic::Packet_descriptor; + using Packet_stream_sink = ::Nic::Packet_stream_sink< ::Nic::Session::Policy>; + using Packet_stream_source = ::Nic::Packet_stream_source< ::Nic::Session::Policy>; + class Forward_rule_tree; + class Transport_rule_list; class Ethernet_frame; class Arp_packet; - class Arp_waiter; - class Arp_cache; - class Arp_cache_entry; - class Port_allocator; - class Tcp_proxy; - class Udp_proxy; class Interface; - class Interface_tree; - - using Interface_list = Genode::List; - 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 Configuration; + class Domain; } -class Net::Interface : public Genode::Session_label, public Genode::Avl_string_base +class Net::Interface { protected: - Signal_rpc_member _sink_ack; - Signal_rpc_member _sink_submit; - Signal_rpc_member _source_ack; - Signal_rpc_member _source_submit; + using Signal_handler = Genode::Signal_handler; + + Signal_handler _sink_ack; + Signal_handler _sink_submit; + Signal_handler _source_ack; + Signal_handler _source_submit; + Mac_address const _router_mac; + Mac_address const _mac; private: - Packet_descriptor _packet; - Genode::Entrypoint &_ep; - Ip_route_list _ip_routes; - Mac_address const _router_mac; - Ipv4_address const _router_ip; - Mac_address const _mac; - Genode::Allocator &_allocator; - Genode::Session_policy _policy; - bool const _proxy; - unsigned _tcp_proxy; - unsigned _tcp_proxy_used; - Tcp_proxy_list &_tcp_proxies; - Port_allocator &_tcp_port_alloc; - unsigned _udp_proxy; - unsigned _udp_proxy_used; - Udp_proxy_list &_udp_proxies; - Port_allocator &_udp_port_alloc; - unsigned const _rtt_sec; - Interface_tree &_interface_tree; - Arp_cache &_arp_cache; - Arp_waiter_list &_arp_waiters; - bool _verbose; + Genode::Timer &_timer; + Genode::Allocator &_alloc; + Domain &_domain; + Arp_cache _arp_cache; + Arp_waiter_list _own_arp_waiters; + Arp_waiter_list _foreign_arp_waiters; + Link_side_tree _tcp_links; + Link_side_tree _udp_links; + Link_list _closed_tcp_links; + Link_list _closed_udp_links; - void _read_route(Genode::Xml_node &route_xn); + void _new_link(Genode::uint8_t const protocol, + Link_side_id const &local_id, + Pointer const remote_port_alloc, + Interface &remote_interface, + Link_side_id const &remote_id); - Tcp_proxy *_find_tcp_proxy_by_client(Ipv4_address ip, - Genode::uint16_t port); + Forward_rule_tree &_forward_rules(Genode::uint8_t const prot) const; - Udp_proxy *_find_udp_proxy_by_client(Ipv4_address ip, - Genode::uint16_t port); + Transport_rule_list &_transport_rules(Genode::uint8_t const prot) const; - Interface *_tlp_proxy_route(Genode::uint8_t tlp, void *ptr, - Genode::uint16_t &dst_port, - Ipv4_packet *ip, Ipv4_address &to, - Ipv4_address &via); + void _handle_arp(Ethernet_frame ð, Genode::size_t const eth_size); - void _tlp_apply_port_proxy(Genode::uint8_t tlp, void *tlp_ptr, - Ipv4_packet *ip, Ipv4_address client_ip, - Genode::uint16_t src_port); + void _handle_arp_reply(Arp_packet &arp); - Tcp_proxy *_find_tcp_proxy_by_proxy(Ipv4_address ip, - Genode::uint16_t port); + void _handle_arp_request(Ethernet_frame ð, + Genode::size_t const eth_size, + Arp_packet &arp); - Udp_proxy *_find_udp_proxy_by_proxy(Ipv4_address ip, - Genode::uint16_t port); + void _handle_ip(Ethernet_frame ð, + Genode::size_t const eth_size, + Packet_descriptor const &pkt); - void _handle_arp_reply(Arp_packet * const arp); + void _adapt_eth(Ethernet_frame ð, + Genode::size_t const eth_size, + Ipv4_address const &ip, + Packet_descriptor const &pkt, + Interface &interface); - void _handle_arp_request(Ethernet_frame * const eth, - Genode::size_t const eth_size, - Arp_packet * const arp); + void _nat_link_and_pass(Ethernet_frame ð, + Genode::size_t const eth_size, + Ipv4_packet &ip, + Genode::uint8_t const prot, + void *const prot_base, + Genode::size_t const prot_size, + Link_side_id const &local_id, + Interface &interface); - Arp_waiter *_new_arp_entry(Arp_waiter *arp_waiter, - Arp_cache_entry *entry); + void _broadcast_arp_request(Ipv4_address const &ip); - void _remove_arp_waiter(Arp_waiter *arp_waiter); + void _send(Ethernet_frame ð, Genode::size_t const eth_size); - void _handle_arp(Ethernet_frame *eth, Genode::size_t size); + void _pass_ip(Ethernet_frame ð, + Genode::size_t const eth_size, + Ipv4_packet &ip, + Genode::uint8_t const prot, + void *const prot_base, + Genode::size_t const prot_size); - void _handle_ip(Ethernet_frame *eth, Genode::size_t eth_size, - bool &ack_packet, Packet_descriptor *packet); + void _continue_handle_eth(Packet_descriptor const &pkt); - Tcp_proxy *_new_tcp_proxy(unsigned const client_port, - Ipv4_address client_ip, - Ipv4_address proxy_ip); + Link_list &_closed_links(Genode::uint8_t const protocol); - Udp_proxy *_new_udp_proxy(unsigned const client_port, - Ipv4_address client_ip, - Ipv4_address proxy_ip); + Link_side_tree &_links(Genode::uint8_t const protocol); - void _delete_tcp_proxy(Tcp_proxy * const proxy); + Configuration &_config() const; - bool _chk_delete_tcp_proxy(Tcp_proxy * &proxy); + Ipv4_address const &_router_ip() const; - void _delete_udp_proxy(Udp_proxy * const proxy); + void _handle_eth(void *const eth_base, + Genode::size_t const eth_size, + Packet_descriptor const &pkt); - bool _chk_delete_udp_proxy(Udp_proxy * &proxy); + void _ack_packet(Packet_descriptor const &pkt); + + void _cancel_arp_waiting(Arp_waiter &waiter); + + virtual Packet_stream_sink &_sink() = 0; + + virtual Packet_stream_source &_source() = 0; /*********************************** ** Packet-stream signal handlers ** ***********************************/ - void _ready_to_submit(unsigned); - void _ack_avail(unsigned) { } - void _ready_to_ack(unsigned); - void _packet_avail(unsigned) { } + void _ready_to_submit(); + void _ack_avail() { } + void _ready_to_ack(); + void _packet_avail() { } public: - struct Too_many_tcp_proxies : Genode::Exception { }; - struct Too_many_udp_proxies : Genode::Exception { }; + struct Bad_transport_protocol : Genode::Exception { }; + struct Bad_network_protocol : Genode::Exception { }; + struct Packet_postponed : Genode::Exception { }; - Interface(Server::Entrypoint &ep, - Mac_address const router_mac, - Ipv4_address const router_ip, - Genode::Allocator &allocator, - char const *args, - Port_allocator &tcp_port_alloc, - Port_allocator &udp_port_alloc, - Mac_address const mac, - Tcp_proxy_list &tcp_proxies, - Udp_proxy_list &udp_proxies, - unsigned const rtt_sec, - Interface_tree &interface_tree, - Arp_cache &arp_cache, - Arp_waiter_list &arp_waiters, - bool verbose); + Interface(Genode::Entrypoint &ep, + Genode::Timer &timer, + Mac_address const router_mac, + Genode::Allocator &alloc, + Mac_address const mac, + Domain &domain); ~Interface(); - void arp_broadcast(Ipv4_address ip_addr); + void link_closed(Link &link, Genode::uint8_t const prot); - void send(Ethernet_frame *eth, Genode::size_t eth_size); + void dissolve_link(Link_side &link_side, Genode::uint8_t const prot); - void handle_ethernet(void *src, Genode::size_t size, bool &ack, - Packet_descriptor *packet); - void continue_handle_ethernet(void *src, Genode::size_t size, - Packet_descriptor *packet); + /********* + ** log ** + *********/ + + void print(Genode::Output &output) const; /*************** ** Accessors ** ***************/ - Mac_address router_mac() const { return _router_mac; } - Mac_address mac() const { return _mac; } - Ipv4_address router_ip() const { return _router_ip; } - Ip_route_list &ip_routes() { return _ip_routes; } - Genode::Allocator &allocator() const { return _allocator; } - Session_label &label() { return *this; } - - virtual Packet_stream_sink< ::Nic::Session::Policy> *sink() = 0; - virtual Packet_stream_source< ::Nic::Session::Policy> *source() = 0; -}; - - -struct Net::Interface_tree : Genode::Avl_tree -{ - Interface *find_by_label(char const *label); + Arp_waiter_list &own_arp_waiters() { return _own_arp_waiters; } + Arp_waiter_list &foreign_arp_waiters() { return _foreign_arp_waiters; } }; #endif /* _INTERFACE_H_ */ diff --git a/repos/os/src/server/nic_router/ip_route.cc b/repos/os/src/server/nic_router/ip_route.cc deleted file mode 100644 index 8eda1b85e..000000000 --- a/repos/os/src/server/nic_router/ip_route.cc +++ /dev/null @@ -1,141 +0,0 @@ -/* - * \brief IP routing entry - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#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 deleted file mode 100644 index b148a91b0..000000000 --- a/repos/os/src/server/nic_router/ip_route.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * \brief IP routing entry - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* local includes */ -#include - -#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/ip_rule.cc b/repos/os/src/server/nic_router/ip_rule.cc new file mode 100644 index 000000000..2cd5708ab --- /dev/null +++ b/repos/os/src/server/nic_router/ip_rule.cc @@ -0,0 +1,25 @@ +/* + * \brief IP routing entry + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include + +using namespace Net; +using namespace Genode; + + +Ip_rule::Ip_rule(Domain_tree &domains, Xml_node const &node) +: + Leaf_rule(domains, node), + Direct_rule(node) +{ } diff --git a/repos/os/src/server/nic_router/ip_rule.h b/repos/os/src/server/nic_router/ip_rule.h new file mode 100644 index 000000000..f9303e660 --- /dev/null +++ b/repos/os/src/server/nic_router/ip_rule.h @@ -0,0 +1,35 @@ +/* + * \brief IP routing entry + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _IP_RULE_H_ +#define _IP_RULE_H_ + +/* local includes */ +#include +#include + +namespace Net { + + class Ip_rule; + struct Ip_rule_list : Direct_rule_list { }; +} + + +struct Net::Ip_rule : Leaf_rule, Direct_rule +{ + public: + + Ip_rule(Domain_tree &domains, Genode::Xml_node const &node); +}; + +#endif /* _IP_RULE_H_ */ diff --git a/repos/os/src/server/nic_router/leaf_rule.cc b/repos/os/src/server/nic_router/leaf_rule.cc new file mode 100644 index 000000000..7e918ec7f --- /dev/null +++ b/repos/os/src/server/nic_router/leaf_rule.cc @@ -0,0 +1,37 @@ +/* + * \brief Routing rule that defines a target interface + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include +#include + +using namespace Net; +using namespace Genode; + + +Domain &Leaf_rule::_find_domain(Domain_tree &domains, + Xml_node const &node) +{ + try { + return domains.find_by_name( + Cstring(node.attribute("domain").value_base(), + node.attribute("domain").value_size())); + } + catch (Domain_tree::No_match) { throw Invalid(); } +} + + +Leaf_rule::Leaf_rule(Domain_tree &domains, Xml_node const &node) +: + _domain(_find_domain(domains, node)) +{ } diff --git a/repos/os/src/server/nic_router/leaf_rule.h b/repos/os/src/server/nic_router/leaf_rule.h new file mode 100644 index 000000000..1e4b0f814 --- /dev/null +++ b/repos/os/src/server/nic_router/leaf_rule.h @@ -0,0 +1,50 @@ +/* + * \brief Routing rule that defines a target interface + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LEAF_RULE_H_ +#define _LEAF_RULE_H_ + +/* local includes */ +#include + +namespace Genode { class Xml_node; } + +namespace Net { + + class Domain; + class Domain_tree; + class Leaf_rule; +} + +class Net::Leaf_rule : public Rule +{ + protected: + + Domain &_domain; + + static Domain &_find_domain(Domain_tree &domains, + Genode::Xml_node const &node); + + public: + + Leaf_rule(Domain_tree &domains, Genode::Xml_node const &node); + + + /*************** + ** Accessors ** + ***************/ + + Domain &domain() const { return _domain; } +}; + +#endif /* _LEAF_RULE_H_ */ diff --git a/repos/os/src/server/nic_router/link.cc b/repos/os/src/server/nic_router/link.cc new file mode 100644 index 000000000..861b28e20 --- /dev/null +++ b/repos/os/src/server/nic_router/link.cc @@ -0,0 +1,237 @@ +/* + * \brief State tracking for UDP/TCP connections + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include +#include +#include +#include + +using namespace Net; +using namespace Genode; + + +/****************** + ** Link_side_id ** + ******************/ + +constexpr size_t Link_side_id::data_size() +{ + return sizeof(src_ip) + sizeof(src_port) + + sizeof(dst_ip) + sizeof(dst_port); +} + + +bool Link_side_id::operator == (Link_side_id const &id) const +{ + return memcmp(id.data, data, data_size()) == 0; +} + + +bool Link_side_id::operator > (Link_side_id const &id) const +{ + return memcmp(id.data, data, data_size()) > 0; +} + + +/*************** + ** Link_side ** + ***************/ + +Link_side::Link_side(Interface &interface, + Link_side_id const &id, + Link &link) +: + _interface(interface), _id(id), _link(link) +{ } + + +Link_side const &Link_side::find_by_id(Link_side_id const &id) const +{ + if (id == _id) { + return *this; } + + bool const side = id > _id; + Link_side *const link_side = Avl_node::child(side); + if (!link_side) { + throw Link_side_tree::No_match(); } + + return link_side->find_by_id(id); +} + + +void Link_side::print(Output &output) const +{ + Genode::print(output, "src ", src_ip(), ":", src_port(), + " dst ", dst_ip(), ":", dst_port()); +} + + +bool Link_side::is_client() const +{ + return this == &_link.client(); +} + + +/******************** + ** Link_side_tree ** + ********************/ + +Link_side const &Link_side_tree::find_by_id(Link_side_id const &id) const +{ + Link_side *const link_side = first(); + if (!link_side) { + throw No_match(); } + + return link_side->find_by_id(id); +} + + +/********** + ** Link ** + **********/ + +void Link::print(Output &output) const +{ + Genode::print(output, "CLN ", _client, " SRV ", _server); +} + + +Link::Link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer const srv_port_alloc, + Interface &srv_interface, + Link_side_id const &srv_id, + Genode::Timer &timer, + Configuration &config, + uint8_t const protocol) +: + _config(config), + _client(cln_interface, cln_id, *this), + _server_port_alloc(srv_port_alloc), + _server(srv_interface, srv_id, *this), + _close_timeout(timer, *this, &Link::_handle_close_timeout), + _close_timeout_us(_config.rtt_sec() * 2 * 1000 * 1000), + _protocol(protocol) +{ + _close_timeout.start(_close_timeout_us); +} + + +void Link::_handle_close_timeout(Genode::Timer::Microseconds) +{ + dissolve(); + _client._interface.link_closed(*this, _protocol); +} + + +void Link::dissolve() +{ + _client._interface.dissolve_link(_client, _protocol); + _server._interface.dissolve_link(_server, _protocol); + if (_config.verbose()) { + log("Dissolve ", protocol_name(_protocol), " link: ", *this); } + + try { + _server_port_alloc.deref().free(_server.dst_port()); + if (_config.verbose()) { + log("Free ", protocol_name(_protocol), + " port ", _server.dst_port(), + " at ", _server.interface(), + " that was used by ", _client.interface()); + } + } + catch (Pointer::Invalid) { } +} + + +/************** + ** Tcp_link ** + **************/ + +Tcp_link::Tcp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer const srv_port_alloc, + Interface &srv_interface, + Link_side_id const &srv_id, + Genode::Timer &timer, + Configuration &config, + uint8_t const protocol) +: + Link(cln_interface, cln_id, srv_port_alloc, srv_interface, srv_id, timer, + config, protocol) +{ } + + +void Tcp_link::_fin_acked() +{ + if (_server_fin_acked && _client_fin_acked) { + _close_timeout.start(_close_timeout_us); + _closed = true; + } +} + + +void Tcp_link::server_packet(Tcp_packet &tcp) +{ + if (_closed) { + return; } + + if (tcp.fin()) { + _server_fin = true; } + + if (tcp.ack() && _client_fin) { + _client_fin_acked = true; + _fin_acked(); + } + if (!_closed) { + _packet(); } +} + + +void Tcp_link::client_packet(Tcp_packet &tcp) +{ + if (_closed) { + return; } + + if (tcp.fin()) { + _client_fin = true; } + + if (tcp.ack() && _server_fin) { + _server_fin_acked = true; + _fin_acked(); + } + if (!_closed) { + _packet(); } +} + + +/************** + ** Udp_link ** + **************/ + +Udp_link::Udp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer const srv_port_alloc, + Interface &srv_interface, + Link_side_id const &srv_id, + Genode::Timer &timer, + Configuration &config, + uint8_t const protocol) +: + Link(cln_interface, cln_id, srv_port_alloc, srv_interface, srv_id, timer, + config, protocol) +{ } diff --git a/repos/os/src/server/nic_router/link.h b/repos/os/src/server/nic_router/link.h new file mode 100644 index 000000000..30a9a850d --- /dev/null +++ b/repos/os/src/server/nic_router/link.h @@ -0,0 +1,213 @@ +/* + * \brief State tracking for UDP/TCP connections + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LINK_H_ +#define _LINK_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include + +namespace Net { + + class Configuration; + class Port_allocator_guard; + class Tcp_packet; + class Interface; + class Link_side_id; + class Link_side; + class Link_side_tree; + class Link; + struct Link_list : Genode::List { }; + class Tcp_link; + class Udp_link; +} + + +struct Net::Link_side_id +{ + int const data[]; + Ipv4_address const src_ip; + Genode::uint16_t const src_port; + Ipv4_address const dst_ip; + Genode::uint16_t const dst_port; + + static constexpr Genode::size_t data_size(); + + + /************************ + ** Standard operators ** + ************************/ + + bool operator == (Link_side_id const &id) const; + + bool operator > (Link_side_id const &id) const; +} +__attribute__((__packed__)); + + +class Net::Link_side : public Genode::Avl_node +{ + friend class Link; + + private: + + Interface &_interface; + Link_side_id const _id; + Link &_link; + + public: + + Link_side(Interface &interface, + Link_side_id const &id, + Link &link); + + Link_side const &find_by_id(Link_side_id const &id) const; + + bool is_client() const; + + + /************** + ** Avl_node ** + **************/ + + bool higher(Link_side *side) { return side->_id > _id; } + + + /********* + ** Log ** + *********/ + + void print(Genode::Output &output) const; + + + /*************** + ** Accessors ** + ***************/ + + Interface &interface() const { return _interface; } + Link &link() const { return _link; } + Ipv4_address const &src_ip() const { return _id.src_ip; } + Ipv4_address const &dst_ip() const { return _id.dst_ip; } + Genode::uint16_t src_port() const { return _id.src_port; } + Genode::uint16_t dst_port() const { return _id.dst_port; } +}; + + +struct Net::Link_side_tree : Genode::Avl_tree +{ + struct No_match : Genode::Exception { }; + + Link_side const &find_by_id(Link_side_id const &id) const; +}; + + +class Net::Link : public Link_list::Element +{ + protected: + + using Signal_handler = Genode::Signal_handler; + + Configuration &_config; + Link_side _client; + Pointer const _server_port_alloc; + Link_side _server; + Genode::One_shot_timeout _close_timeout; + Genode::Timer::Microseconds const _close_timeout_us; + Genode::uint8_t const _protocol; + + void _handle_close_timeout(Genode::Timer::Microseconds); + + void _packet() { _close_timeout.start(_close_timeout_us); } + + public: + + struct No_port_allocator : Genode::Exception { }; + + Link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer const srv_port_alloc, + Interface &srv_interface, + Link_side_id const &srv_id, + Genode::Timer &timer, + Configuration &config, + Genode::uint8_t const protocol); + + void dissolve(); + + + /********* + ** Log ** + *********/ + + void print(Genode::Output &output) const; + + + /*************** + ** Accessors ** + ***************/ + + Link_side &client() { return _client; } + Link_side &server() { return _server; } +}; + + +class Net::Tcp_link : public Link +{ + private: + + bool _client_fin = false; + bool _server_fin = false; + bool _client_fin_acked = false; + bool _server_fin_acked = false; + bool _closed = false; + + void _fin_acked(); + + public: + + Tcp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer const srv_port_alloc, + Interface &srv_interface, + Link_side_id const &srv_id, + Genode::Timer &timer, + Configuration &config, + Genode::uint8_t const protocol); + + void client_packet(Tcp_packet &tcp); + + void server_packet(Tcp_packet &tcp); +}; + + +struct Net::Udp_link : Link +{ + Udp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer const srv_port_alloc, + Interface &srv_interface, + Link_side_id const &srv_id, + Genode::Timer &timer, + Configuration &config, + Genode::uint8_t const protocol); + + void packet() { _packet(); } +}; + +#endif /* _LINK_H_ */ diff --git a/repos/os/src/server/nic_router/mac_allocator.cc b/repos/os/src/server/nic_router/mac_allocator.cc index 7e5ac058a..ecf0ee07d 100644 --- a/repos/os/src/server/nic_router/mac_allocator.cc +++ b/repos/os/src/server/nic_router/mac_allocator.cc @@ -1,21 +1,17 @@ /* * \brief MAC-address allocator - * \author Stefan Kalkowski - * \date 2010-08-25 + * \author Martin Stein + * \date 2016-10-24 */ /* - * Copyright (C) 2010-2013 Genode Labs GmbH + * Copyright (C) 2016 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ -#include +/* Genode includes */ +#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/main.cc b/repos/os/src/server/nic_router/main.cc index 830ecbfea..55600b2a4 100644 --- a/repos/os/src/server/nic_router/main.cc +++ b/repos/os/src/server/nic_router/main.cc @@ -12,118 +12,53 @@ */ /* Genode */ +#include +#include +#include +#include #include -#include +#include /* local includes */ #include -#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); - + Timer::Connection _timer_connection; + Genode::Timer _timer; + Genode::Heap _heap; + Configuration _config; + Uplink _uplink; + Net::Root _root; public: - Main(Server::Entrypoint &ep); + Main(Env &env); }; -void Main::_read_ports(Xml_node &route, char const *name, - Port_allocator &port_alloc) -{ - try { - for (Xml_node port = route.sub_node(name); ; port = port.next(name)) { - uint32_t const dst = port.attribute_value("dst", 0UL); - if (!dst) { - warning("missing 'dst' attribute in port route"); - continue; - } - if (dst >= Port_allocator::FIRST && - dst < Port_allocator::FIRST + Port_allocator::COUNT) - { - error("port forwarding clashes with dynamic port range"); } - } - } catch (Xml_node::Nonexistent_sub_node) { } -} - - -Main::Main(Server::Entrypoint &ep) +Main::Main(Env &env) : - _verbose(config()->xml_node().attribute_value("verbose", false)), - _ep(ep), _rtt_sec(read_rtt_sec()), - - _uplink(_ep, _tcp_port_alloc, _udp_port_alloc, _tcp_proxys, - _udp_proxys, _rtt_sec, _interface_tree, _arp_cache, - _arp_waiters, _verbose), - - _root(_ep, *env()->heap(), _uplink.router_mac(), _tcp_port_alloc, - _udp_port_alloc, _tcp_proxys, _udp_proxys, - _rtt_sec, _interface_tree, _arp_cache, _arp_waiters, _verbose) + _timer_connection(env), _timer(_timer_connection, env.ep()), + _heap(&env.ram(), &env.rm()), _config(config()->xml_node(), _heap), + _uplink(env.ep(), _timer, _heap, _config), + _root(env.ep(), _timer, _heap, _uplink.router_mac(), _config, env.ram()) { - /* reserve all ports that are used in port routes */ - try { - Xml_node policy = config()->xml_node().sub_node("policy"); - for (; ; policy = policy.next("policy")) { - try { - Xml_node route = policy.sub_node("ip"); - for (; ; route = route.next("ip")) { - _read_ports(route, "tcp", _tcp_port_alloc); - _read_ports(route, "udp", _udp_port_alloc); - } - } catch (Xml_node::Nonexistent_sub_node) { } - } - } catch (Xml_node::Nonexistent_sub_node) { } - - /* announce service */ - env()->parent()->announce(ep.manage(_root)); + env.parent().announce(env.ep().manage(_root)); } -/************ - ** Server ** - ************/ +/*************** + ** Component ** + ***************/ -namespace Server { - - char const *name() { return "nic_router_ep"; } - - size_t stack_size() { return 4096 *sizeof(addr_t); } - - void construct(Entrypoint &ep) { static Main router(ep); } -} +size_t Component::stack_size() { return 4 * 1024 * sizeof(addr_t); } +void Component::construct(Env &env) { static Main main(env); } diff --git a/repos/os/src/server/nic_router/nat_rule.cc b/repos/os/src/server/nic_router/nat_rule.cc new file mode 100644 index 000000000..8c635ad04 --- /dev/null +++ b/repos/os/src/server/nic_router/nat_rule.cc @@ -0,0 +1,84 @@ +/* + * \brief Rule for doing NAT from one given interface to another + * \author Martin Stein + * \date 2016-09-13 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include + +using namespace Net; +using namespace Genode; + + +bool Nat_rule::higher(Nat_rule *rule) +{ + return (addr_t)&rule->domain() > (addr_t)&_domain; +} + + +Nat_rule::Nat_rule(Domain_tree &domains, + Port_allocator &tcp_port_alloc, + Port_allocator &udp_port_alloc, + Xml_node const &node) +: + Leaf_rule(domains, node), + _tcp_port_alloc(tcp_port_alloc, node.attribute_value("tcp-ports", 0UL)), + _udp_port_alloc(udp_port_alloc, node.attribute_value("udp-ports", 0UL)) +{ } + + +Nat_rule &Nat_rule::find_by_domain(Domain &domain) +{ + if (&domain == &_domain) { + return *this; } + + bool const side = (addr_t)&domain > (addr_t)&_domain; + Nat_rule *const rule = Avl_node::child(side); + if (!rule) { + throw Nat_rule_tree::No_match(); } + + return rule->find_by_domain(domain); +} + + +Nat_rule &Nat_rule_tree::find_by_domain(Domain &domain) +{ + Nat_rule *const rule = first(); + if (!rule) { + throw No_match(); } + + return rule->find_by_domain(domain); +} + + +void Nat_rule::print(Output &output) const +{ + Genode::print(output, "domain ", _domain, + " tcp-ports ", _tcp_port_alloc.max(), + " udp-ports ", _udp_port_alloc.max()); +} + + +Port_allocator_guard &Nat_rule::port_alloc(uint8_t const prot) +{ + switch (prot) { + case Tcp_packet::IP_ID: return _tcp_port_alloc; + case Udp_packet::IP_ID: return _udp_port_alloc; + default: throw Interface::Bad_transport_protocol(); } +} diff --git a/repos/os/src/server/nic_router/nat_rule.h b/repos/os/src/server/nic_router/nat_rule.h new file mode 100644 index 000000000..d6ff9b739 --- /dev/null +++ b/repos/os/src/server/nic_router/nat_rule.h @@ -0,0 +1,83 @@ +/* + * \brief Rule for doing NAT from one given interface to another + * \author Martin Stein + * \date 2016-09-13 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _NAT_RULE_H_ +#define _NAT_RULE_H_ + +/* local includes */ +#include +#include + +/* Genode includes */ +#include + +namespace Net { + + class Port_allocator; + class Nat_rule_base; + class Nat_rule; + class Nat_rule_tree; +} + + +class Net::Nat_rule : public Leaf_rule, + public Genode::Avl_node +{ + private: + + Port_allocator_guard _tcp_port_alloc; + Port_allocator_guard _udp_port_alloc; + + public: + + Nat_rule(Domain_tree &domains, + Port_allocator &tcp_port_alloc, + Port_allocator &udp_port_alloc, + Genode::Xml_node const &node); + + Nat_rule &find_by_domain(Domain &domain); + + Port_allocator_guard &port_alloc(Genode::uint8_t const prot); + + + /********* + ** log ** + *********/ + + void print(Genode::Output &output) const; + + + /************** + ** Avl_node ** + **************/ + + bool higher(Nat_rule *rule); + + + /*************** + ** Accessors ** + ***************/ + + Port_allocator_guard &tcp_port_alloc() { return _tcp_port_alloc; } + Port_allocator_guard &udp_port_alloc() { return _udp_port_alloc; } +}; + + +struct Net::Nat_rule_tree : Genode::Avl_tree +{ + struct No_match : Genode::Exception { }; + + Nat_rule &find_by_domain(Domain &domain); +}; + +#endif /* _NAT_RULE_H_ */ diff --git a/repos/os/src/server/nic_router/permit_rule.cc b/repos/os/src/server/nic_router/permit_rule.cc new file mode 100644 index 000000000..8cd6cf52c --- /dev/null +++ b/repos/os/src/server/nic_router/permit_rule.cc @@ -0,0 +1,106 @@ +/* + * \brief Rule for permitting ports in the context of a transport rule + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include +#include + +using namespace Net; +using namespace Genode; + + +/***************** + ** Permit_rule ** + *****************/ + +Permit_rule::Permit_rule(Domain_tree &domains, Xml_node const &node) +: + Leaf_rule(domains, node) +{ } + + +/********************* + ** Permit_any_rule ** + *********************/ + +void Permit_any_rule::print(Output &output) const +{ + Genode::print(output, "requests to ", _domain); +} + + +Permit_any_rule::Permit_any_rule(Domain_tree &domains, Xml_node const &node) +: + Permit_rule(domains, node) +{ } + + +/************************ + ** Permit_single_rule ** + ************************/ + +bool Permit_single_rule::higher(Permit_single_rule *rule) +{ + return rule->_port > _port; +} + + +void Permit_single_rule::print(Output &output) const +{ + Genode::print(output, "port ", _port, " requests to ", _domain); +} + + +Permit_single_rule::Permit_single_rule(Domain_tree &domains, + Xml_node const &node) +: + Permit_rule(domains, node), + _port(node.attribute_value("port", 0UL)) +{ + if (!_port || dynamic_port(_port)) { + throw Invalid(); } +} + + +Permit_single_rule const & +Permit_single_rule::find_by_port(uint16_t const port) const +{ + if (port == _port) { + return *this; } + + bool const side = port > _port; + Permit_single_rule *const rule = Avl_node::child(side); + if (!rule) { + throw Permit_single_rule_tree::No_match(); } + + return rule->find_by_port(port); +} + + + +/***************************** + ** Permit_single_rule_tree ** + *****************************/ + +Permit_single_rule const & +Permit_single_rule_tree::find_by_port(uint16_t const port) const +{ + Permit_single_rule *const rule = first(); + if (!rule) { + throw No_match(); } + + return rule->find_by_port(port); +} diff --git a/repos/os/src/server/nic_router/permit_rule.h b/repos/os/src/server/nic_router/permit_rule.h new file mode 100644 index 000000000..3a57f5be1 --- /dev/null +++ b/repos/os/src/server/nic_router/permit_rule.h @@ -0,0 +1,105 @@ +/* + * \brief Rules for permitting ports in the context of a transport rule + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PERMIT_RULE_H_ +#define _PERMIT_RULE_H_ + +/* local includes */ +#include + +/* Genode includes */ +#include + +namespace Genode { class Output; } + +namespace Net { + + class Permit_rule; + class Permit_any_rule; + class Permit_single_rule; + class Permit_single_rule_tree; +} + + +struct Net::Permit_rule : Leaf_rule +{ + Permit_rule(Domain_tree &domains, Genode::Xml_node const &node); + + + /********* + ** log ** + *********/ + + virtual void print(Genode::Output &output) const = 0; +}; + + +struct Net::Permit_any_rule : Permit_rule +{ + Permit_any_rule(Domain_tree &domains, Genode::Xml_node const &node); + + + /********* + ** log ** + *********/ + + void print(Genode::Output &output) const; +}; + + +class Net::Permit_single_rule : public Permit_rule, + public Genode::Avl_node +{ + private: + + Genode::uint16_t const _port; + + public: + + Permit_single_rule(Domain_tree &domains, + Genode::Xml_node const &node); + + Permit_single_rule const & + find_by_port(Genode::uint16_t const port) const; + + + /********* + ** log ** + *********/ + + void print(Genode::Output &output) const; + + + /************** + ** Avl_node ** + **************/ + + bool higher(Permit_single_rule *rule); + + + /*************** + ** Accessors ** + ***************/ + + Genode::uint16_t port() const { return _port; } +}; + + +struct Net::Permit_single_rule_tree : Genode::Avl_tree +{ + struct No_match : Genode::Exception { }; + + Permit_single_rule const &find_by_port(Genode::uint16_t const port) const; +}; + +#endif /* _PERMIT_RULE_H_ */ diff --git a/repos/os/src/server/nic_router/pointer.h b/repos/os/src/server/nic_router/pointer.h new file mode 100644 index 000000000..cd08a80af --- /dev/null +++ b/repos/os/src/server/nic_router/pointer.h @@ -0,0 +1,55 @@ +/* + * \brief Pointer that can be dereferenced only when valid + * \author Martin Stein + * \date 2016-08-24 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _POINTER_H_ +#define _POINTER_H_ + +/* Genode includes */ +#include + +namespace Net { template class Pointer; } + +template +class Net::Pointer +{ + private: + + T *_ptr; + + public: + + struct Valid : Genode::Exception { }; + struct Invalid : Genode::Exception { }; + + Pointer() : _ptr(nullptr) { } + + T &deref() const + { + if (_ptr == nullptr) { + throw Invalid(); } + + return *_ptr; + } + + void set(T &ptr) + { + if (_ptr != nullptr) { + throw Valid(); } + + _ptr = &ptr; + } + + void unset() { _ptr = nullptr; } +}; + +#endif /* _POINTER_H_ */ diff --git a/repos/os/src/server/nic_router/port_allocator.cc b/repos/os/src/server/nic_router/port_allocator.cc index a12e3d26b..6daf372cc 100644 --- a/repos/os/src/server/nic_router/port_allocator.cc +++ b/repos/os/src/server/nic_router/port_allocator.cc @@ -18,8 +18,33 @@ using namespace Net; using namespace Genode; -Port_allocator::Port_allocator() +Genode::uint16_t Port_allocator_guard::alloc() { - try { alloc_index(0); } - catch (Already_allocated) { throw Failed_to_reserve_port_0(); } + if (_used == _max) { + throw Out_of_indices(); } + + uint16_t const port = _port_alloc.alloc(); + _used++; + return port; +} + + +void Port_allocator_guard::free(Genode::uint16_t port) +{ + _port_alloc.free(port); + _used = _used ? _used - 1 : 0; +} + + +Port_allocator_guard::Port_allocator_guard(Port_allocator & port_alloc, + unsigned const max) +: + _port_alloc(port_alloc), _max(max) +{ } + + +bool Net::dynamic_port(uint16_t const port) +{ + return port >= Port_allocator::FIRST && + port < (uint32_t)Port_allocator::FIRST + Port_allocator::COUNT; } diff --git a/repos/os/src/server/nic_router/port_allocator.h b/repos/os/src/server/nic_router/port_allocator.h index 12d3e987a..1607979d7 100644 --- a/repos/os/src/server/nic_router/port_allocator.h +++ b/repos/os/src/server/nic_router/port_allocator.h @@ -18,7 +18,14 @@ /* Genode includes */ #include -namespace Net { class Port_allocator; } +namespace Net { + + class Port_allocator; + class Port_allocator_guard; + + bool dynamic_port(Genode::uint16_t const port); +} + class Net::Port_allocator { @@ -37,4 +44,26 @@ class Net::Port_allocator void free(Genode::uint16_t port) { _alloc.free(port - FIRST); } }; + +class Net::Port_allocator_guard +{ + private: + + Port_allocator &_port_alloc; + unsigned const _max; + unsigned _used = 0; + + public: + + class Out_of_indices : Genode::Exception {}; + + Genode::uint16_t alloc(); + + void free(Genode::uint16_t port); + + Port_allocator_guard(Port_allocator & port_alloc, unsigned const max); + + unsigned max() const { return _max; } +}; + #endif /* _PORT_ALLOCATOR_H_ */ diff --git a/repos/os/src/server/nic_router/port_route.cc b/repos/os/src/server/nic_router/port_route.cc deleted file mode 100644 index bf906c04d..000000000 --- a/repos/os/src/server/nic_router/port_route.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * \brief Port routing entry - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* local includes */ -#include - -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 deleted file mode 100644 index 2ca3aadd7..000000000 --- a/repos/os/src/server/nic_router/port_route.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * \brief Port routing entry - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#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/protocol_name.cc b/repos/os/src/server/nic_router/protocol_name.cc new file mode 100644 index 000000000..baa4190bc --- /dev/null +++ b/repos/os/src/server/nic_router/protocol_name.cc @@ -0,0 +1,39 @@ +/* + * \brief Provide protocol names as Genode Cstring objects + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include +#include + +/* Genode includes */ +#include +#include +#include + +using namespace Net; +using namespace Genode; + +static Cstring const _udp_name("UDP"); +static Cstring const _tcp_name("TCP"); + +Cstring const &Net::udp_name() { return _udp_name; } +Cstring const &Net::tcp_name() { return _tcp_name; } + + +Cstring const &Net::protocol_name(uint8_t protocol) +{ + switch (protocol) { + case Tcp_packet::IP_ID: return tcp_name(); + case Udp_packet::IP_ID: return udp_name(); + default: throw Interface::Bad_transport_protocol(); } +} diff --git a/repos/os/src/server/nic_router/protocol_name.h b/repos/os/src/server/nic_router/protocol_name.h new file mode 100644 index 000000000..d08b9c0e8 --- /dev/null +++ b/repos/os/src/server/nic_router/protocol_name.h @@ -0,0 +1,29 @@ +/* + * \brief Provide protocol names as Genode Cstring objects + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PROTOCOL_NAME_H_ +#define _PROTOCOL_NAME_H_ + +/* Genode includes */ +#include + +namespace Genode { class Cstring; } + +namespace Net { + + Genode::Cstring const &tcp_name(); + Genode::Cstring const &udp_name(); + Genode::Cstring const &protocol_name(Genode::uint8_t protocol); +} + +#endif /* _PROTOCOL_NAME_H_ */ diff --git a/repos/os/src/server/nic_router/proxy.cc b/repos/os/src/server/nic_router/proxy.cc deleted file mode 100644 index 8fe38881a..000000000 --- a/repos/os/src/server/nic_router/proxy.cc +++ /dev/null @@ -1,120 +0,0 @@ -/* - * \brief UDP/TCP proxy session - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#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 deleted file mode 100644 index 556f2bfdf..000000000 --- a/repos/os/src/server/nic_router/proxy.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * \brief UDP/TCP proxy session - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _PROXY_H_ -#define _PROXY_H_ - -/* Genode includes */ -#include -#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/rule.h b/repos/os/src/server/nic_router/rule.h new file mode 100644 index 000000000..050f981c3 --- /dev/null +++ b/repos/os/src/server/nic_router/rule.h @@ -0,0 +1,22 @@ +/* + * \brief Base of each routing rule + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _RULE_H_ +#define _RULE_H_ + +/* Genode includes */ +#include + +namespace Net { struct Rule { struct Invalid : Genode::Exception { }; }; } + +#endif /* _RULE_H_ */ diff --git a/repos/os/src/server/nic_router/target.mk b/repos/os/src/server/nic_router/target.mk index 7fdcbd49e..aa1e192c8 100644 --- a/repos/os/src/server/nic_router/target.mk +++ b/repos/os/src/server/nic_router/target.mk @@ -1,11 +1,12 @@ TARGET = nic_router -LIBS += base net config server +LIBS += base net config timeout -SRC_CC += arp_waiter.cc ip_route.cc proxy.cc -SRC_CC += port_route.cc component.cc -SRC_CC += mac_allocator.cc main.cc uplink.cc interface.cc arp_cache.cc +SRC_CC += arp_waiter.cc ip_rule.cc +SRC_CC += component.cc port_allocator.cc forward_rule.cc +SRC_CC += nat_rule.cc mac_allocator.cc main.cc +SRC_CC += uplink.cc interface.cc arp_cache.cc configuration.cc +SRC_CC += domain.cc protocol_name.cc direct_rule.cc link.cc +SRC_CC += transport_rule.cc leaf_rule.cc permit_rule.cc INC_DIR += $(PRG_DIR) - -vpath *.cc $(REP_DIR)/src/server/proxy_arp diff --git a/repos/os/src/server/nic_router/transport_rule.cc b/repos/os/src/server/nic_router/transport_rule.cc new file mode 100644 index 000000000..352cefb6a --- /dev/null +++ b/repos/os/src/server/nic_router/transport_rule.cc @@ -0,0 +1,79 @@ +/* + * \brief Rule for allowing direct TCP/UDP traffic between two interfaces + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include +#include + +using namespace Net; +using namespace Genode; + + +Permit_any_rule *Transport_rule::_read_permit_any(Domain_tree &domains, + Xml_node const &node, + Allocator &alloc) +{ + try { + Xml_node sub_node = node.sub_node("permit-any"); + return new (alloc) Permit_any_rule(domains, sub_node); + } + catch (Xml_node::Nonexistent_sub_node) { } + catch (Rule::Invalid) { warning("invalid permit-any rule"); } + return nullptr; +} + + +Transport_rule::Transport_rule(Domain_tree &domains, + Xml_node const &node, + Allocator &alloc, + Cstring const &protocol, + Configuration &config) +: + Direct_rule(node), _permit_any(_read_permit_any(domains, node, alloc)) +{ + /* skip specific permit rules if all ports are permitted anyway */ + if (_permit_any) { + if (config.verbose()) { + log(" ", protocol, " rule: ", _dst, " ", *_permit_any); } + + return; + } + + /* read specific permit rules */ + node.for_each_sub_node("permit", [&] (Xml_node const &node) { + try { + Permit_single_rule &rule = *new (alloc) + Permit_single_rule(domains, node); + + _permit_single_rules.insert(&rule); + if (config.verbose()) { + log(protocol, " rule: ", _dst, " ", rule); } + } + catch (Rule::Invalid) { warning("invalid permit rule"); } + }); + /* drop the transport rule if it has no permitted ports */ + if (!_permit_single_rules.first()) { + throw Invalid(); } +} + + +Permit_rule const &Transport_rule::permit_rule(uint16_t const port) const +{ + if (_permit_any) { return *_permit_any; } + return _permit_single_rules.find_by_port(port); +} diff --git a/repos/os/src/server/nic_router/transport_rule.h b/repos/os/src/server/nic_router/transport_rule.h new file mode 100644 index 000000000..4daee4334 --- /dev/null +++ b/repos/os/src/server/nic_router/transport_rule.h @@ -0,0 +1,54 @@ +/* + * \brief Rule for allowing direct TCP/UDP traffic between two interfaces + * \author Martin Stein + * \date 2016-08-19 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TRANSPORT_RULE_H_ +#define _TRANSPORT_RULE_H_ + +/* local includes */ +#include +#include + +namespace Genode { class Allocator; } + +namespace Net { + + class Configuration; + class Transport_rule; + struct Transport_rule_list : Direct_rule_list { }; +} + + +class Net::Transport_rule : public Direct_rule +{ + private: + + Permit_any_rule *const _permit_any; + Permit_single_rule_tree _permit_single_rules; + + static Permit_any_rule * + _read_permit_any(Domain_tree &domains, + Genode::Xml_node const &node, + Genode::Allocator &alloc); + + public: + + Transport_rule(Domain_tree &domains, + Genode::Xml_node const &node, + Genode::Allocator &alloc, + Genode::Cstring const &protocol, + Configuration &config); + + Permit_rule const &permit_rule(Genode::uint16_t const port) const; +}; + +#endif /* _TRANSPORT_RULE_H_ */ diff --git a/repos/os/src/server/nic_router/uplink.cc b/repos/os/src/server/nic_router/uplink.cc index 1aceb8e01..dd10e464d 100644 --- a/repos/os/src/server/nic_router/uplink.cc +++ b/repos/os/src/server/nic_router/uplink.cc @@ -1,11 +1,11 @@ /* - * \brief NIC handler - * \author Stefan Kalkowski - * \date 2013-05-24 + * \brief Uplink interface in form of a NIC session component + * \author Martin Stein + * \date 2016-08-23 */ /* - * Copyright (C) 2013 Genode Labs GmbH + * Copyright (C) 2016 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. @@ -14,48 +14,24 @@ /* Genode includes */ #include #include -#include -#include -#include -#include -#include /* local includes */ -#include #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) +Net::Uplink::Uplink(Entrypoint &ep, + Genode::Timer &timer, + Genode::Allocator &alloc, + Configuration &config) : - Nic::Packet_allocator(env()->heap()), + Nic::Packet_allocator(&alloc), Nic::Connection(this, BUF_SIZE, BUF_SIZE), - - Interface(ep, mac_address(), _read_src(), *env()->heap(), - "label=\"uplink\"", tcp_port_alloc, udp_port_alloc, - Mac_address(), tcp_proxys, udp_proxys, - rtt_sec, interface_tree, arp_cache, arp_waiters, verbose) + Interface(ep, timer, mac_address(), alloc, Mac_address(), + config.domains().find_by_name(Cstring("uplink"))) { rx_channel()->sigh_ready_to_ack(_sink_ack); rx_channel()->sigh_packet_avail(_sink_submit); diff --git a/repos/os/src/server/nic_router/uplink.h b/repos/os/src/server/nic_router/uplink.h index 1d866b4c0..fef343b20 100644 --- a/repos/os/src/server/nic_router/uplink.h +++ b/repos/os/src/server/nic_router/uplink.h @@ -16,18 +16,16 @@ /* Genode includes */ #include +#include /* local includes */ #include -namespace Net { - - class Port_allocator; - class Uplink; -} +namespace Net { class Uplink; } class Net::Uplink : public Nic::Packet_allocator, - public Nic::Connection, public Net::Interface + public Nic::Connection, + public Interface { private: @@ -36,28 +34,29 @@ class Net::Uplink : public Nic::Packet_allocator, BUF_SIZE = Nic::Session::QUEUE_SIZE * PKT_SIZE, }; - Ipv4_address _read_src(); - - public: - - Uplink(Server::Entrypoint &ep, - Port_allocator &tcp_port_alloc, - Port_allocator &udp_port_alloc, - Tcp_proxy_list &tcp_proxys, - Udp_proxy_list &udp_proxys, - unsigned rtt_sec, - Interface_tree &interface_tree, - Arp_cache &arp_cache, - Arp_waiter_list &arp_waiters, - bool verbose); + Ipv4_address_prefix _read_interface(); /******************** ** Net::Interface ** ********************/ - Packet_stream_sink *sink() { return rx(); } - Packet_stream_source *source() { return tx(); } + Packet_stream_sink &_sink() { return *rx(); } + Packet_stream_source &_source() { return *tx(); } + + public: + + Uplink(Genode::Entrypoint &ep, + Genode::Timer &timer, + Genode::Allocator &alloc, + Configuration &config); + + + /*************** + ** Accessors ** + ***************/ + + Mac_address const &router_mac() const { return _router_mac; } }; #endif /* _UPLINK_H_ */ diff --git a/tool/autopilot.list b/tool/autopilot.list index 75decf3b4..d951437ca 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -76,3 +76,4 @@ usb_hid smartcard new_delete timeout +nic_router