mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-01 16:58:29 +00:00
nic_router: support multiple DHCP option 6 entries
* The NIC router now considers, memorizes, and, if configured, reports multiple DHCP option 6 entries from DHCP replies that it received as DHCP client * A DHCP server at the NIC router can now be configured statically with multiple DNS server addresses to propagate * The 'dns_server_from' attribute of the DHCP server of the NIC router now supports the forwarding of multiple DNS server addresses * The automated run/nic_router_dhcp test tests all the above mentioned new functionality and reconfiguring it at runtime. The test was added to the autopilot. * All run scripts were adapted to fit the new NIC router configuration interface Fixes #3952
This commit is contained in:
parent
306466fc60
commit
bad8caee3f
@ -228,8 +228,11 @@ proc test_7_router_config { } {
|
||||
<domain name="t7_d1" interface="100.200.0.1/24">
|
||||
<dhcp-server ip_first="100.200.0.32"
|
||||
ip_last="100.200.0.64"
|
||||
ip_lease_time_sec="3600"
|
||||
dns_server="8.8.8.8"/>
|
||||
ip_lease_time_sec="3600">
|
||||
|
||||
<dns-server ip="8.8.8.8"/>
|
||||
|
||||
</dhcp-server>
|
||||
|
||||
<tcp dst="10.0.0.0/16">
|
||||
<permit port="2345" domain="t7_d2" />
|
||||
|
@ -159,8 +159,9 @@ proc test_7_router_config { } {
|
||||
<domain name="lan_2" interface="100.200.0.1/24">
|
||||
<dhcp-server ip_first="100.200.0.32"
|
||||
ip_last="100.200.0.64"
|
||||
ip_lease_time_sec="3600"
|
||||
dns_server="8.8.8.8"/>
|
||||
ip_lease_time_sec="3600">
|
||||
<dns-server ip="8.8.8.8"/>
|
||||
</dhcp-server>
|
||||
|
||||
<tcp dst="10.0.0.0/16">
|
||||
<permit port="2345" domain="lan_3" />
|
||||
|
282
repos/os/run/nic_router_dhcp.inc
Normal file
282
repos/os/run/nic_router_dhcp.inc
Normal file
@ -0,0 +1,282 @@
|
||||
#
|
||||
# See os/src/test/nic_router_dhcp/README for a documentation.
|
||||
#
|
||||
|
||||
create_boot_directory
|
||||
|
||||
import_from_depot [depot_user]/src/[base_src]
|
||||
|
||||
set build_components {
|
||||
|
||||
init
|
||||
server/dynamic_rom
|
||||
test/nic_router_dhcp/client
|
||||
server/nic_router
|
||||
}
|
||||
|
||||
lappend_if [nic_router_2_managed] build_components test/nic_router_dhcp/manager
|
||||
lappend_if [nic_router_2_managed] build_components server/report_rom
|
||||
|
||||
build $build_components
|
||||
|
||||
append config {
|
||||
|
||||
<config>
|
||||
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<default caps="200"/>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
|
||||
<start name="dynamic_rom">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides><service name="ROM"/> </provides>
|
||||
<config verbose="yes">
|
||||
<rom name="nic_router_1.config">
|
||||
<inline>
|
||||
|
||||
<config>
|
||||
|
||||
<policy label="nic_router_2 -> " domain="downlink"/>
|
||||
|
||||
<domain name="downlink" interface="10.2.3.1/24">
|
||||
|
||||
<dhcp-server ip_first="10.2.3.2"
|
||||
ip_last="10.2.3.2">
|
||||
|
||||
<dns-server ip="1.2.3.4"/>
|
||||
<dns-server ip="2.3.4.5"/>
|
||||
<dns-server ip="3.4.5.6"/>
|
||||
|
||||
</dhcp-server>
|
||||
|
||||
</domain>
|
||||
|
||||
</config>
|
||||
|
||||
</inline>
|
||||
<sleep milliseconds="3000"/>
|
||||
<inline>
|
||||
|
||||
<config>
|
||||
|
||||
<policy label="nic_router_2 -> " domain="downlink"/>
|
||||
|
||||
<domain name="downlink" interface="10.2.3.1/24">
|
||||
|
||||
<dhcp-server ip_first="10.2.3.2"
|
||||
ip_last="10.2.3.2">
|
||||
|
||||
<dns-server ip="4.5.6.7"/>
|
||||
<dns-server ip="5.6.7.8"/>
|
||||
|
||||
</dhcp-server>
|
||||
|
||||
</domain>
|
||||
|
||||
</config>
|
||||
|
||||
</inline>
|
||||
<sleep milliseconds="3000"/>
|
||||
<inline>
|
||||
|
||||
<config>
|
||||
|
||||
<policy label="nic_router_2 -> " domain="downlink"/>
|
||||
|
||||
<domain name="downlink" interface="10.2.3.1/24">
|
||||
|
||||
<dhcp-server ip_first="10.2.3.2"
|
||||
ip_last="10.2.3.2">
|
||||
|
||||
<dns-server ip="6.7.8.9"/>
|
||||
|
||||
</dhcp-server>
|
||||
|
||||
</domain>
|
||||
|
||||
</config>
|
||||
|
||||
</inline>
|
||||
<sleep milliseconds="3000"/>
|
||||
<inline>
|
||||
|
||||
<config>
|
||||
|
||||
<policy label="nic_router_2 -> " domain="downlink"/>
|
||||
|
||||
<domain name="downlink" interface="10.2.3.1/24">
|
||||
|
||||
<dhcp-server ip_first="10.2.3.2"
|
||||
ip_last="10.2.3.2">
|
||||
|
||||
</dhcp-server>
|
||||
|
||||
</domain>
|
||||
|
||||
</config>
|
||||
|
||||
</inline>
|
||||
<sleep milliseconds="3000"/>
|
||||
|
||||
</rom>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="nic_router_1">
|
||||
<binary name="nic_router"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides><service name="Nic"/></provides>
|
||||
<route>
|
||||
<service name="ROM" label="config">
|
||||
<child name="dynamic_rom" label="nic_router_1.config"/>
|
||||
</service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="nic_router_2">
|
||||
<binary name="nic_router"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides><service name="Nic"/></provides>}
|
||||
|
||||
append_if [expr ![nic_router_2_managed]] config {
|
||||
|
||||
<config verbose_packets="no">
|
||||
|
||||
<policy label="test_client -> " domain="downlink"/>
|
||||
<uplink domain="uplink"/>
|
||||
|
||||
<domain name="uplink"/>
|
||||
<domain name="downlink" interface="10.0.3.1/24">
|
||||
|
||||
<dhcp-server ip_first="10.0.3.2"
|
||||
ip_last="10.0.3.2"
|
||||
dns_server_from="uplink"/>
|
||||
|
||||
</domain>
|
||||
|
||||
</config>}
|
||||
|
||||
append config {
|
||||
|
||||
<route>
|
||||
<service name="Nic"> <child name="nic_router_1"/> </service>}
|
||||
|
||||
append_if [nic_router_2_managed] config {
|
||||
|
||||
<service name="ROM" label="config">
|
||||
<child name="report_rom"/>
|
||||
</service>}
|
||||
|
||||
append config {
|
||||
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="test_client">
|
||||
<binary name="test-nic_router_dhcp-client"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<config verbose="no"/>
|
||||
<route>
|
||||
<service name="Nic"> <child name="nic_router_2"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>}
|
||||
|
||||
append_if [nic_router_2_managed] config {
|
||||
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Report"/> <service name="ROM"/> </provides>
|
||||
<config verbose="no">
|
||||
<policy label="test_manager -> router_state"
|
||||
report="nic_router_2 -> state"/>
|
||||
|
||||
<policy label="nic_router_2 -> config"
|
||||
report="test_manager -> router_config"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="test_manager">
|
||||
<binary name="test-nic_router_dhcp-manager"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<route>
|
||||
<service name="ROM" label="router_state">
|
||||
<child name="report_rom"/>
|
||||
</service>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>}
|
||||
|
||||
append config {
|
||||
|
||||
</config>}
|
||||
|
||||
install_config $config
|
||||
|
||||
set boot_modules {
|
||||
|
||||
init
|
||||
dynamic_rom
|
||||
nic_router
|
||||
test-nic_router_dhcp-client
|
||||
}
|
||||
|
||||
lappend_if [nic_router_2_managed] boot_modules test-nic_router_dhcp-manager
|
||||
lappend_if [nic_router_2_managed] boot_modules report_rom
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
append qemu_args " -nographic "
|
||||
append_qemu_nic_args
|
||||
|
||||
append done_string ".*DHCP request completed:.*\n"
|
||||
append done_string ".* IP lease time: 3600 seconds.*\n"
|
||||
append done_string ".* Interface: 10.0.3.2/24.*\n"
|
||||
append done_string ".* Router: 10.0.3.1.*\n"
|
||||
append done_string ".* DNS server #1: 1.2.3.4.*\n"
|
||||
append done_string ".* DNS server #2: 2.3.4.5.*\n"
|
||||
append done_string ".* DNS server #3: 3.4.5.6.*\n"
|
||||
append done_string ".*DHCP request completed:.*\n"
|
||||
append done_string ".* IP lease time: 3600 seconds.*\n"
|
||||
append done_string ".* Interface: 10.0.3.2/24.*\n"
|
||||
append done_string ".* Router: 10.0.3.1.*\n"
|
||||
append done_string ".* DNS server #1: 4.5.6.7.*\n"
|
||||
append done_string ".* DNS server #2: 5.6.7.8.*\n"
|
||||
append done_string ".*DHCP request completed:.*\n"
|
||||
append done_string ".* IP lease time: 3600 seconds.*\n"
|
||||
append done_string ".* Interface: 10.0.3.2/24.*\n"
|
||||
append done_string ".* Router: 10.0.3.1.*\n"
|
||||
append done_string ".* DNS server #1: 6.7.8.9.*\n"
|
||||
append done_string ".*DHCP request completed:.*\n"
|
||||
append done_string ".* IP lease time: 3600 seconds.*\n"
|
||||
append done_string ".* Interface: 10.0.3.2/24.*\n"
|
||||
append done_string ".* Router: 10.0.3.1.*\n"
|
||||
append done_string ".*DHCP request completed:.*\n"
|
||||
append done_string ".* IP lease time: 3600 seconds.*\n"
|
||||
append done_string ".* Interface: 10.0.3.2/24.*\n"
|
||||
append done_string ".* Router: 10.0.3.1.*\n"
|
||||
append done_string ".* DNS server #1: 1.2.3.4.*\n"
|
||||
append done_string ".* DNS server #2: 2.3.4.5.*\n"
|
||||
append done_string ".* DNS server #3: 3.4.5.6.*\n"
|
||||
|
||||
run_genode_until $done_string 30
|
6
repos/os/run/nic_router_dhcp_managed.run
Normal file
6
repos/os/run/nic_router_dhcp_managed.run
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# See os/src/test/nic_router_dhcp/README for a documentation.
|
||||
#
|
||||
|
||||
proc nic_router_2_managed { } { return 1 }
|
||||
source ${genode_dir}/repos/os/run/nic_router_dhcp.inc
|
6
repos/os/run/nic_router_dhcp_unmanaged.run
Normal file
6
repos/os/run/nic_router_dhcp_unmanaged.run
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# See os/src/test/nic_router_dhcp/README for a documentation.
|
||||
#
|
||||
|
||||
proc nic_router_2_managed { } { return 0 }
|
||||
source ${genode_dir}/repos/os/run/nic_router_dhcp.inc
|
@ -385,34 +385,61 @@ One can configure the NIC router to act as DHCP server at interfaces of a
|
||||
domain by adding the <dhcp> tag to the configuration of the domain like
|
||||
this:
|
||||
|
||||
<domain name="vbox" interface="10.0.1.1/24">
|
||||
<dhcp-server ip_first="10.0.1.80"
|
||||
ip_last="10.0.1.100"
|
||||
ip_lease_time_sec="3600"
|
||||
dns_server="10.0.0.2"
|
||||
dns_server_from="uplink" />
|
||||
...
|
||||
</domain>
|
||||
! <domain name="vbox" interface="10.0.1.1/24">
|
||||
!
|
||||
! <dhcp-server ip_first="10.0.1.80"
|
||||
! ip_last="10.0.1.100"
|
||||
! ip_lease_time_sec="3600">
|
||||
!
|
||||
! <dns-server ip="8.8.8.8" />
|
||||
! <dns-server ip="1.1.1.1" />
|
||||
! ...
|
||||
!
|
||||
! </dhcp-server>
|
||||
! ...
|
||||
!
|
||||
! </domain>
|
||||
|
||||
The attributes ip_first and ip_last define the available IPv4 address range
|
||||
while ip_lease_time_sec defines the lifetime of an IPv4 address assignment in
|
||||
seconds. The IPv4 address range must be in the subnet defined by the interface
|
||||
attribute of the domain tag and must not cover the IPv4 address in this
|
||||
attribute. The dns_server attribute gives the IPv4 address of the DNS server
|
||||
that might also be in another subnet. The dns_server_from attribute has effect
|
||||
only if the dns_server attribute is not set. If this is the case, the
|
||||
dns_server_from attribute states the domain from whose IP config to take the
|
||||
DNS server address. This is useful, for instance, if the stated domain
|
||||
receives the address of a local DNS server via DHCP. Whenever the IP config
|
||||
of the stated domain becomes invalid, the DHCP server switches to a mode where
|
||||
it drops all requests unanswered until the IP config becomes valid again.
|
||||
or like this:
|
||||
|
||||
! <domain name="vbox" interface="10.0.1.1/24">
|
||||
!
|
||||
! <dhcp-server ip_first="10.0.1.80"
|
||||
! ip_last="10.0.1.100"
|
||||
! ip_lease_time_sec="3600"
|
||||
! dns_server_from="uplink" />
|
||||
! ...
|
||||
!
|
||||
! </domain>
|
||||
|
||||
The mandatory attributes 'ip_first' and 'ip_last' define the available IPv4
|
||||
address range while the optional attribute 'ip_lease_time_sec' defines the
|
||||
lifetime of an IPv4 address assignment in seconds. The IPv4 address range must
|
||||
be in the subnet defined by the 'interface' attribute of the <domain> tag and
|
||||
must not cover the IPv4 address given by this attribute.
|
||||
|
||||
The <dns-server> sub-tags from the first example statically provide a list of
|
||||
DNS server addresses that shall be propagated by the DHCP server through DHCP
|
||||
option 6 entries to its clients. These addresses might be of any IP subnet. The
|
||||
DHCP option 6 entries in the DHCP replies will have the same order as the
|
||||
<dns-server> tags in the configuration.
|
||||
|
||||
The 'dns_server_from' attribute from the second example takes effect only when
|
||||
the <dhcp-server> tag does not contain any <dns-server> sub-tags. The attribute
|
||||
states the domain from whose IP config to take the list of propagated DNS
|
||||
server addresses. Note that the order of DNS server adresses is not altered
|
||||
thereby. This is useful in scenarios where these addresses must be obtained
|
||||
dynamically through the DHCP client of another domain. An implication of the
|
||||
'dns_server_from' attribute is that the link state of all interfaces at the
|
||||
domain with the DHCP server becomes bound to the validity of the IP config of
|
||||
the domain that is stated in the attribute.
|
||||
|
||||
The lifetime of an assignment that was yet only offered to the client can be
|
||||
configured for all domains in the <config> tag of the router:
|
||||
|
||||
! <config dhcp_offer_timeout_sec="6">
|
||||
|
||||
The timeout ip_lease_time_sec is applied only when the offer is acknowledged
|
||||
The timeout 'ip_lease_time_sec' is applied only when the offer is acknowledged
|
||||
by the client in time.
|
||||
|
||||
|
||||
@ -454,7 +481,7 @@ Configuration example (shows default values of attributes):
|
||||
! quota="yes"
|
||||
! config="yes"
|
||||
! config_triggers="no"
|
||||
! interval_sec="5">
|
||||
! interval_sec="5"/>
|
||||
! </config>
|
||||
|
||||
If the 'report' tag is not available, no reports are send.
|
||||
@ -560,19 +587,24 @@ two contrary edges at a max. The following diagrams demonstrate this:
|
||||
Examples
|
||||
~~~~~~~~
|
||||
|
||||
This section will list and explain some interesting configuration snippets. A
|
||||
comprehensive example of how to use the router (except DHCP server
|
||||
functionality) can be found in the test script 'libports/run/nic_router.run'.
|
||||
For an example of how to use the DHCP server and the DHCP client functionality
|
||||
see the 'os/run/ping_nic_router.run' script.
|
||||
In-action examples of how to use the router are provided through the following
|
||||
automated run scripts:
|
||||
|
||||
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 .
|
||||
* libports/run/nic_router.run (basic functionality)
|
||||
* dde_linux/run/nic_router_uplinks.run (dynamically switching uplinks)
|
||||
* os/run/ping_nic_router.run (ICMP routing)
|
||||
* os/run/nic_router_dhcp_unmanaged.run (DHCP + link states without a manager)
|
||||
* os/run/nic_router_dhcp_managed.run (DHCP + link states with a manager)
|
||||
* os/run/nic_router_flood.run (client misbehaving on protocol level)
|
||||
* os/run/nic_router_stress.run (client misbehaving on session level)
|
||||
|
||||
The rest of this section will list and explain some smaller configuration
|
||||
snippets. The environment for these 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 .
|
||||
|
||||
|
||||
Connecting local networks
|
||||
|
@ -109,10 +109,18 @@
|
||||
|
||||
<xs:element name="dhcp-server">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
|
||||
<xs:element name="dns-server">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="ip" type="Ipv4_address" />
|
||||
</xs:complexType>
|
||||
</xs:element><!-- dns-server -->
|
||||
|
||||
</xs:choice>
|
||||
<xs:attribute name="ip_first" type="Ipv4_address" />
|
||||
<xs:attribute name="ip_last" type="Ipv4_address" />
|
||||
<xs:attribute name="ip_lease_time_sec" type="Seconds" />
|
||||
<xs:attribute name="dns_server" type="Ipv4_address" />
|
||||
<xs:attribute name="dns_server_from" type="Domain_name" />
|
||||
</xs:complexType>
|
||||
</xs:element><!-- dhcp-server -->
|
||||
|
30
repos/os/src/server/nic_router/dhcp.h
Normal file
30
repos/os/src/server/nic_router/dhcp.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* \brief Genode DHCP protocol plus local utilities
|
||||
* \author Martin Stein
|
||||
* \date 2020-11-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _DHCP_H_
|
||||
#define _DHCP_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/dhcp.h>
|
||||
|
||||
namespace Net {
|
||||
|
||||
template <typename T>
|
||||
static Ipv4_address dhcp_ipv4_option(Dhcp_packet &dhcp)
|
||||
{
|
||||
try { return dhcp.option<T>().value(); }
|
||||
catch (Dhcp_packet::Option_not_found) { return Ipv4_address { }; }
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _DHCP_H_ */
|
@ -142,20 +142,7 @@ void Dhcp_client::handle_dhcp_reply(Dhcp_packet &dhcp)
|
||||
}
|
||||
_lease_time_sec = dhcp.option<Dhcp_packet::Ip_lease_time>().value();
|
||||
_set_state(State::BOUND, _rerequest_timeout(1));
|
||||
|
||||
Ipv4_address dns_server;
|
||||
Ipv4_address subnet_mask;
|
||||
Ipv4_address router_ip;
|
||||
|
||||
try { dns_server = dhcp.option<Dhcp_packet::Dns_server_ipv4>().value(); }
|
||||
catch (Dhcp_packet::Option_not_found) { }
|
||||
try { subnet_mask = dhcp.option<Dhcp_packet::Subnet_mask>().value(); }
|
||||
catch (Dhcp_packet::Option_not_found) { }
|
||||
try { router_ip = dhcp.option<Dhcp_packet::Router_ipv4>().value(); }
|
||||
catch (Net::Dhcp_packet::Option_not_found) { }
|
||||
|
||||
_domain().ip_config(dhcp.yiaddr(), subnet_mask, router_ip,
|
||||
dns_server);
|
||||
_domain().ip_config_from_dhcp_ack(dhcp);
|
||||
break;
|
||||
}
|
||||
case State::RENEW:
|
||||
|
@ -21,11 +21,37 @@ using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/*****************
|
||||
** Dhcp_server **
|
||||
*****************/
|
||||
/**********************
|
||||
** Dhcp_server_base **
|
||||
**********************/
|
||||
|
||||
void Dhcp_server::_invalid(Domain &domain,
|
||||
Dhcp_server_base::Dhcp_server_base(Xml_node const &node,
|
||||
Domain const &domain,
|
||||
Allocator &alloc)
|
||||
:
|
||||
_alloc { alloc }
|
||||
{
|
||||
node.for_each_sub_node("dns-server", [&] (Xml_node const &sub_node) {
|
||||
|
||||
try {
|
||||
_dns_servers.insert_as_tail(*new (alloc)
|
||||
Dns_server(sub_node.attribute_value("ip", Ipv4_address())));
|
||||
|
||||
} catch (Dns_server::Invalid) {
|
||||
|
||||
_invalid(domain, "invalid DNS server entry");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Dhcp_server_base::~Dhcp_server_base()
|
||||
{
|
||||
_dns_servers.destroy_each(_alloc);
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_server_base::_invalid(Domain const &domain,
|
||||
char const *reason)
|
||||
{
|
||||
if (domain.config().verbose()) {
|
||||
@ -35,13 +61,17 @@ void Dhcp_server::_invalid(Domain &domain,
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** Dhcp_server **
|
||||
*****************/
|
||||
|
||||
Dhcp_server::Dhcp_server(Xml_node const node,
|
||||
Domain &domain,
|
||||
Allocator &alloc,
|
||||
Ipv4_address_prefix const &interface,
|
||||
Domain_tree &domains)
|
||||
:
|
||||
_dns_server(node.attribute_value("dns_server", Ipv4_address())),
|
||||
Dhcp_server_base(node, domain, alloc),
|
||||
_dns_server_from(_init_dns_server_from(node, domains)),
|
||||
_ip_lease_time (_init_ip_lease_time(node)),
|
||||
_ip_first(node.attribute_value("ip_first", Ipv4_address())),
|
||||
@ -75,9 +105,9 @@ Microseconds Dhcp_server::_init_ip_lease_time(Xml_node const node)
|
||||
|
||||
void Dhcp_server::print(Output &output) const
|
||||
{
|
||||
if (_dns_server.valid()) {
|
||||
Genode::print(output, "DNS server ", _dns_server, ", ");
|
||||
}
|
||||
_dns_servers.for_each([&] (Dns_server const &dns_server) {
|
||||
Genode::print(output, "DNS server ", dns_server.ip(), ", ");
|
||||
});
|
||||
try { Genode::print(output, "DNS server from ", _dns_server_from(), ", "); }
|
||||
catch (Pointer<Domain>::Invalid) { }
|
||||
|
||||
@ -88,6 +118,18 @@ void Dhcp_server::print(Output &output) const
|
||||
}
|
||||
|
||||
|
||||
bool Dhcp_server::dns_servers_equal_to_those_of(Dhcp_server const &dhcp_server) const
|
||||
{
|
||||
return _dns_servers.equal_to(dhcp_server._dns_servers);
|
||||
}
|
||||
|
||||
|
||||
Ipv4_config const &Dhcp_server::_resolve_dns_server_from() const
|
||||
{
|
||||
return _dns_server_from().ip_config();
|
||||
}
|
||||
|
||||
|
||||
Ipv4_address Dhcp_server::alloc_ip()
|
||||
{
|
||||
try {
|
||||
@ -117,7 +159,7 @@ void Dhcp_server::free_ip(Ipv4_address const &ip)
|
||||
Pointer<Domain> Dhcp_server::_init_dns_server_from(Genode::Xml_node const node,
|
||||
Domain_tree &domains)
|
||||
{
|
||||
if (_dns_server.valid()) {
|
||||
if (!_dns_servers.empty()) {
|
||||
return Pointer<Domain>();
|
||||
}
|
||||
Domain_name dns_server_from =
|
||||
@ -131,17 +173,9 @@ Pointer<Domain> Dhcp_server::_init_dns_server_from(Genode::Xml_node const node,
|
||||
}
|
||||
|
||||
|
||||
Ipv4_address const &Dhcp_server::dns_server() const
|
||||
{
|
||||
try { return _dns_server_from().ip_config().dns_server; }
|
||||
catch (Pointer<Domain>::Invalid) { }
|
||||
return _dns_server;
|
||||
}
|
||||
|
||||
|
||||
bool Dhcp_server::ready() const
|
||||
{
|
||||
if (_dns_server.valid()) {
|
||||
if (!_dns_servers.empty()) {
|
||||
return true;
|
||||
}
|
||||
try { return _dns_server_from().ip_config().valid; }
|
||||
|
@ -15,10 +15,11 @@
|
||||
#define _DHCP_SERVER_H_
|
||||
|
||||
/* local includes */
|
||||
#include <ipv4_address_prefix.h>
|
||||
#include <bit_allocator_dynamic.h>
|
||||
#include <list.h>
|
||||
#include <pointer.h>
|
||||
#include <dns_server.h>
|
||||
#include <ipv4_config.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/mac_address.h>
|
||||
@ -29,6 +30,7 @@
|
||||
namespace Net {
|
||||
|
||||
class Configuration;
|
||||
class Dhcp_server_base;
|
||||
class Dhcp_server;
|
||||
class Dhcp_allocation;
|
||||
class Dhcp_allocation_tree;
|
||||
@ -41,11 +43,31 @@ namespace Net {
|
||||
}
|
||||
|
||||
|
||||
class Net::Dhcp_server : private Genode::Noncopyable
|
||||
class Net::Dhcp_server_base
|
||||
{
|
||||
protected:
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
Net::List<Dns_server> _dns_servers { };
|
||||
|
||||
void _invalid(Domain const &domain,
|
||||
char const *reason);
|
||||
|
||||
public:
|
||||
|
||||
Dhcp_server_base(Genode::Xml_node const &node,
|
||||
Domain const &domain,
|
||||
Genode::Allocator &alloc);
|
||||
|
||||
~Dhcp_server_base();
|
||||
};
|
||||
|
||||
|
||||
class Net::Dhcp_server : private Genode::Noncopyable,
|
||||
private Net::Dhcp_server_base
|
||||
{
|
||||
private:
|
||||
|
||||
Ipv4_address const _dns_server;
|
||||
Pointer<Domain> const _dns_server_from;
|
||||
Genode::Microseconds const _ip_lease_time;
|
||||
Ipv4_address const _ip_first;
|
||||
@ -54,14 +76,13 @@ class Net::Dhcp_server : private Genode::Noncopyable
|
||||
Genode::uint32_t const _ip_count;
|
||||
Genode::Bit_allocator_dynamic _ip_alloc;
|
||||
|
||||
void _invalid(Domain &domain,
|
||||
char const *reason);
|
||||
|
||||
Genode::Microseconds _init_ip_lease_time(Genode::Xml_node const node);
|
||||
|
||||
Pointer<Domain> _init_dns_server_from(Genode::Xml_node const node,
|
||||
Domain_tree &domains);
|
||||
|
||||
Ipv4_config const &_resolve_dns_server_from() const;
|
||||
|
||||
public:
|
||||
|
||||
enum { DEFAULT_IP_LEASE_TIME_SEC = 3600 };
|
||||
@ -83,6 +104,27 @@ class Net::Dhcp_server : private Genode::Noncopyable
|
||||
|
||||
bool ready() const;
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each_dns_server_ip(FUNC && functor) const
|
||||
{
|
||||
if (_dns_server_from.valid()) {
|
||||
|
||||
_resolve_dns_server_from().for_each_dns_server(
|
||||
[&] (Dns_server const &dns_server) {
|
||||
functor(dns_server.ip());
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
_dns_servers.for_each([&] (Dns_server const &dns_server) {
|
||||
functor(dns_server.ip());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
dns_servers_equal_to_those_of(Dhcp_server const &dhcp_server) const;
|
||||
|
||||
|
||||
/*********
|
||||
** log **
|
||||
@ -95,7 +137,6 @@ class Net::Dhcp_server : private Genode::Noncopyable
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Ipv4_address const &dns_server() const;
|
||||
Domain &dns_server_from() { return _dns_server_from(); }
|
||||
Genode::Microseconds ip_lease_time() const { return _ip_lease_time; }
|
||||
};
|
||||
|
34
repos/os/src/server/nic_router/dns_server.cc
Normal file
34
repos/os/src/server/nic_router/dns_server.cc
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* \brief DNS server entry of a DHCP server or IPv4 config
|
||||
* \author Martin Stein
|
||||
* \date 2020-11-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <dns_server.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Net::Dns_server::Dns_server(Ipv4_address const &ip)
|
||||
:
|
||||
_ip { ip }
|
||||
{
|
||||
if (!_ip.valid()) {
|
||||
throw Invalid { };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Net::Dns_server::equal_to(Dns_server const &server) const
|
||||
{
|
||||
return _ip == server._ip;
|
||||
}
|
50
repos/os/src/server/nic_router/dns_server.h
Normal file
50
repos/os/src/server/nic_router/dns_server.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* \brief DNS server entry of a DHCP server or IPv4 config
|
||||
* \author Martin Stein
|
||||
* \date 2020-11-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _DNS_SERVER_H_
|
||||
#define _DNS_SERVER_H_
|
||||
|
||||
/* local includes */
|
||||
#include <list.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ipv4.h>
|
||||
|
||||
namespace Net { class Dns_server; }
|
||||
|
||||
class Net::Dns_server : private Genode::Noncopyable,
|
||||
public Net::List<Dns_server>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
Net::Ipv4_address const _ip;
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid : Genode::Exception { };
|
||||
|
||||
Dns_server(Net::Ipv4_address const &ip);
|
||||
|
||||
bool equal_to(Dns_server const &server) const;
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Net::Ipv4_address const &ip() const { return _ip; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* _DHCP_SERVER_H_ */
|
@ -47,7 +47,7 @@ void Domain::_log_ip_config() const
|
||||
if (!ip_config.valid &&
|
||||
(ip_config.interface_valid ||
|
||||
ip_config.gateway_valid ||
|
||||
ip_config.dns_server_valid))
|
||||
!ip_config.dns_servers.empty()))
|
||||
{
|
||||
log("[", *this, "] malformed ", _ip_config_dynamic ? "dynamic" :
|
||||
"static", "IP config: ", ip_config);
|
||||
@ -60,7 +60,7 @@ void Domain::_log_ip_config() const
|
||||
}
|
||||
|
||||
|
||||
void Domain::ip_config(Ipv4_config const &new_ip_config)
|
||||
void Domain::_prepare_reconstructing_ip_config()
|
||||
{
|
||||
if (!_ip_config_dynamic) {
|
||||
throw Ip_config_static(); }
|
||||
@ -69,7 +69,7 @@ void Domain::ip_config(Ipv4_config const &new_ip_config)
|
||||
if (ip_config().valid) {
|
||||
|
||||
/* mark IP config invalid */
|
||||
_ip_config.construct();
|
||||
_ip_config.construct(_alloc);
|
||||
|
||||
/* detach all dependent interfaces from old IP config */
|
||||
_interfaces.for_each([&] (Interface &interface) {
|
||||
@ -86,8 +86,11 @@ void Domain::ip_config(Ipv4_config const &new_ip_config)
|
||||
waiter.src().cancel_arp_waiting(waiter);
|
||||
}
|
||||
}
|
||||
/* overwrite old with new IP config */
|
||||
_ip_config.construct(new_ip_config);
|
||||
}
|
||||
|
||||
|
||||
void Domain::_finish_reconstructing_ip_config()
|
||||
{
|
||||
_log_ip_config();
|
||||
|
||||
/* attach all dependent interfaces to new IP config if it is valid */
|
||||
@ -113,20 +116,15 @@ void Domain::ip_config(Ipv4_config const &new_ip_config)
|
||||
|
||||
void Domain::discard_ip_config()
|
||||
{
|
||||
/* install invalid IP config */
|
||||
Ipv4_config const new_ip_config;
|
||||
ip_config(new_ip_config);
|
||||
_reconstruct_ip_config([&] (Reconstructible<Ipv4_config> &ip_config) {
|
||||
ip_config.construct(_alloc); });
|
||||
}
|
||||
|
||||
|
||||
void Domain::ip_config(Ipv4_address ip,
|
||||
Ipv4_address subnet_mask,
|
||||
Ipv4_address gateway,
|
||||
Ipv4_address dns_server)
|
||||
void Domain::ip_config_from_dhcp_ack(Dhcp_packet &dhcp_ack)
|
||||
{
|
||||
Ipv4_config const new_ip_config(Ipv4_address_prefix(ip, subnet_mask),
|
||||
gateway, dns_server);
|
||||
ip_config(new_ip_config);
|
||||
_reconstruct_ip_config([&] (Reconstructible<Ipv4_config> &ip_config) {
|
||||
ip_config.construct(dhcp_ack, _alloc); });
|
||||
}
|
||||
|
||||
|
||||
@ -139,7 +137,8 @@ void Domain::try_reuse_ip_config(Domain const &domain)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ip_config(domain.ip_config());
|
||||
_reconstruct_ip_config([&] (Reconstructible<Ipv4_config> &ip_config) {
|
||||
ip_config.construct(domain.ip_config(), _alloc); });
|
||||
}
|
||||
|
||||
|
||||
@ -198,18 +197,20 @@ void Domain::print(Output &output) const
|
||||
|
||||
Domain::Domain(Configuration &config, Xml_node const node, Allocator &alloc)
|
||||
:
|
||||
Domain_base(node), Avl_string_base(_name.string()), _config(config),
|
||||
_node(node), _alloc(alloc),
|
||||
_ip_config(_node.attribute_value("interface", Ipv4_address_prefix()),
|
||||
_node.attribute_value("gateway", Ipv4_address()),
|
||||
Ipv4_address()),
|
||||
_verbose_packets(_node.attribute_value("verbose_packets",
|
||||
_config.verbose_packets())),
|
||||
_verbose_packet_drop(_node.attribute_value("verbose_packet_drop",
|
||||
_config.verbose_packet_drop())),
|
||||
_icmp_echo_server(_node.attribute_value("icmp_echo_server",
|
||||
_config.icmp_echo_server())),
|
||||
_label(_node.attribute_value("label", String<160>()).string())
|
||||
Domain_base { node },
|
||||
Avl_string_base { Domain_base::_name.string() },
|
||||
_config { config },
|
||||
_node { node },
|
||||
_alloc { alloc },
|
||||
_ip_config { node, alloc },
|
||||
_verbose_packets { node.attribute_value("verbose_packets",
|
||||
config.verbose_packets()) },
|
||||
_verbose_packet_drop { node.attribute_value("verbose_packet_drop",
|
||||
config.verbose_packet_drop()) },
|
||||
_icmp_echo_server { node.attribute_value("icmp_echo_server",
|
||||
config.icmp_echo_server()) },
|
||||
_label { node.attribute_value("label",
|
||||
String<160>()).string() }
|
||||
{
|
||||
_log_ip_config();
|
||||
|
||||
@ -375,7 +376,11 @@ void Domain::report(Xml_generator &xml)
|
||||
if (_config.report().config()) {
|
||||
xml.attribute("ipv4", String<19>(ip_config().interface));
|
||||
xml.attribute("gw", String<16>(ip_config().gateway));
|
||||
xml.attribute("dns", String<16>(ip_config().dns_server));
|
||||
ip_config().for_each_dns_server([&] (Dns_server const &dns_server) {
|
||||
xml.node("dns", [&] () {
|
||||
xml.attribute("ip", String<16>(dns_server.ip()));
|
||||
});
|
||||
});
|
||||
empty = false;
|
||||
}
|
||||
if (_config.report().stats()) {
|
||||
|
@ -142,6 +142,18 @@ class Net::Domain : public Domain_base,
|
||||
|
||||
void _log_ip_config() const;
|
||||
|
||||
void _prepare_reconstructing_ip_config();
|
||||
|
||||
void _finish_reconstructing_ip_config();
|
||||
|
||||
template <typename FUNC>
|
||||
void _reconstruct_ip_config(FUNC && functor)
|
||||
{
|
||||
_prepare_reconstructing_ip_config();
|
||||
functor(_ip_config);
|
||||
_finish_reconstructing_ip_config();
|
||||
}
|
||||
|
||||
void __FIXME__dissolve_foreign_arp_waiters();
|
||||
|
||||
public:
|
||||
@ -162,12 +174,7 @@ class Net::Domain : public Domain_base,
|
||||
|
||||
Ipv4_address const &next_hop(Ipv4_address const &ip) const;
|
||||
|
||||
void ip_config(Ipv4_config const &ip_config);
|
||||
|
||||
void ip_config(Ipv4_address ip,
|
||||
Ipv4_address subnet_mask,
|
||||
Ipv4_address gateway,
|
||||
Ipv4_address dns_server);
|
||||
void ip_config_from_dhcp_ack(Dhcp_packet &dhcp_ack);
|
||||
|
||||
void discard_ip_config();
|
||||
|
||||
|
@ -34,6 +34,7 @@ using Genode::Exception;
|
||||
using Genode::Out_of_ram;
|
||||
using Genode::Out_of_caps;
|
||||
using Genode::Constructible;
|
||||
using Genode::Reconstructible;
|
||||
using Genode::Signal_context_capability;
|
||||
using Genode::Signal_transmitter;
|
||||
|
||||
@ -644,8 +645,9 @@ void Interface::_send_dhcp_reply(Dhcp_server const &dhcp_srv,
|
||||
dhcp_opts.append_option<Dhcp_packet::Ip_lease_time>(dhcp_srv.ip_lease_time().value / 1000 / 1000);
|
||||
dhcp_opts.append_option<Dhcp_packet::Subnet_mask>(local_intf.subnet_mask());
|
||||
dhcp_opts.append_option<Dhcp_packet::Router_ipv4>(local_intf.address);
|
||||
if (dhcp_srv.dns_server().valid()) {
|
||||
dhcp_opts.append_option<Dhcp_packet::Dns_server_ipv4>(dhcp_srv.dns_server()); }
|
||||
dhcp_srv.for_each_dns_server_ip([&] (Ipv4_address const &dns_server_ip) {
|
||||
dhcp_opts.append_option<Dhcp_packet::Dns_server_ipv4>(dns_server_ip);
|
||||
});
|
||||
dhcp_opts.append_option<Dhcp_packet::Broadcast_addr>(local_intf.broadcast_address());
|
||||
dhcp_opts.append_option<Dhcp_packet::Options_end>();
|
||||
|
||||
@ -1865,7 +1867,7 @@ void Interface::_update_dhcp_allocations(Domain &old_domain,
|
||||
try {
|
||||
Dhcp_server &old_dhcp_srv = old_domain.dhcp_server();
|
||||
Dhcp_server &new_dhcp_srv = new_domain.dhcp_server();
|
||||
if (old_dhcp_srv.dns_server() != new_dhcp_srv.dns_server()) {
|
||||
if (!old_dhcp_srv.dns_servers_equal_to_those_of(new_dhcp_srv)) {
|
||||
throw Pointer<Dhcp_server>::Invalid();
|
||||
}
|
||||
if (old_dhcp_srv.ip_lease_time().value !=
|
||||
|
@ -20,19 +20,79 @@
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
|
||||
Ipv4_config::Ipv4_config(Ipv4_address_prefix interface,
|
||||
Ipv4_address gateway,
|
||||
Ipv4_address dns_server)
|
||||
|
||||
Ipv4_config::Ipv4_config(Allocator &alloc)
|
||||
:
|
||||
interface(interface), gateway(gateway), dns_server(dns_server)
|
||||
alloc { alloc },
|
||||
interface { },
|
||||
gateway { }
|
||||
{ }
|
||||
|
||||
|
||||
Ipv4_config::Ipv4_config(Xml_node const &domain_node,
|
||||
Allocator &alloc)
|
||||
:
|
||||
alloc { alloc },
|
||||
interface { domain_node.attribute_value("interface", Ipv4_address_prefix()) },
|
||||
gateway { domain_node.attribute_value("gateway", Ipv4_address()) }
|
||||
{ }
|
||||
|
||||
|
||||
Ipv4_config::Ipv4_config(Ipv4_config const &ip_config,
|
||||
Allocator &alloc)
|
||||
:
|
||||
alloc { alloc },
|
||||
interface { ip_config.interface },
|
||||
gateway { ip_config.gateway }
|
||||
{
|
||||
ip_config.dns_servers.for_each([&] (Dns_server const &dns_server) {
|
||||
dns_servers.insert_as_tail(
|
||||
*new (alloc) Dns_server(dns_server.ip()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Ipv4_config::Ipv4_config(Dhcp_packet &dhcp_ack,
|
||||
Allocator &alloc)
|
||||
:
|
||||
alloc { alloc },
|
||||
interface { dhcp_ack.yiaddr(),
|
||||
dhcp_ipv4_option<Dhcp_packet::Subnet_mask>(dhcp_ack) },
|
||||
gateway { dhcp_ipv4_option<Dhcp_packet::Router_ipv4>(dhcp_ack) }
|
||||
{
|
||||
dhcp_ack.for_each_option([&] (Dhcp_packet::Option const &opt)
|
||||
{
|
||||
if (opt.code() != Dhcp_packet::Option::Code::DNS_SERVER) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dns_servers.insert_as_tail(*new (alloc)
|
||||
Dns_server(
|
||||
reinterpret_cast<Dhcp_packet::Dns_server_ipv4 const *>(&opt)->value()));
|
||||
}
|
||||
catch (Dns_server::Invalid) { }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Ipv4_config::~Ipv4_config()
|
||||
{
|
||||
dns_servers.destroy_each(alloc);
|
||||
}
|
||||
|
||||
|
||||
void Ipv4_config::print(Output &output) const
|
||||
{
|
||||
if (valid) {
|
||||
|
||||
Genode::print(output, "interface ", interface, ", gateway ", gateway,
|
||||
", DNS server ", dns_server, " P2P ", point_to_point); }
|
||||
else {
|
||||
Genode::print(output, "none"); }
|
||||
" P2P ", point_to_point);
|
||||
|
||||
for_each_dns_server([&] (Dns_server const &dns_server) {
|
||||
Genode::print(output, ", DNS server ", dns_server.ip()); });
|
||||
|
||||
} else {
|
||||
|
||||
Genode::print(output, "none");
|
||||
}
|
||||
}
|
||||
|
@ -16,36 +16,56 @@
|
||||
|
||||
/* local includes */
|
||||
#include <ipv4_address_prefix.h>
|
||||
#include <dhcp.h>
|
||||
#include <dns_server.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/xml_node.h>
|
||||
|
||||
namespace Net { class Ipv4_config; }
|
||||
|
||||
struct Net::Ipv4_config
|
||||
{
|
||||
Ipv4_address_prefix const interface { };
|
||||
Genode::Allocator &alloc;
|
||||
Ipv4_address_prefix const interface;
|
||||
bool const interface_valid { interface.valid() };
|
||||
Ipv4_address const gateway { };
|
||||
Ipv4_address const gateway;
|
||||
bool const gateway_valid { gateway.valid() };
|
||||
bool const point_to_point { gateway_valid &&
|
||||
interface_valid &&
|
||||
interface.prefix == 32 };
|
||||
Ipv4_address const dns_server { };
|
||||
bool const dns_server_valid { dns_server.valid() };
|
||||
Net::List<Dns_server> dns_servers { };
|
||||
bool const valid { point_to_point ||
|
||||
(interface_valid &&
|
||||
(!gateway_valid ||
|
||||
interface.prefix_matches(gateway))) };
|
||||
|
||||
Ipv4_config(Ipv4_address_prefix interface,
|
||||
Ipv4_address gateway,
|
||||
Ipv4_address dns_server);
|
||||
Ipv4_config(Net::Dhcp_packet &dhcp_ack,
|
||||
Genode::Allocator &alloc);
|
||||
|
||||
Ipv4_config() { }
|
||||
Ipv4_config(Genode::Xml_node const &domain_node,
|
||||
Genode::Allocator &alloc);
|
||||
|
||||
Ipv4_config(Ipv4_config const &ip_config,
|
||||
Genode::Allocator &alloc);
|
||||
|
||||
Ipv4_config(Genode::Allocator &alloc);
|
||||
|
||||
~Ipv4_config();
|
||||
|
||||
bool operator != (Ipv4_config const &other) const
|
||||
{
|
||||
return interface != other.interface ||
|
||||
gateway != other.gateway ||
|
||||
dns_server != other.dns_server;
|
||||
!dns_servers.equal_to(other.dns_servers);
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each_dns_server(FUNC && functor) const
|
||||
{
|
||||
dns_servers.for_each([&] (Dns_server const &dns_server) {
|
||||
functor(dns_server);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -37,6 +37,17 @@ struct Net::List : Genode::List<LT>
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each(FUNC && functor) const
|
||||
{
|
||||
for (LT const *elem = Base::first(); elem; )
|
||||
{
|
||||
LT const *const next = elem->Base::Element::next();
|
||||
functor(*elem);
|
||||
elem = next;
|
||||
}
|
||||
}
|
||||
|
||||
void destroy_each(Genode::Deallocator &dealloc)
|
||||
{
|
||||
while (LT *elem = Base::first()) {
|
||||
@ -44,6 +55,48 @@ struct Net::List : Genode::List<LT>
|
||||
destroy(dealloc, elem);
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return Base::first() == nullptr;
|
||||
}
|
||||
|
||||
void insert_as_tail(LT const &le)
|
||||
{
|
||||
LT *elem { Base::first() };
|
||||
if (elem) {
|
||||
while (elem->Base::Element::next()) {
|
||||
elem = elem->Base::Element::next();
|
||||
}
|
||||
}
|
||||
Base::insert(&le, elem);
|
||||
}
|
||||
|
||||
bool equal_to(List<LT> const &list) const
|
||||
{
|
||||
LT const *curr_elem_1 { Base::first() };
|
||||
LT const *curr_elem_2 { list.Base::first() };
|
||||
while (true) {
|
||||
|
||||
if (curr_elem_1 == nullptr) {
|
||||
return curr_elem_2 == nullptr;
|
||||
}
|
||||
if (curr_elem_2 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
LT const *const next_elem_1 {
|
||||
curr_elem_1->List<LT>::Element::next() };
|
||||
|
||||
LT const *const next_elem_2 {
|
||||
curr_elem_2->List<LT>::Element::next() };
|
||||
|
||||
if (!curr_elem_1->equal_to(*curr_elem_2)) {
|
||||
return false;
|
||||
}
|
||||
curr_elem_1 = next_elem_1;
|
||||
curr_elem_2 = next_elem_2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _LIST_H_ */
|
||||
|
@ -7,7 +7,7 @@ SRC_CC += component.cc port_allocator.cc forward_rule.cc
|
||||
SRC_CC += nat_rule.cc main.cc ipv4_config.cc
|
||||
SRC_CC += uplink.cc interface.cc arp_cache.cc configuration.cc
|
||||
SRC_CC += domain.cc l3_protocol.cc direct_rule.cc link.cc
|
||||
SRC_CC += transport_rule.cc permit_rule.cc
|
||||
SRC_CC += transport_rule.cc permit_rule.cc dns_server.cc
|
||||
SRC_CC += dhcp_client.cc dhcp_server.cc report.cc xml_node.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
||||
|
153
repos/os/src/test/nic_router_dhcp/README
Normal file
153
repos/os/src/test/nic_router_dhcp/README
Normal file
@ -0,0 +1,153 @@
|
||||
|
||||
|
||||
====================
|
||||
NIC-router-DHCP test
|
||||
====================
|
||||
|
||||
Martin Stein
|
||||
|
||||
|
||||
|
||||
The NIC-router-DHCP test is meant to test and demonstrate the functionalities
|
||||
of the NIC router that correspond to the management of networks through DHCP.
|
||||
This comprises the abilities of the router to basically act as DHCP client and
|
||||
DHCP server, to recognize and propagate additional information through DHCP,
|
||||
and to react to changes in DHCP environments.
|
||||
|
||||
|
||||
Unmanaged test scenario
|
||||
#######################
|
||||
|
||||
The test is integrated by the run scripts
|
||||
'os/run/nic_router_dhcp_unmanaged.run' and 'os/run/nic_router_managed.run'.
|
||||
The "unmanaged" variant creates the following setup:
|
||||
|
||||
|
||||
+--------------------------------------+ Received
|
||||
| Test Client | DHCP info
|
||||
| (DHCP Client 2) |----------------> Serial Output
|
||||
+--------------------------------------+
|
||||
|
|
||||
| NIC session
|
||||
|
|
||||
v
|
||||
+---------------+-------------------+--+
|
||||
| NIC Router 2 | Domain Downlink 2 | |
|
||||
| | (DHCP Server 2) | |
|
||||
| +-------------------+ |
|
||||
| ^ |
|
||||
| | Built-in |
|
||||
| | DHCP info |
|
||||
| | forwarding |
|
||||
| | |
|
||||
| | |
|
||||
| +-------------------+ |
|
||||
| | Domain Uplink | |
|
||||
| | (DHCP Client 1) | |
|
||||
+---------------+-------------------+--+
|
||||
|
|
||||
| NIC session
|
||||
|
|
||||
v
|
||||
+---------------+-------------------+--+ Reconfigure
|
||||
| NIC Router 1 | Domain Downlink 1 | | DHCP server +--------------+
|
||||
| | (DHCP Server 1) | |<----------------| Dynamic ROM |
|
||||
| +-------------------+ | +--------------+
|
||||
| |
|
||||
+--------------------------------------+
|
||||
|
||||
|
||||
Throughout the test, the dynamic ROM changes the setup of DHCP Server 1
|
||||
multiple times. This results also in changes of the additional information
|
||||
propagated via DHCP. As a reaction to the changes, the link state of domain
|
||||
Downlink 1 does a "down-up" sequence in order to advise all connected DHCP
|
||||
clients to reset. DHCP Client 1 does so and receives the updated information
|
||||
for its domain Uplink.
|
||||
|
||||
But the domain Uplink is also watched by Downlink 2 for additional DHCP
|
||||
information through the router-internal forwarding mechanism (currently only
|
||||
DNS server addresses, see attribute 'dns_server_from'). Therefore, the reset of
|
||||
DHCP Client 1 also causes the link state of Downlink 2 to go "down" until DHCP
|
||||
Client 1 finishes re-requesting DHCP. This causes DHCP Client 2 in the test
|
||||
client (os/src/test/nic_router_dhcp/manager) to reset and re-request both the
|
||||
basic DHCP info (originating from DHCP Server 2) and the updated additional
|
||||
DHCP info (originating from DHCP Server 1) as soon as Downlink 2 is "up" again.
|
||||
|
||||
The test terminates successfully when the test client has printed a certain
|
||||
sequence of successively received DHCP setups to the serial output. It fails,
|
||||
at the other hand, when the expected output wasn't observed for a certain time.
|
||||
|
||||
|
||||
Managed test scenario
|
||||
#####################
|
||||
|
||||
The "managed" variant of test differs only in one detail from the "unmanaged"
|
||||
variant: It doesn't use the router-internal mechanism for forwarding additional
|
||||
DHCP information from the domain Uplink to domain Downlink 2. Instead, it
|
||||
achieves the forwarding through an additional manager component
|
||||
(os/src/test/nic_router_dhcp/manager):
|
||||
|
||||
|
||||
+--------------------------------------+ Received
|
||||
| Test Client | DHCP info
|
||||
| (DHCP Client 2) |----------------> Serial Output
|
||||
+--------------------------------------+
|
||||
|
|
||||
| NIC session
|
||||
|
|
||||
v Reconfigure
|
||||
+---------------+-------------------+--+ DHCP server +--------------+
|
||||
| NIC Router 2 | Domain Downlink 2 | | +---+ | Test Manager |
|
||||
| | (DHCP Server 2) | |<-----| R |------| |
|
||||
| +-------------------+ | | e | | |
|
||||
| | | p | | |
|
||||
| | | o | | |
|
||||
| | | r | | |
|
||||
| | | t | | |
|
||||
| | | | | |
|
||||
| | | R | | |
|
||||
| +-------------------+ | | O | | |
|
||||
| | Domain Uplink | |------| M |----->| |
|
||||
| | (DHCP Client 1) | | +---+ | |
|
||||
+---------------+-------------------+--+ Observe DHCP +--------------+
|
||||
| client state
|
||||
| NIC session
|
||||
|
|
||||
v
|
||||
+---------------+-------------------+--+ Reconfigure
|
||||
| NIC Router 1 | Domain Downlink 1 | | DHCP server +--------------+
|
||||
| | (DHCP Server 1) | |<----------------| Dynamic ROM |
|
||||
| +-------------------+ | +--------------+
|
||||
| |
|
||||
+--------------------------------------+
|
||||
|
||||
|
||||
The manager initially writes out a router configuration using a Report session.
|
||||
The configuration is received by NIC Router 2 through a ROM session. The
|
||||
mediator between the managers Report and the routers ROM session is a Report
|
||||
ROM server. The initial router configuration written by the manager is
|
||||
basically the same as the static NIC-Router-2 configuration in the "unmanaged"
|
||||
scenario with the small addition that it causes the router to report its state.
|
||||
|
||||
Now, each time that the DCHP info of domain Uplink changes, NIC Router 2
|
||||
generates a new state report reflecting the updated DHCP info. The manager
|
||||
receives the state update through its ROM session with the Report ROM server.
|
||||
Now, the manager checks whether the additional DHCP info of domain Uplink
|
||||
was affected by the update. If so, it generates a new router configuration
|
||||
injecting the new additional DHCP info into DHCP Server 2.
|
||||
|
||||
Note that the test manager takes care not to create endless feedback-response
|
||||
loops by re-configuring the router only when the interesting part of the router
|
||||
state changes (the additional DHCP info of domain Uplink). The test manager
|
||||
reduces re-configuration even further by doing it only when the DHCP info at
|
||||
domain Uplink became valid. This means that it skips the short periods where
|
||||
DHCP Client 1 is waiting for the re-request to finish and no DHCP info is
|
||||
available. It's fine for DHCP Server 2 to stay with the outdated information
|
||||
during this time as no basic DHCP information is affected and routing via
|
||||
Uplink remains blocked until DHCP finished anyway.
|
||||
|
||||
The rest of the process remains the same as in the "unmanaged" variant. The
|
||||
"managed" approach has the benefit that the forwarding of additional DHCP info
|
||||
can be adapted to scenarios with special requirements. For instance, one might
|
||||
want to filter information in order to restrict or protect the client behind
|
||||
the router.
|
281
repos/os/src/test/nic_router_dhcp/client/dhcp_client.cc
Normal file
281
repos/os/src/test/nic_router_dhcp/client/dhcp_client.cc
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* \brief DHCP client state model
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <nic.h>
|
||||
#include <dhcp_client.h>
|
||||
#include <ipv4_config.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/xml_node.h>
|
||||
|
||||
enum { PKT_SIZE = 1024 };
|
||||
|
||||
struct Send_buffer_too_small : Genode::Exception { };
|
||||
struct Bad_send_dhcp_args : Genode::Exception { };
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
using Message_type = Dhcp_packet::Message_type;
|
||||
using Dhcp_options = Dhcp_packet::Options_aggregator<Size_guard>;
|
||||
|
||||
|
||||
/***************
|
||||
** Utilities **
|
||||
***************/
|
||||
|
||||
void append_param_req_list(Dhcp_options &dhcp_opts)
|
||||
{
|
||||
dhcp_opts.append_param_req_list([&] (Dhcp_options::Parameter_request_list_data &data) {
|
||||
data.append_param_req<Dhcp_packet::Message_type_option>();
|
||||
data.append_param_req<Dhcp_packet::Server_ipv4>();
|
||||
data.append_param_req<Dhcp_packet::Ip_lease_time>();
|
||||
data.append_param_req<Dhcp_packet::Dns_server_ipv4>();
|
||||
data.append_param_req<Dhcp_packet::Subnet_mask>();
|
||||
data.append_param_req<Dhcp_packet::Router_ipv4>();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** Dhcp_client **
|
||||
*****************/
|
||||
|
||||
Dhcp_client::Dhcp_client(Genode::Allocator &alloc,
|
||||
Timer::Connection &timer,
|
||||
Nic &nic,
|
||||
Dhcp_client_handler &handler)
|
||||
:
|
||||
_alloc (alloc),
|
||||
_timeout (timer, *this, &Dhcp_client::_handle_timeout),
|
||||
_nic (nic),
|
||||
_handler (handler)
|
||||
{
|
||||
_discover();
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_discover()
|
||||
{
|
||||
_set_state(State::SELECT, _discover_timeout);
|
||||
_send(Message_type::DISCOVER, Ipv4_address(), Ipv4_address(),
|
||||
Ipv4_address());
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_rerequest(State next_state)
|
||||
{
|
||||
_set_state(next_state, _rerequest_timeout(2));
|
||||
Ipv4_address const client_ip = _handler.ip_config().interface.address;
|
||||
_send(Message_type::REQUEST, client_ip, Ipv4_address(), client_ip);
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_set_state(State state, Microseconds timeout)
|
||||
{
|
||||
_state = state;
|
||||
_timeout.schedule(timeout);
|
||||
}
|
||||
|
||||
|
||||
Microseconds Dhcp_client::_rerequest_timeout(unsigned lease_time_div_log2)
|
||||
{
|
||||
/* FIXME limit the time because of shortcomings in timeout framework */
|
||||
enum { MAX_TIMEOUT_SEC = 3600 };
|
||||
uint64_t timeout_sec = _lease_time_sec >> lease_time_div_log2;
|
||||
|
||||
if (timeout_sec > MAX_TIMEOUT_SEC) {
|
||||
timeout_sec = MAX_TIMEOUT_SEC;
|
||||
warning("Had to prune the state timeout of DHCP client");
|
||||
}
|
||||
return Microseconds(timeout_sec * 1000 * 1000);
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_handle_timeout(Duration)
|
||||
{
|
||||
switch (_state) {
|
||||
case State::BOUND: _rerequest(State::RENEW); break;
|
||||
case State::RENEW: _rerequest(State::REBIND); break;
|
||||
default: _discover();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::handle_eth(Ethernet_frame ð, Size_guard &size_guard)
|
||||
{
|
||||
if (eth.dst() != _nic.mac() &&
|
||||
eth.dst() != Mac_address(0xff))
|
||||
{
|
||||
throw Drop_packet_inform("DHCP client expects Ethernet targeting the router");
|
||||
}
|
||||
Ipv4_packet &ip = eth.data<Ipv4_packet>(size_guard);
|
||||
if (ip.protocol() != Ipv4_packet::Protocol::UDP) {
|
||||
throw Drop_packet_inform("DHCP client expects UDP packet"); }
|
||||
|
||||
Udp_packet &udp = ip.data<Udp_packet>(size_guard);
|
||||
if (!Dhcp_packet::is_dhcp(&udp)) {
|
||||
throw Drop_packet_inform("DHCP client expects DHCP packet"); }
|
||||
|
||||
Dhcp_packet &dhcp = udp.data<Dhcp_packet>(size_guard);
|
||||
if (dhcp.op() != Dhcp_packet::REPLY) {
|
||||
throw Drop_packet_inform("DHCP client expects DHCP reply"); }
|
||||
|
||||
if (dhcp.client_mac() != _nic.mac()) {
|
||||
throw Drop_packet_inform("DHCP client expects DHCP targeting the router"); }
|
||||
|
||||
try { _handle_dhcp_reply(dhcp); }
|
||||
catch (Dhcp_packet::Option_not_found) {
|
||||
throw Drop_packet_inform("DHCP client misses DHCP option"); }
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_handle_dhcp_reply(Dhcp_packet &dhcp)
|
||||
{
|
||||
Message_type const msg_type =
|
||||
dhcp.option<Dhcp_packet::Message_type_option>().value();
|
||||
|
||||
switch (_state) {
|
||||
case State::SELECT:
|
||||
|
||||
if (msg_type != Message_type::OFFER) {
|
||||
throw Drop_packet_inform("DHCP client expects an offer");
|
||||
}
|
||||
_set_state(State::REQUEST, _request_timeout);
|
||||
_send(Message_type::REQUEST, Ipv4_address(),
|
||||
dhcp.option<Dhcp_packet::Server_ipv4>().value(),
|
||||
dhcp.yiaddr());
|
||||
break;
|
||||
|
||||
case State::REQUEST:
|
||||
{
|
||||
if (msg_type != Message_type::ACK) {
|
||||
throw Drop_packet_inform("DHCP client expects an acknowledgement");
|
||||
}
|
||||
_lease_time_sec = dhcp.option<Dhcp_packet::Ip_lease_time>().value();
|
||||
_set_state(State::BOUND, _rerequest_timeout(1));
|
||||
|
||||
log("DHCP request completed:");
|
||||
log(" IP lease time: ", _lease_time_sec, " seconds");
|
||||
log(" Interface: ", Ipv4_address_prefix(dhcp.yiaddr(), dhcp.option<Dhcp_packet::Subnet_mask>().value()));
|
||||
log(" Router: ", dhcp.option<Dhcp_packet::Router_ipv4>().value());
|
||||
|
||||
Ipv4_address dns_server { };
|
||||
unsigned idx { 1 };
|
||||
dhcp.for_each_option([&] (Dhcp_packet::Option const &opt)
|
||||
{
|
||||
if (!dns_server.valid()) {
|
||||
dns_server = reinterpret_cast<Dhcp_packet::Dns_server_ipv4 const *>(&opt)->value();
|
||||
}
|
||||
if (opt.code() != Dhcp_packet::Option::Code::DNS_SERVER) {
|
||||
return;
|
||||
}
|
||||
log(" DNS server #", idx++, ": ", reinterpret_cast<Dhcp_packet::Dns_server_ipv4 const *>(&opt)->value());
|
||||
});
|
||||
Ipv4_config ip_config(
|
||||
Ipv4_address_prefix(
|
||||
dhcp.yiaddr(),
|
||||
dhcp.option<Dhcp_packet::Subnet_mask>().value()),
|
||||
dhcp.option<Dhcp_packet::Router_ipv4>().value(),
|
||||
dns_server);
|
||||
|
||||
_handler.ip_config(ip_config);
|
||||
break;
|
||||
}
|
||||
case State::RENEW:
|
||||
case State::REBIND:
|
||||
|
||||
if (msg_type != Message_type::ACK) {
|
||||
throw Drop_packet_inform("DHCP client expects an acknowledgement");
|
||||
}
|
||||
_set_state(State::BOUND, _rerequest_timeout(1));
|
||||
_lease_time_sec = dhcp.option<Dhcp_packet::Ip_lease_time>().value();
|
||||
break;
|
||||
|
||||
default: throw Drop_packet_inform("DHCP client doesn't expect a packet");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_send(Message_type msg_type,
|
||||
Ipv4_address client_ip,
|
||||
Ipv4_address server_ip,
|
||||
Ipv4_address requested_ip)
|
||||
{
|
||||
_nic.send(PKT_SIZE, [&] (void *pkt_base, Size_guard &size_guard) {
|
||||
|
||||
/* create ETH header of the request */
|
||||
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
||||
eth.dst(Mac_address(0xff));
|
||||
eth.src(_nic.mac());
|
||||
eth.type(Ethernet_frame::Type::IPV4);
|
||||
|
||||
/* create IP header of the request */
|
||||
enum { IPV4_TIME_TO_LIVE = 64 };
|
||||
size_t const ip_off = size_guard.head_size();
|
||||
Ipv4_packet &ip = eth.construct_at_data<Ipv4_packet>(size_guard);
|
||||
ip.header_length(sizeof(Ipv4_packet) / 4);
|
||||
ip.version(4);
|
||||
ip.time_to_live(IPV4_TIME_TO_LIVE);
|
||||
ip.protocol(Ipv4_packet::Protocol::UDP);
|
||||
ip.src(client_ip);
|
||||
ip.dst(Ipv4_address(0xff));
|
||||
|
||||
/* create UDP header of the request */
|
||||
size_t const udp_off = size_guard.head_size();
|
||||
Udp_packet &udp = ip.construct_at_data<Udp_packet>(size_guard);
|
||||
udp.src_port(Port(Dhcp_packet::BOOTPC));
|
||||
udp.dst_port(Port(Dhcp_packet::BOOTPS));
|
||||
|
||||
/* create mandatory DHCP fields of the request */
|
||||
size_t const dhcp_off = size_guard.head_size();
|
||||
Dhcp_packet &dhcp = udp.construct_at_data<Dhcp_packet>(size_guard);
|
||||
dhcp.op(Dhcp_packet::REQUEST);
|
||||
dhcp.htype(Dhcp_packet::Htype::ETH);
|
||||
dhcp.hlen(sizeof(Mac_address));
|
||||
dhcp.ciaddr(client_ip);
|
||||
dhcp.client_mac(_nic.mac());
|
||||
dhcp.default_magic_cookie();
|
||||
|
||||
/* append DHCP option fields to the request */
|
||||
Dhcp_options dhcp_opts(dhcp, size_guard);
|
||||
dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type);
|
||||
switch (msg_type) {
|
||||
case Message_type::DISCOVER:
|
||||
append_param_req_list(dhcp_opts);
|
||||
dhcp_opts.append_option<Dhcp_packet::Client_id>(_nic.mac());
|
||||
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
|
||||
break;
|
||||
|
||||
case Message_type::REQUEST:
|
||||
append_param_req_list(dhcp_opts);
|
||||
dhcp_opts.append_option<Dhcp_packet::Client_id>(_nic.mac());
|
||||
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
|
||||
if (_state == State::REQUEST) {
|
||||
dhcp_opts.append_option<Dhcp_packet::Requested_addr>(requested_ip);
|
||||
dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(server_ip);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Bad_send_dhcp_args();
|
||||
}
|
||||
dhcp_opts.append_option<Dhcp_packet::Options_end>();
|
||||
|
||||
/* fill in header values that need the packet to be complete already */
|
||||
udp.length(size_guard.head_size() - udp_off);
|
||||
udp.update_checksum(ip.src(), ip.dst());
|
||||
ip.total_length(size_guard.head_size() - ip_off);
|
||||
ip.update_checksum();
|
||||
});
|
||||
}
|
105
repos/os/src/test/nic_router_dhcp/client/dhcp_client.h
Normal file
105
repos/os/src/test/nic_router_dhcp/client/dhcp_client.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* \brief DHCP client state model
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _DHCP_CLIENT_H_
|
||||
#define _DHCP_CLIENT_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/dhcp.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
namespace Net {
|
||||
|
||||
/* external definition */
|
||||
class Nic_peer;
|
||||
class Ipv4_config;
|
||||
class Ethernet_frame;
|
||||
|
||||
/* local definition */
|
||||
class Dhcp_client;
|
||||
class Dhcp_client_handler;
|
||||
class Drop_packet_inform;
|
||||
}
|
||||
|
||||
|
||||
struct Net::Drop_packet_inform : Genode::Exception
|
||||
{
|
||||
char const *msg;
|
||||
|
||||
Drop_packet_inform(char const *msg) : msg(msg) { }
|
||||
};
|
||||
|
||||
|
||||
class Net::Dhcp_client_handler
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void ip_config(Ipv4_config const &ip_config) = 0;
|
||||
|
||||
virtual Ipv4_config const &ip_config() const = 0;
|
||||
|
||||
virtual ~Dhcp_client_handler() { }
|
||||
};
|
||||
|
||||
|
||||
class Net::Dhcp_client
|
||||
{
|
||||
private:
|
||||
|
||||
enum class State
|
||||
{
|
||||
INIT = 0, SELECT = 1, REQUEST = 2, BOUND = 3, RENEW = 4, REBIND = 5
|
||||
};
|
||||
|
||||
enum { DISCOVER_TIMEOUT_SEC = 2 };
|
||||
enum { REQUEST_TIMEOUT_SEC = 2 };
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
State _state { State::INIT };
|
||||
Timer::One_shot_timeout<Dhcp_client> _timeout;
|
||||
unsigned long _lease_time_sec = 0;
|
||||
Genode::Microseconds const _discover_timeout { (Genode::uint64_t)DISCOVER_TIMEOUT_SEC * 1000 * 1000 };
|
||||
Genode::Microseconds const _request_timeout { (Genode::uint64_t)REQUEST_TIMEOUT_SEC * 1000 * 1000 };
|
||||
Nic &_nic;
|
||||
Dhcp_client_handler &_handler;
|
||||
|
||||
void _handle_dhcp_reply(Dhcp_packet &dhcp);
|
||||
|
||||
void _handle_timeout(Genode::Duration);
|
||||
|
||||
void _rerequest(State next_state);
|
||||
|
||||
Genode::Microseconds _rerequest_timeout(unsigned lease_time_div_log2);
|
||||
|
||||
void _set_state(State state, Genode::Microseconds timeout);
|
||||
|
||||
void _send(Dhcp_packet::Message_type msg_type,
|
||||
Ipv4_address client_ip,
|
||||
Ipv4_address server_ip,
|
||||
Ipv4_address requested_ip);
|
||||
|
||||
void _discover();
|
||||
|
||||
public:
|
||||
|
||||
Dhcp_client(Genode::Allocator &alloc,
|
||||
Timer::Connection &timer,
|
||||
Nic &nic,
|
||||
Dhcp_client_handler &handler);
|
||||
|
||||
void handle_eth(Ethernet_frame ð,
|
||||
Size_guard &size_guard);
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DHCP_CLIENT_H_ */
|
103
repos/os/src/test/nic_router_dhcp/client/ipv4_address_prefix.cc
Normal file
103
repos/os/src/test/nic_router_dhcp/client/ipv4_address_prefix.cc
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* \brief Ipv4 address combined with a subnet prefix length
|
||||
* \author Martin Stein
|
||||
* \date 2017-10-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <ipv4_address_prefix.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
|
||||
|
||||
Ipv4_address Ipv4_address_prefix::subnet_mask() const
|
||||
{
|
||||
Ipv4_address result;
|
||||
if (prefix >= 8) {
|
||||
|
||||
result.addr[0] = 0xff;
|
||||
|
||||
if (prefix >= 16) {
|
||||
|
||||
result.addr[1] = 0xff;
|
||||
|
||||
if (prefix >= 24) {
|
||||
|
||||
result.addr[2] = 0xff;
|
||||
result.addr[3] = 0xff << (32 - prefix);
|
||||
} else {
|
||||
result.addr[2] = 0xff << (24 - prefix);
|
||||
}
|
||||
} else {
|
||||
result.addr[1] = 0xff << (16 - prefix);
|
||||
}
|
||||
} else {
|
||||
result.addr[0] = 0xff << (8 - prefix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void Ipv4_address_prefix::print(Genode::Output &output) const
|
||||
{
|
||||
Genode::print(output, address, "/", prefix);
|
||||
}
|
||||
|
||||
|
||||
bool Ipv4_address_prefix::prefix_matches(Ipv4_address const &ip) const
|
||||
{
|
||||
uint8_t prefix_left = prefix;
|
||||
uint8_t byte = 0;
|
||||
for (; prefix_left >= 8; prefix_left -= 8, byte++) {
|
||||
if (ip.addr[byte] != address.addr[byte]) {
|
||||
return false; }
|
||||
}
|
||||
if (prefix_left == 0) {
|
||||
return true; }
|
||||
|
||||
uint8_t const mask = ~(0xff >> prefix_left);
|
||||
return !((ip.addr[byte] ^ address.addr[byte]) & mask);
|
||||
}
|
||||
|
||||
|
||||
Ipv4_address Ipv4_address_prefix::broadcast_address() const
|
||||
{
|
||||
Ipv4_address result = address;
|
||||
Ipv4_address const mask = subnet_mask();
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
result.addr[i] |= ~mask.addr[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Ipv4_address_prefix::Ipv4_address_prefix(Ipv4_address address,
|
||||
Ipv4_address subnet_mask)
|
||||
:
|
||||
address(address), prefix(0)
|
||||
{
|
||||
Genode::uint8_t rest;
|
||||
if (subnet_mask.addr[0] != 0xff) {
|
||||
rest = subnet_mask.addr[0];
|
||||
prefix = 0;
|
||||
} else if (subnet_mask.addr[1] != 0xff) {
|
||||
rest = subnet_mask.addr[1];
|
||||
prefix = 8;
|
||||
} else if (subnet_mask.addr[2] != 0xff) {
|
||||
rest = subnet_mask.addr[2];
|
||||
prefix = 16;
|
||||
} else {
|
||||
rest = subnet_mask.addr[3];
|
||||
prefix = 24;
|
||||
}
|
||||
for (Genode::uint8_t mask = 1 << 7; rest & mask; mask >>= 1)
|
||||
prefix++;
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* \brief Ipv4 address combined with a subnet prefix length
|
||||
* \author Martin Stein
|
||||
* \date 2017-10-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _IPV4_ADDRESS_PREFIX_H_
|
||||
#define _IPV4_ADDRESS_PREFIX_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ipv4.h>
|
||||
|
||||
namespace Net {
|
||||
class Ipv4_address_prefix;
|
||||
|
||||
static inline Genode::size_t ascii_to(char const *, Net::Ipv4_address_prefix &);
|
||||
}
|
||||
|
||||
|
||||
struct Net::Ipv4_address_prefix
|
||||
{
|
||||
Ipv4_address address { };
|
||||
Genode::uint8_t prefix;
|
||||
|
||||
Ipv4_address_prefix(Ipv4_address address,
|
||||
Ipv4_address subnet_mask);
|
||||
|
||||
Ipv4_address_prefix() : prefix(32) { }
|
||||
|
||||
bool valid() const { return address.valid() || prefix == 0; }
|
||||
|
||||
void print(Genode::Output &output) const;
|
||||
|
||||
bool prefix_matches(Ipv4_address const &ip) const;
|
||||
|
||||
Ipv4_address subnet_mask() const;
|
||||
|
||||
Ipv4_address broadcast_address() const;
|
||||
|
||||
bool operator != (Ipv4_address_prefix const &other) const
|
||||
{
|
||||
return prefix != other.prefix ||
|
||||
address != other.address;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Genode::size_t Net::ascii_to(char const *s, Ipv4_address_prefix &result)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* read the leading IPv4 address, fail if there's no address */
|
||||
Net::Ipv4_address_prefix buf;
|
||||
size_t read_len = ascii_to(s, buf.address);
|
||||
if (!read_len) {
|
||||
return 0; }
|
||||
|
||||
/* check for the following slash */
|
||||
s += read_len;
|
||||
if (*s != '/') {
|
||||
return 0; }
|
||||
read_len++;
|
||||
s++;
|
||||
|
||||
/* read the prefix, fail if there's no prefix */
|
||||
size_t prefix_len = ascii_to_unsigned(s, buf.prefix, 10);
|
||||
if (!prefix_len) {
|
||||
return 0; }
|
||||
|
||||
/* fill result and return read length */
|
||||
result = buf;
|
||||
return read_len + prefix_len;
|
||||
}
|
||||
|
||||
#endif /* _IPV4_ADDRESS_PREFIX_H_ */
|
43
repos/os/src/test/nic_router_dhcp/client/ipv4_config.cc
Normal file
43
repos/os/src/test/nic_router_dhcp/client/ipv4_config.cc
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* \brief IPv4 peer configuration
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/log.h>
|
||||
|
||||
/* local includes */
|
||||
#include <ipv4_config.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
|
||||
Ipv4_config::Ipv4_config(Ipv4_address_prefix interface,
|
||||
Ipv4_address gateway,
|
||||
Ipv4_address dns_server)
|
||||
:
|
||||
interface(interface), gateway(gateway), dns_server(dns_server)
|
||||
{
|
||||
if (!valid && (interface_valid || gateway_valid)) {
|
||||
error("Bad IP configuration");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Ipv4_config::print(Genode::Output &output) const
|
||||
{
|
||||
Genode::print(output, "interface ", interface);
|
||||
if (gateway.valid()) {
|
||||
Genode::print(output, ", gateway ", gateway); }
|
||||
|
||||
if (dns_server.valid()) {
|
||||
Genode::print(output, ", DNS server ", dns_server); }
|
||||
}
|
49
repos/os/src/test/nic_router_dhcp/client/ipv4_config.h
Normal file
49
repos/os/src/test/nic_router_dhcp/client/ipv4_config.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* \brief IPv4 peer configuration
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _IPV4_CONFIG_H_
|
||||
#define _IPV4_CONFIG_H_
|
||||
|
||||
/* local includes */
|
||||
#include <ipv4_address_prefix.h>
|
||||
|
||||
namespace Net { class Ipv4_config; }
|
||||
|
||||
struct Net::Ipv4_config
|
||||
{
|
||||
Ipv4_address_prefix const interface { };
|
||||
bool const interface_valid { interface.valid() };
|
||||
Ipv4_address const gateway { };
|
||||
bool const gateway_valid { gateway.valid() };
|
||||
Ipv4_address const dns_server { };
|
||||
bool const valid { interface_valid &&
|
||||
(!gateway_valid ||
|
||||
interface.prefix_matches(gateway)) };
|
||||
|
||||
Ipv4_config(Ipv4_address_prefix interface,
|
||||
Ipv4_address gateway,
|
||||
Ipv4_address dns_server);
|
||||
|
||||
Ipv4_config() { }
|
||||
|
||||
bool operator != (Ipv4_config const &other) const
|
||||
{
|
||||
return interface != other.interface ||
|
||||
gateway != other.gateway ||
|
||||
dns_server != other.dns_server;
|
||||
}
|
||||
|
||||
void print(Genode::Output &output) const;
|
||||
};
|
||||
|
||||
#endif /* _IPV4_CONFIG_H_ */
|
119
repos/os/src/test/nic_router_dhcp/client/main.cc
Normal file
119
repos/os/src/test/nic_router_dhcp/client/main.cc
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* \brief Test the DHCP functionality of the NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2020-11-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <nic.h>
|
||||
#include <ipv4_config.h>
|
||||
#include <dhcp_client.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ethernet.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
class Main : public Nic_handler,
|
||||
public Dhcp_client_handler
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Attached_rom_dataspace _config_rom { _env, "config" };
|
||||
Xml_node _config { _config_rom.xml() };
|
||||
Timer::Connection _timer { _env };
|
||||
Heap _heap { &_env.ram(), &_env.rm() };
|
||||
bool const _verbose { _config.attribute_value("verbose", false) };
|
||||
Net::Nic _nic { _env, _heap, *this, _verbose };
|
||||
Constructible<Dhcp_client> _dhcp_client { };
|
||||
bool _link_state { false };
|
||||
Reconstructible<Ipv4_config> _ip_config { };
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid_arguments : Exception { };
|
||||
|
||||
Main(Env &env);
|
||||
|
||||
|
||||
/*****************
|
||||
** Nic_handler **
|
||||
*****************/
|
||||
|
||||
void handle_eth(Ethernet_frame ð,
|
||||
Size_guard &size_guard) override;
|
||||
|
||||
void handle_link_state(bool link_state) override
|
||||
{
|
||||
if (!_link_state && link_state) {
|
||||
_dhcp_client.construct(_heap, _timer, _nic, *this);
|
||||
}
|
||||
if (_link_state && !link_state && ip_config().valid) {
|
||||
ip_config(Ipv4_config { });
|
||||
}
|
||||
_link_state = link_state;
|
||||
};
|
||||
|
||||
|
||||
/*************************
|
||||
** Dhcp_client_handler **
|
||||
*************************/
|
||||
|
||||
void ip_config(Ipv4_config const &ip_config) override;
|
||||
|
||||
Ipv4_config const &ip_config() const override { return *_ip_config; }
|
||||
};
|
||||
|
||||
|
||||
void Main::ip_config(Ipv4_config const &ip_config)
|
||||
{
|
||||
if (_verbose) {
|
||||
log("IP config: ", ip_config); }
|
||||
|
||||
_ip_config.construct(ip_config);
|
||||
}
|
||||
|
||||
|
||||
Main::Main(Env &env) : _env(env)
|
||||
{
|
||||
log("Initialized");
|
||||
_nic.handle_link_state();
|
||||
}
|
||||
|
||||
|
||||
void Main::handle_eth(Ethernet_frame ð,
|
||||
Size_guard &size_guard)
|
||||
{
|
||||
try {
|
||||
/* print receipt message */
|
||||
if (_verbose) {
|
||||
log("rcv ", eth); }
|
||||
|
||||
if (!ip_config().valid) {
|
||||
_dhcp_client->handle_eth(eth, size_guard); }
|
||||
else {
|
||||
throw Drop_packet_inform("IP config still valid");
|
||||
}
|
||||
}
|
||||
catch (Drop_packet_inform exception) {
|
||||
if (_verbose) {
|
||||
log("drop packet: ", exception.msg); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Env &env) { static Main main(env); }
|
46
repos/os/src/test/nic_router_dhcp/client/nic.cc
Normal file
46
repos/os/src/test/nic_router_dhcp/client/nic.cc
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* \brief NIC connection wrapper for a more convenient interface
|
||||
* \author Martin Stein
|
||||
* \date 2018-04-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <nic.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Net::Nic::_ready_to_ack()
|
||||
{
|
||||
while (_source().ack_avail()) {
|
||||
_source().release_packet(_source().get_acked_packet()); }
|
||||
}
|
||||
|
||||
|
||||
void Net::Nic::_ready_to_submit()
|
||||
{
|
||||
while (_sink().packet_avail()) {
|
||||
|
||||
Packet_descriptor const pkt = _sink().get_packet();
|
||||
if (!pkt.size()) {
|
||||
continue; }
|
||||
|
||||
Size_guard size_guard(pkt.size());
|
||||
_handler.handle_eth(Ethernet_frame::cast_from(_sink().packet_content(pkt), size_guard),
|
||||
size_guard);
|
||||
|
||||
if (!_sink().ready_to_ack()) {
|
||||
error("ack state FULL");
|
||||
return;
|
||||
}
|
||||
_sink().acknowledge_packet(pkt);
|
||||
}
|
||||
}
|
137
repos/os/src/test/nic_router_dhcp/client/nic.h
Normal file
137
repos/os/src/test/nic_router_dhcp/client/nic.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* \brief NIC connection wrapper for a more convenient interface
|
||||
* \author Martin Stein
|
||||
* \date 2018-04-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _NIC_H_
|
||||
#define _NIC_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <nic_session/connection.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <net/ethernet.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Env;
|
||||
}
|
||||
|
||||
namespace Net {
|
||||
|
||||
struct Nic_handler;
|
||||
class Nic;
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
|
||||
struct Net::Nic_handler
|
||||
{
|
||||
virtual void handle_eth(Ethernet_frame ð,
|
||||
Size_guard &size_guard) = 0;
|
||||
|
||||
virtual void handle_link_state(bool link_state) = 0;
|
||||
|
||||
virtual ~Nic_handler() { }
|
||||
};
|
||||
|
||||
|
||||
class Net::Nic
|
||||
{
|
||||
private:
|
||||
|
||||
using Signal_handler = Genode::Signal_handler<Nic>;
|
||||
|
||||
enum { PKT_SIZE = ::Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
|
||||
enum { BUF_SIZE = ::Nic::Session::QUEUE_SIZE * PKT_SIZE };
|
||||
|
||||
Genode::Env &_env;
|
||||
Genode::Allocator &_alloc;
|
||||
Nic_handler &_handler;
|
||||
bool const &_verbose;
|
||||
::Nic::Packet_allocator _pkt_alloc { &_alloc };
|
||||
::Nic::Connection _nic { _env, &_pkt_alloc, BUF_SIZE, BUF_SIZE };
|
||||
Signal_handler _sink_ack { _env.ep(), *this, &Nic::_ack_avail };
|
||||
Signal_handler _sink_submit { _env.ep(), *this, &Nic::_ready_to_submit };
|
||||
Signal_handler _source_ack { _env.ep(), *this, &Nic::_ready_to_ack };
|
||||
Signal_handler _source_submit { _env.ep(), *this, &Nic::_packet_avail };
|
||||
Signal_handler _link_state_handler { _env.ep(), *this, &Nic::handle_link_state };
|
||||
Mac_address const _mac { _nic.mac_address() };
|
||||
|
||||
Net::Packet_stream_sink &_sink() { return *_nic.rx(); }
|
||||
Net::Packet_stream_source &_source() { return *_nic.tx(); }
|
||||
|
||||
|
||||
/***********************************
|
||||
** Packet-stream signal handlers **
|
||||
***********************************/
|
||||
|
||||
void _ready_to_submit();
|
||||
void _ack_avail() { }
|
||||
void _ready_to_ack();
|
||||
void _packet_avail() { }
|
||||
|
||||
public:
|
||||
|
||||
Nic(Genode::Env &env,
|
||||
Genode::Allocator &alloc,
|
||||
Nic_handler &handler,
|
||||
bool const &verbose)
|
||||
:
|
||||
_env (env),
|
||||
_alloc (alloc),
|
||||
_handler (handler),
|
||||
_verbose (verbose)
|
||||
{
|
||||
_nic.rx_channel()->sigh_ready_to_ack(_sink_ack);
|
||||
_nic.rx_channel()->sigh_packet_avail(_sink_submit);
|
||||
_nic.tx_channel()->sigh_ack_avail(_source_ack);
|
||||
_nic.tx_channel()->sigh_ready_to_submit(_source_submit);
|
||||
_nic.link_state_sigh(_link_state_handler);
|
||||
}
|
||||
|
||||
void handle_link_state()
|
||||
{
|
||||
_handler.handle_link_state(_nic.link_state());
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void send(Genode::size_t pkt_size,
|
||||
FUNC && write_to_pkt)
|
||||
{
|
||||
try {
|
||||
Packet_descriptor pkt = _source().alloc_packet(pkt_size);
|
||||
void *pkt_base = _source().packet_content(pkt);
|
||||
Size_guard size_guard(pkt_size);
|
||||
write_to_pkt(pkt_base, size_guard);
|
||||
_source().submit_packet(pkt);
|
||||
if (_verbose) {
|
||||
Size_guard size_guard(pkt_size);
|
||||
try { Genode::log("snd ", Ethernet_frame::cast_from(pkt_base, size_guard)); }
|
||||
catch (Size_guard::Exceeded) { Genode::log("snd ?"); }
|
||||
}
|
||||
}
|
||||
catch (Net::Packet_stream_source::Packet_alloc_failed) {
|
||||
Genode::warning("failed to allocate packet"); }
|
||||
}
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Mac_address const &mac() const { return _mac; }
|
||||
};
|
||||
|
||||
|
||||
#endif /* _NIC_H_ */
|
8
repos/os/src/test/nic_router_dhcp/client/target.mk
Normal file
8
repos/os/src/test/nic_router_dhcp/client/target.mk
Normal file
@ -0,0 +1,8 @@
|
||||
TARGET = test-nic_router_dhcp-client
|
||||
|
||||
LIBS += base net
|
||||
|
||||
SRC_CC += main.cc dhcp_client.cc ipv4_address_prefix.cc
|
||||
SRC_CC += nic.cc ipv4_config.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
34
repos/os/src/test/nic_router_dhcp/manager/dns_server.cc
Normal file
34
repos/os/src/test/nic_router_dhcp/manager/dns_server.cc
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* \brief DNS server entry of a DHCP server or IPv4 config
|
||||
* \author Martin Stein
|
||||
* \date 2020-11-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <dns_server.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Local::Dns_server::Dns_server(Ipv4_address const &ip)
|
||||
:
|
||||
_ip { ip }
|
||||
{
|
||||
if (!_ip.valid()) {
|
||||
throw Invalid { };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Local::Dns_server::equal_to(Dns_server const &server) const
|
||||
{
|
||||
return _ip == server._ip;
|
||||
}
|
50
repos/os/src/test/nic_router_dhcp/manager/dns_server.h
Normal file
50
repos/os/src/test/nic_router_dhcp/manager/dns_server.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* \brief DNS server entry of a DHCP server or IPv4 config
|
||||
* \author Martin Stein
|
||||
* \date 2020-11-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _DNS_SERVER_H_
|
||||
#define _DNS_SERVER_H_
|
||||
|
||||
/* local includes */
|
||||
#include <list.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ipv4.h>
|
||||
|
||||
namespace Local { class Dns_server; }
|
||||
|
||||
class Local::Dns_server : private Genode::Noncopyable,
|
||||
public Local::List<Dns_server>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
Net::Ipv4_address const _ip;
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid : Genode::Exception { };
|
||||
|
||||
Dns_server(Net::Ipv4_address const &ip);
|
||||
|
||||
bool equal_to(Dns_server const &server) const;
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Net::Ipv4_address const &ip() const { return _ip; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* _DHCP_SERVER_H_ */
|
103
repos/os/src/test/nic_router_dhcp/manager/ipv4_address_prefix.cc
Normal file
103
repos/os/src/test/nic_router_dhcp/manager/ipv4_address_prefix.cc
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* \brief Ipv4 address combined with a subnet prefix length
|
||||
* \author Martin Stein
|
||||
* \date 2017-10-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <ipv4_address_prefix.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
|
||||
|
||||
Ipv4_address Ipv4_address_prefix::subnet_mask() const
|
||||
{
|
||||
Ipv4_address result;
|
||||
if (prefix >= 8) {
|
||||
|
||||
result.addr[0] = 0xff;
|
||||
|
||||
if (prefix >= 16) {
|
||||
|
||||
result.addr[1] = 0xff;
|
||||
|
||||
if (prefix >= 24) {
|
||||
|
||||
result.addr[2] = 0xff;
|
||||
result.addr[3] = 0xff << (32 - prefix);
|
||||
} else {
|
||||
result.addr[2] = 0xff << (24 - prefix);
|
||||
}
|
||||
} else {
|
||||
result.addr[1] = 0xff << (16 - prefix);
|
||||
}
|
||||
} else {
|
||||
result.addr[0] = 0xff << (8 - prefix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void Ipv4_address_prefix::print(Genode::Output &output) const
|
||||
{
|
||||
Genode::print(output, address, "/", prefix);
|
||||
}
|
||||
|
||||
|
||||
bool Ipv4_address_prefix::prefix_matches(Ipv4_address const &ip) const
|
||||
{
|
||||
uint8_t prefix_left = prefix;
|
||||
uint8_t byte = 0;
|
||||
for (; prefix_left >= 8; prefix_left -= 8, byte++) {
|
||||
if (ip.addr[byte] != address.addr[byte]) {
|
||||
return false; }
|
||||
}
|
||||
if (prefix_left == 0) {
|
||||
return true; }
|
||||
|
||||
uint8_t const mask = ~(0xff >> prefix_left);
|
||||
return !((ip.addr[byte] ^ address.addr[byte]) & mask);
|
||||
}
|
||||
|
||||
|
||||
Ipv4_address Ipv4_address_prefix::broadcast_address() const
|
||||
{
|
||||
Ipv4_address result = address;
|
||||
Ipv4_address const mask = subnet_mask();
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
result.addr[i] |= ~mask.addr[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Ipv4_address_prefix::Ipv4_address_prefix(Ipv4_address address,
|
||||
Ipv4_address subnet_mask)
|
||||
:
|
||||
address(address), prefix(0)
|
||||
{
|
||||
Genode::uint8_t rest;
|
||||
if (subnet_mask.addr[0] != 0xff) {
|
||||
rest = subnet_mask.addr[0];
|
||||
prefix = 0;
|
||||
} else if (subnet_mask.addr[1] != 0xff) {
|
||||
rest = subnet_mask.addr[1];
|
||||
prefix = 8;
|
||||
} else if (subnet_mask.addr[2] != 0xff) {
|
||||
rest = subnet_mask.addr[2];
|
||||
prefix = 16;
|
||||
} else {
|
||||
rest = subnet_mask.addr[3];
|
||||
prefix = 24;
|
||||
}
|
||||
for (Genode::uint8_t mask = 1 << 7; rest & mask; mask >>= 1)
|
||||
prefix++;
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* \brief Ipv4 address combined with a subnet prefix length
|
||||
* \author Martin Stein
|
||||
* \date 2017-10-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _IPV4_ADDRESS_PREFIX_H_
|
||||
#define _IPV4_ADDRESS_PREFIX_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ipv4.h>
|
||||
|
||||
namespace Net {
|
||||
class Ipv4_address_prefix;
|
||||
|
||||
static inline Genode::size_t ascii_to(char const *, Net::Ipv4_address_prefix &);
|
||||
}
|
||||
|
||||
|
||||
struct Net::Ipv4_address_prefix
|
||||
{
|
||||
Ipv4_address address { };
|
||||
Genode::uint8_t prefix;
|
||||
|
||||
Ipv4_address_prefix(Ipv4_address address,
|
||||
Ipv4_address subnet_mask);
|
||||
|
||||
Ipv4_address_prefix() : prefix(32) { }
|
||||
|
||||
bool valid() const { return address.valid() || prefix == 0; }
|
||||
|
||||
void print(Genode::Output &output) const;
|
||||
|
||||
bool prefix_matches(Ipv4_address const &ip) const;
|
||||
|
||||
Ipv4_address subnet_mask() const;
|
||||
|
||||
Ipv4_address broadcast_address() const;
|
||||
|
||||
bool operator != (Ipv4_address_prefix const &other) const
|
||||
{
|
||||
return prefix != other.prefix ||
|
||||
address != other.address;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Genode::size_t Net::ascii_to(char const *s, Ipv4_address_prefix &result)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* read the leading IPv4 address, fail if there's no address */
|
||||
Net::Ipv4_address_prefix buf;
|
||||
size_t read_len = ascii_to(s, buf.address);
|
||||
if (!read_len) {
|
||||
return 0; }
|
||||
|
||||
/* check for the following slash */
|
||||
s += read_len;
|
||||
if (*s != '/') {
|
||||
return 0; }
|
||||
read_len++;
|
||||
s++;
|
||||
|
||||
/* read the prefix, fail if there's no prefix */
|
||||
size_t prefix_len = ascii_to_unsigned(s, buf.prefix, 10);
|
||||
if (!prefix_len) {
|
||||
return 0; }
|
||||
|
||||
/* fill result and return read length */
|
||||
result = buf;
|
||||
return read_len + prefix_len;
|
||||
}
|
||||
|
||||
#endif /* _IPV4_ADDRESS_PREFIX_H_ */
|
102
repos/os/src/test/nic_router_dhcp/manager/list.h
Normal file
102
repos/os/src/test/nic_router_dhcp/manager/list.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* \brief Genode list with additional functions needed by NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _LIST_H_
|
||||
#define _LIST_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list.h>
|
||||
#include <base/allocator.h>
|
||||
|
||||
namespace Local { template <typename> class List; }
|
||||
|
||||
|
||||
template <typename LT>
|
||||
struct Local::List : Genode::List<LT>
|
||||
{
|
||||
using Base = Genode::List<LT>;
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each(FUNC && functor)
|
||||
{
|
||||
for (LT *elem = Base::first(); elem; )
|
||||
{
|
||||
LT *const next = elem->Base::Element::next();
|
||||
functor(*elem);
|
||||
elem = next;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each(FUNC && functor) const
|
||||
{
|
||||
for (LT const *elem = Base::first(); elem; )
|
||||
{
|
||||
LT const *const next = elem->Base::Element::next();
|
||||
functor(*elem);
|
||||
elem = next;
|
||||
}
|
||||
}
|
||||
|
||||
void destroy_each(Genode::Deallocator &dealloc)
|
||||
{
|
||||
while (LT *elem = Base::first()) {
|
||||
Base::remove(elem);
|
||||
destroy(dealloc, elem);
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return Base::first() == nullptr;
|
||||
}
|
||||
|
||||
void insert_as_tail(LT const &le)
|
||||
{
|
||||
LT *elem { Base::first() };
|
||||
if (elem) {
|
||||
while (elem->Base::Element::next()) {
|
||||
elem = elem->Base::Element::next();
|
||||
}
|
||||
}
|
||||
Base::insert(&le, elem);
|
||||
}
|
||||
|
||||
bool equal_to(List<LT> const &list) const
|
||||
{
|
||||
LT const *curr_elem_1 { Base::first() };
|
||||
LT const *curr_elem_2 { list.Base::first() };
|
||||
while (true) {
|
||||
|
||||
if (curr_elem_1 == nullptr) {
|
||||
return curr_elem_2 == nullptr;
|
||||
}
|
||||
if (curr_elem_2 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
LT const *const next_elem_1 {
|
||||
curr_elem_1->List<LT>::Element::next() };
|
||||
|
||||
LT const *const next_elem_2 {
|
||||
curr_elem_2->List<LT>::Element::next() };
|
||||
|
||||
if (!curr_elem_1->equal_to(*curr_elem_2)) {
|
||||
return false;
|
||||
}
|
||||
curr_elem_1 = next_elem_1;
|
||||
curr_elem_2 = next_elem_2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _LIST_H_ */
|
175
repos/os/src/test/nic_router_dhcp/manager/main.cc
Normal file
175
repos/os/src/test/nic_router_dhcp/manager/main.cc
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* \brief Server component for Network Address Translation on NIC sessions
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode */
|
||||
#include <base/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/heap.h>
|
||||
#include <util/list.h>
|
||||
#include <os/reporter.h>
|
||||
|
||||
/* local includes */
|
||||
#include <ipv4_address_prefix.h>
|
||||
#include <dns_server.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
using Domain_name = String<160>;
|
||||
|
||||
namespace Local { class Main; }
|
||||
|
||||
|
||||
class Local::Main
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Heap _heap { &_env.ram(), &_env.rm() };
|
||||
Attached_rom_dataspace _router_state_rom { _env, "router_state" };
|
||||
Signal_handler<Main> _router_state_handler { _env.ep(), *this, &Main::_handle_router_state };
|
||||
Expanding_reporter _router_config_reporter { _env, "config", "router_config" };
|
||||
bool _router_config_outdated { true };
|
||||
Local::List<Dns_server> _dns_servers { };
|
||||
|
||||
void _handle_router_state();
|
||||
|
||||
public:
|
||||
|
||||
Main(Env &env);
|
||||
};
|
||||
|
||||
|
||||
Local::Main::Main(Env &env) : _env(env)
|
||||
{
|
||||
log("Initialized");
|
||||
_router_state_rom.sigh(_router_state_handler);
|
||||
_handle_router_state();
|
||||
}
|
||||
|
||||
|
||||
void Local::Main::_handle_router_state()
|
||||
{
|
||||
/* Request the moste recent content of the router state dataspace. */
|
||||
log("Read state of nic_router_2");
|
||||
_router_state_rom.update();
|
||||
|
||||
/*
|
||||
* Search for the uplink domain tag in the updated router state,
|
||||
* read the new list of DNS servers from and compare it to the old list to
|
||||
* see whether we have to re-configure the router.
|
||||
*/
|
||||
bool domain_found { false };
|
||||
_router_state_rom.xml().for_each_sub_node(
|
||||
"domain",
|
||||
[&] (Xml_node const &domain_node)
|
||||
{
|
||||
/*
|
||||
* If we already found the uplink domain, refrain from inspecting
|
||||
* further domain tags.
|
||||
*/
|
||||
if (domain_found) {
|
||||
return;
|
||||
}
|
||||
if (domain_node.attribute_value("name", Domain_name()) == "uplink") {
|
||||
|
||||
domain_found = true;
|
||||
|
||||
/*
|
||||
* Consider re-configuring the router only when the uplink has
|
||||
* a valid IPv4 config. This prevents us from propagating each,
|
||||
* normally short-living "No-DNS-Server"-state that merely comes
|
||||
* from the fact that the uplink has to redo DHCP and invalidates
|
||||
* its Ipv4 config for this time.
|
||||
*/
|
||||
if (!domain_node.attribute_value("ipv4", Ipv4_address_prefix { }).valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read out all DNS servers from the new uplink state
|
||||
* and memorize them in a function-local list.
|
||||
*/
|
||||
Local::List<Dns_server> dns_servers { };
|
||||
domain_node.for_each_sub_node(
|
||||
"dns",
|
||||
[&] (Xml_node const &dns_node)
|
||||
{
|
||||
|
||||
dns_servers.insert_as_tail(
|
||||
*new (_heap) Dns_server(dns_node.attribute_value("ip", Ipv4_address { })));
|
||||
});
|
||||
|
||||
/*
|
||||
* If the new list of DNS servers differs our member list,
|
||||
* update the member list, and remember to write out a new router
|
||||
* configuration.
|
||||
*/
|
||||
if (!_dns_servers.equal_to(dns_servers)) {
|
||||
|
||||
_dns_servers.destroy_each(_heap);
|
||||
dns_servers.for_each([&] (Dns_server const &dns_server) {
|
||||
_dns_servers.insert_as_tail(
|
||||
*new (_heap) Dns_server(dns_server.ip()));
|
||||
});
|
||||
_router_config_outdated = true;
|
||||
}
|
||||
dns_servers.destroy_each(_heap);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Write out a new router configuration with the updated list of
|
||||
* DNS servers if necessary.
|
||||
*/
|
||||
if (_router_config_outdated) {
|
||||
|
||||
log("Write config of nic_router_2");
|
||||
_router_config_reporter.generate([&] (Xml_generator &xml) {
|
||||
xml.node("report", [&] () {
|
||||
xml.attribute("bytes", "no");
|
||||
xml.attribute("stats", "no");
|
||||
xml.attribute("quota", "no");
|
||||
xml.attribute("config", "yes");
|
||||
xml.attribute("config_triggers", "yes");
|
||||
xml.attribute("interval_sec", "100");
|
||||
});
|
||||
xml.node("policy", [&] () {
|
||||
xml.attribute("label", "test_client -> ");
|
||||
xml.attribute("domain", "downlink");
|
||||
});
|
||||
xml.node("uplink", [&] () {
|
||||
xml.attribute("domain", "uplink");
|
||||
});
|
||||
xml.node("domain", [&] () {
|
||||
xml.attribute("name", "uplink");
|
||||
});
|
||||
xml.node("domain", [&] () {
|
||||
xml.attribute("name", "downlink");
|
||||
xml.attribute("interface", "10.0.3.1/24");
|
||||
xml.node("dhcp-server", [&] () {
|
||||
xml.attribute("ip_first", "10.0.3.2");
|
||||
xml.attribute("ip_last", "10.0.3.2");
|
||||
_dns_servers.for_each([&] (Dns_server const &dns_server) {
|
||||
xml.node("dns-server", [&] () {
|
||||
xml.attribute("ip", String<16>(dns_server.ip()));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
_router_config_outdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Env &env) { static Local::Main main(env); }
|
7
repos/os/src/test/nic_router_dhcp/manager/target.mk
Normal file
7
repos/os/src/test/nic_router_dhcp/manager/target.mk
Normal file
@ -0,0 +1,7 @@
|
||||
TARGET = test-nic_router_dhcp-manager
|
||||
|
||||
LIBS += base
|
||||
|
||||
SRC_CC += main.cc ipv4_address_prefix.cc dns_server.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
@ -41,6 +41,8 @@ nic_bridge
|
||||
nic_bridge_stress
|
||||
nic_dump
|
||||
nic_router
|
||||
nic_router_dhcp_managed
|
||||
nic_router_dhcp_unmanaged
|
||||
nic_router_flood
|
||||
nic_router_stress
|
||||
nic_router_uplinks
|
||||
|
Loading…
x
Reference in New Issue
Block a user