sntp_client: add DNS lookup option

This commit adds a new configuration option, `dst_addr` to the
'sntp_client' that accepts either an IP address or a DNS hostname. If a
DNS hostname is provided, the 'sntp_client' will resolve the IP address
before each SNTP request. The 'dst_ip' configuration option is
deprecated but kept for compatibility until 'dst_addr' is fully adopted.

xsd/net_types.xsd: add Net_address type
sntp_dummy_rtc: adjust configuration to use pool.ntp.org

Fixes genodelabs/genode#5003
This commit is contained in:
Alice Domage 2023-09-22 13:43:57 +02:00 committed by Christian Helmuth
parent 132e027c69
commit 6402182815
6 changed files with 158 additions and 17 deletions

View File

@ -33,7 +33,7 @@
<start name="sntp_client" caps="100">
<resource name="RAM" quantum="4M"/>
<config verbose="no" dst_ip="216.239.35.0" period_min="5"/>
<config verbose="no" dst_addr="pool.ntp.org" period_min="5"/>
<route>
<service name="Report"> <child name="report_rom"/> </service>
<any-service> <parent/> </any-service>

View File

@ -5,11 +5,6 @@ if { [have_spec linux] } {
exit 0
}
set host_tool [installed_command host]
set ntp_ip [exec $host_tool -t A pool.ntp.org | head -1 | cut -d " " -f 4]
puts "Using NTP server $ntp_ip"
create_boot_directory
import_from_depot [depot_user]/src/[base_src] \
@ -98,7 +93,7 @@ install_config {
<start name="sntp_client">
<resource name="RAM" quantum="8M"/>
<config verbose="no" dst_ip="} $ntp_ip {" period_min="1"/>
<config verbose="no" dst_addr="pool.ntp.org" period_min="1"/>
<route>
<service name="Nic"> <child name="nic_router"/> </service>
<any-service> <parent/> <any-child/> </any-service>

View File

@ -11,14 +11,14 @@ an individual request interval, and debugging output enabled:
! <config interface="10.0.0.72/24"
! gateway="10.0.0.1"
! dst_ip="10.0.0.24"
! dst_addr="10.0.0.24"
! period_min="10"
! verbose="yes" />
This is an example configuration of the component with DHCP:
! <config dst_ip="10.0.0.24" />
! <config dst_addr="10.0.0.24" />
This is a short description of the tags and attributes:
@ -32,8 +32,8 @@ This is a short description of the tags and attributes:
Optional. Determined via DHCP if not configured. IP address of the gateway
of the IP subnet.
:config.dst_ip:
Mandatory. IP address of the SNTP server.
:config.dst_addr:
Mandatory. IP address or domain name of a SNTP server.
:config.period_min:
Optional. Default is 60. Length of request interval in minutes.
@ -74,4 +74,4 @@ Examples
~~~~~~~~
An example of how to use the component can be found in the test script
'libports/run/sntp_client.run' and 'os/run/ping_nic_router.run'.
'libports/run/sntp_client.run'.

View File

@ -8,6 +8,7 @@
<xs:element name="config">
<xs:complexType>
<xs:attribute name="verbose" type="Boolean" />
<xs:attribute name="dst_addr" type="Net_address" />
<xs:attribute name="dst_ip" type="Ipv4_address" />
<xs:attribute name="interface" type="Ipv4_address_prefix" />
<xs:attribute name="gateway" type="Ipv4_address" />

View File

@ -22,6 +22,7 @@
#include <net/ethernet.h>
#include <net/arp.h>
#include <net/sntp.h>
#include <net/dns.h>
#include <base/component.h>
#include <base/heap.h>
#include <base/attached_rom_dataspace.h>
@ -70,12 +71,14 @@ class Main : public Nic_handler,
Heap _heap { &_env.ram(), &_env.rm() };
bool const _verbose { _config.attribute_value("verbose", false) };
Net::Nic _nic { _env, _heap, *this, _verbose };
Ipv4_address const _dst_ip { _config.attribute_value("dst_ip", Ipv4_address()) };
Ipv4_address _dst_ip { _config.attribute_value("dst_addr", Ipv4_address()) };
Domain_name const _dst_ns { _config.attribute_value("dst_addr", Domain_name()) };
unsigned short _dns_req_id { 0 };
Mac_address _dst_mac { };
Constructible<Dhcp_client> _dhcp_client { };
Reconstructible<Ipv4_config> _ip_config { _config.attribute_value("interface", Ipv4_address_prefix()),
_config.attribute_value("gateway", Ipv4_address()),
Ipv4_address() };
_config.attribute_value("dns-server", Ipv4_address()) };
Reporter reporter { _env, "set_rtc" };
Sntp_timestamp _rtc_ts_to_sntp_ts(Rtc::Timestamp const rtc_ts)
@ -110,6 +113,11 @@ class Main : public Nic_handler,
void _handle_arp(Ethernet_frame &eth,
Size_guard &size_guard);
void _handle_dns(Udp_packet &udp,
Size_guard &size_guard);
void _send_dns_request();
void _broadcast_arp_request(Ipv4_address const &ip);
void _send_arp_reply(Ethernet_frame &req_eth,
@ -154,9 +162,17 @@ void Main::ip_config(Ipv4_config const &ip_config)
Main::Main(Env &env) : _env(env)
{
/* deprecated dst_ip configuration option */
if (_config.has_attribute("dst_ip")) {
warning("\"dst_ip\" configuration attribute is deprecated, please use \"dst_addr\"");
_dst_ip = _config.attribute_value("dst_ip", Ipv4_address());
if (_dst_ip == Ipv4_address()) {
throw Invalid_arguments(); } }
/* exit unsuccessful if parameters are invalid */
if (_dst_ip == Ipv4_address()) {
throw Invalid_arguments(); }
if (_dst_ns == Domain_name()) {
if (_dst_ip == Ipv4_address()) {
throw Invalid_arguments(); } }
/* if there is a static IP config, start sending pings periodically */
if (ip_config().valid) {
@ -237,6 +253,12 @@ void Main::_handle_udp(Ipv4_packet &ip,
log("bad UDP checksum"); }
return;
}
if (udp.src_port().value == Dns_packet::UDP_PORT) {
_handle_dns(udp, size_guard);
return;
}
/* drop packet if UDP source port is invalid */
if (udp.src_port().value != Sntp_packet::UDP_PORT) {
if (_verbose) {
@ -261,6 +283,7 @@ void Main::_handle_udp(Ipv4_packet &ip,
log("bad SNTP mode"); }
return;
}
Rtc::Timestamp rtc_ts { _sntp_ts_to_rtc_ts(sntp.transmit_timestamp()) };
Reporter::Xml_generator xml(reporter, [&] () {
xml.attribute("year", rtc_ts.year);
@ -270,6 +293,9 @@ void Main::_handle_udp(Ipv4_packet &ip,
xml.attribute("minute", rtc_ts.minute);
xml.attribute("second", rtc_ts.second);
});
if (_dst_ns != Domain_name()) {
_dst_ip = Ipv4_address();
}
}
@ -298,7 +324,11 @@ void Main::_handle_arp(Ethernet_frame &eth,
}
/* set destination MAC address and retry to ping */
_dst_mac = arp.src_mac();
_send_sntp_request();
if (_dst_ip == Ipv4_address() && _dst_ns != Domain_name()) {
_send_dns_request();
} else {
_send_sntp_request();
}
return;
case Arp_packet::REQUEST:
@ -313,6 +343,43 @@ void Main::_handle_arp(Ethernet_frame &eth,
}
void Main::_handle_dns(Udp_packet &udp,
Size_guard &size_guard)
{
Dns_packet &dns = udp.data<Dns_packet>(size_guard);
if (!dns.response()) {
error("DNS message is not a response");
return; }
if (dns.id() != _dns_req_id) {
if (_verbose) {
log("unexpeted DNS request id in response"); }
return; }
try {
dns.for_each_entry(size_guard, [&] (Dns_packet::Dns_entry const &entry) {
if (_dst_ip == Ipv4_address()) {
_dst_ip = entry.addr;
if (_verbose) {
Genode::log(entry.name, " resolved to ", entry.addr); } } });
} catch (Size_guard::Exceeded const &) {
error("Malformated DNS response");
_dst_ip = Ipv4_address();
return;
}
if (_dst_ip == Ipv4_address()) {
if (_verbose) {
Genode::log(_dst_ns, " could not be resolved."); }
} else {
_send_sntp_request();
}
}
void Main::_send_arp_reply(Ethernet_frame &req_eth,
Arp_packet &req_arp)
{
@ -366,8 +433,78 @@ void Main::_broadcast_arp_request(Ipv4_address const &dst_ip)
}
void Main::_send_dns_request()
{
if (_ip_config->dns_server == Ipv4_address()) {
throw Invalid_arguments(); }
if (_verbose) {
Genode::log("Sending dns query for ", _dst_ns ," to ", ip_config().dns_server); }
/* if we do not yet know the Ethernet destination, request it via ARP */
if (_dst_mac == Mac_address()) {
if (ip_config().interface.prefix_matches(ip_config().dns_server)) {
_broadcast_arp_request(ip_config().dns_server); }
else {
_broadcast_arp_request(ip_config().gateway); }
return;
}
++_dns_req_id;
_nic.send(sizeof(Ethernet_frame) + sizeof(Ipv4_packet) +
sizeof(Udp_packet) + sizeof(Dns_packet) +
Dns_packet::sizeof_question(_dst_ns),
[&] (void *pkt_base, Size_guard &size_guard)
{
/* create ETH header */
Ethernet_frame &eth = Ethernet_frame::construct_at(pkt_base, size_guard);
eth.dst(_dst_mac);
eth.src(_nic.mac());
eth.type(Ethernet_frame::Type::IPV4);
/* create IP header */
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.src(ip_config().interface.address);
ip.dst(ip_config().dns_server);
/* adapt IP header to UDP */
ip.protocol(Ipv4_packet::Protocol::UDP);
/* create UDP header */
size_t const udp_off = size_guard.head_size();
Udp_packet &udp = ip.construct_at_data<Udp_packet>(size_guard);
udp.src_port(Port(SRC_PORT));
udp.dst_port(Port(Dns_packet::UDP_PORT));
/* create DNS header */
Dns_packet &dns = udp.construct_at_data<Dns_packet>(size_guard);
dns.id(_dns_req_id);
dns.recursion_desired(true);
dns.question(size_guard, _dst_ns);
/* finish UDP header */
udp.length(size_guard.head_size() - udp_off);
udp.update_checksum(ip.src(), ip.dst());
/* finish IP header */
ip.total_length(size_guard.head_size() - ip_off);
ip.update_checksum();
});
}
void Main::_send_sntp_request(Duration)
{
/* if we do not yet know the IP destination, resolve it */
if (_dst_ip == Ipv4_address() && _dst_ns != Domain_name()) {
_send_dns_request();
return;
}
/* if we do not yet know the Ethernet destination, request it via ARP */
if (_dst_mac == Mac_address()) {
if (ip_config().interface.prefix_matches(_dst_ip)) {

View File

@ -7,6 +7,14 @@
</xs:restriction>
</xs:simpleType><!-- Mac_address -->
<xs:simpleType name="Net_address">
<xs:restriction base="xs:string">
<xs:pattern value="([0-9A-Za-z\-]+(.[0-9A-Za-z\-]+)*.[0-9A-Za-z\-]+)|([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})"/>
<xs:minLength value="1"/>
<xs:maxLength value="160"/>
</xs:restriction>
</xs:simpleType><!-- Net_address -->
<xs:simpleType name="Ipv4_address">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}"/>