nic_router: list dns servers in one dhcp option 6

The NIC router DHCP server used to add an extra option 6 field to DHCP replies
for each DNS server address. This conflicts with RFC #2132 section 3.8 which
states that the addresses should be listed within one option 6 field without
delimiter. The discrepancy is fixed by this commit.

Ref #4242
This commit is contained in:
Martin Stein 2021-08-04 15:33:48 +02:00 committed by Christian Helmuth
parent 80c1459e79
commit 9e6f7988c2
5 changed files with 112 additions and 36 deletions

View File

@ -201,6 +201,32 @@ class Net::Dhcp_packet
Parameter_request_list(Genode::size_t len) : Option(CODE, len) { }
};
/**
* Domain name server option
*/
class Dns_server : public Option
{
private:
Ipv4_address _dns_servers[0];
public:
static constexpr Code CODE = Code::DNS_SERVER;
Dns_server(Genode::size_t len) : Option(CODE, len) { }
template <typename FUNC>
void for_each_address(FUNC && func) const
{
for (unsigned idx = 0;
idx < len() / sizeof(_dns_servers[0]); idx++) {
func(_dns_servers[idx]);
}
}
};
enum class Message_type : Genode::uint8_t {
DISCOVER = 1,
OFFER = 2,
@ -341,6 +367,36 @@ class Net::Dhcp_packet
Genode::size_t size() const { return _size; }
};
class Dns_server_data
{
private:
Ipv4_address *const _addr_array;
Genode::size_t _addr_idx { 0 };
SIZE_GUARD &_pkt_size_guard;
public:
Dns_server_data(Ipv4_address *addr_array,
SIZE_GUARD &pkt_size_guard)
:
_addr_array { addr_array },
_pkt_size_guard { pkt_size_guard }
{ }
void append_address(Ipv4_address const &addr)
{
_pkt_size_guard.consume_head(sizeof(_addr_array[0]));
_addr_array[_addr_idx] = addr;
_addr_idx++;
}
Genode::size_t size() const
{
return _addr_idx * sizeof(Ipv4_address);
}
};
Options_aggregator(Dhcp_packet &packet,
SIZE_GUARD &size_guard)
:
@ -371,6 +427,21 @@ class Net::Dhcp_packet
_base += sizeof(Parameter_request_list) + data.size();
}
template <typename INIT_DATA>
void append_dns_server(INIT_DATA && init_data)
{
Genode::size_t const header_size { sizeof(Dns_server) };
_size_guard.consume_head(header_size);
Dns_server_data data {
(Ipv4_address *)(_base + header_size), _size_guard };
init_data(data);
Genode::construct_at<Dns_server>(
(void *)_base, data.size());
_base += header_size + data.size();
}
};

View File

@ -469,10 +469,10 @@ 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.
DNS server addresses that shall be propagated by the DHCP server through a DHCP
option 6 entry to its clients. These addresses might be of any IP subnet. The
addresses in the DHCP option 6 entry 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
@ -484,8 +484,8 @@ dynamically through the DHCP client of another domain. An implication of 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:
The lifetime of an IP address 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">
@ -692,10 +692,9 @@ attribute 'ipv4' contains the current IPv4 address of the router in this domain
suffixed by the length of the subnet prefix in bits. The 'gw' attribute
contains the IPv4 address of the gateway in this domain. Each <dns> subtag of a
<domain> tag shows the IPv4 address of a DNS server known to this domain. The
<dns> subtags have the same order that the addresses had when the router
received them (i.e., the order of DHCP option 6 entries in the corresponding
DHCP replies or the order of <dns> entries in the corresponding router
configuration).
<dns> subtags have the same order that the addresses had in the DHCP option 6
entry of the DHCP reply that the router received and that led to the current
IPv4 configuration of the domain.
'config_triggers'

View File

@ -37,6 +37,7 @@ using Genode::Constructible;
using Genode::Reconstructible;
using Genode::Signal_context_capability;
using Genode::Signal_transmitter;
using Dhcp_options = Dhcp_packet::Options_aggregator<Size_guard>;
/***************
@ -671,8 +672,11 @@ 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);
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_dns_server([&] (Dhcp_options::Dns_server_data &data) {
dhcp_srv.for_each_dns_server_ip([&] (Ipv4_address const &addr) {
data.append_address(addr);
});
});
dhcp_opts.append_option<Dhcp_packet::Broadcast_addr>(local_intf.broadcast_address());
dhcp_opts.append_option<Dhcp_packet::Options_end>();

View File

@ -60,19 +60,17 @@ Ipv4_config::Ipv4_config(Dhcp_packet &dhcp_ack,
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) { }
Dhcp_packet::Dns_server const &dns_server {
dhcp_ack.option<Dhcp_packet::Dns_server>() };
dns_server.for_each_address([&] (Ipv4_address const &addr) {
dns_servers.insert_as_tail(*new (alloc) Dns_server(addr));
});
}
catch (Dhcp_packet::Option_not_found) { }
}
Ipv4_config::~Ipv4_config()

View File

@ -170,24 +170,28 @@ void Dhcp_client::_handle_dhcp_reply(Dhcp_packet &dhcp)
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 { };
Ipv4_address dns_server_addr { };
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();
try {
Dhcp_packet::Dns_server const &dns_server {
dhcp.option<Dhcp_packet::Dns_server>() };
dns_server.for_each_address([&] (Ipv4_address const &addr) {
if (!dns_server_addr.valid()) {
dns_server_addr = addr;
}
if (opt.code() != Dhcp_packet::Option::Code::DNS_SERVER) {
return;
}
log(" DNS server #", idx++, ": ", reinterpret_cast<Dhcp_packet::Dns_server_ipv4 const *>(&opt)->value());
log(" DNS server #", idx++, ": ", addr);
});
}
catch (Dhcp_packet::Option_not_found) { }
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);
dns_server_addr);
_handler.ip_config(ip_config);
break;