diff --git a/repos/os/src/app/ping/README b/repos/os/src/app/ping/README index 97253783cb..4019a9b9fa 100644 --- a/repos/os/src/app/ping/README +++ b/repos/os/src/app/ping/README @@ -1,7 +1,7 @@ -The 'ping' component continuously sends ICMP Echo requests to a given IP host -and waits for the corresponding ICMP Echo replies. For each successfull ICMP -Echo handshake it prints a short statistic. The ICMP data field gets filled -with the letters of the alphabet ('a' to 'z') repeatedly. +The 'ping' component continuously sends ICMP Echo or UDP requests to a given +IP host and waits for the corresponding ICMP Echo or UDP replies. For each +successfull ICMP Echo or UDP handshake it prints a short statistic. The ICMP +data field gets filled with the letters of the alphabet ('a' to 'z') repeatedly. Configuration @@ -12,6 +12,8 @@ value for each attribute except 'config.dst_ip' and 'config.interface': ! @@ -37,6 +39,12 @@ This is a short description of the tags and attributes: :config.count: Optional. After how many successful pings the component exits successfully. +:config.dst_port: + Optional. Destination port resp. ICMP query ID to use. + +:config.protocol: + Optional. Protocol to ping with. Can be one of 'icmp', 'udp'. + Sessions ~~~~~~~~ diff --git a/repos/os/src/app/ping/config.xsd b/repos/os/src/app/ping/config.xsd index 7526ab2d24..154c376fe6 100644 --- a/repos/os/src/app/ping/config.xsd +++ b/repos/os/src/app/ping/config.xsd @@ -12,6 +12,20 @@ + + + + + + + + + + + + + + @@ -35,6 +49,8 @@ + + diff --git a/repos/os/src/app/ping/main.cc b/repos/os/src/app/ping/main.cc index e9106c651d..c2513439e9 100644 --- a/repos/os/src/app/ping/main.cc +++ b/repos/os/src/app/ping/main.cc @@ -15,6 +15,7 @@ #include #include #include +#include /* Genode includes */ #include @@ -51,10 +52,11 @@ class Main : public Nic_handler, using Periodic_timeout = Timer::Periodic_timeout
; enum { IPV4_TIME_TO_LIVE = 64 }; - enum { ICMP_ID = 1166 }; + enum { DEFAULT_DST_PORT = 50000 }; enum { ICMP_DATA_SIZE = 56 }; enum { DEFAULT_COUNT = 5 }; enum { DEFAULT_PERIOD_SEC = 5 }; + enum { SRC_PORT = 50000 }; Env &_env; Attached_rom_dataspace _config_rom { _env, "config" }; @@ -75,6 +77,8 @@ class Main : public Nic_handler, Reconstructible _ip_config { _config.attribute_value("interface", Ipv4_address_prefix()), _config.attribute_value("gateway", Ipv4_address()), Ipv4_address() }; + Protocol const _protocol { _config.attribute_value("protocol", Protocol::ICMP) }; + Port const _dst_port { _config.attribute_value("dst_port", Port(DEFAULT_DST_PORT)) }; void _handle_ip(Ethernet_frame ð, Size_guard &size_guard); @@ -82,6 +86,9 @@ class Main : public Nic_handler, void _handle_icmp(Ipv4_packet &ip, Size_guard &size_guard); + void _handle_udp(Ipv4_packet &ip, + Size_guard &size_guard); + void _handle_icmp_echo_reply(Ipv4_packet &ip, Icmp_packet &icmp, Size_guard &size_guard); @@ -203,7 +210,8 @@ void Main::_handle_ip(Ethernet_frame ð, } /* select IP sub-protocol */ switch (ip.protocol()) { - case Ipv4_packet::Protocol::ICMP: _handle_icmp(ip, size_guard); + case Ipv4_packet::Protocol::ICMP: _handle_icmp(ip, size_guard); break; + case Ipv4_packet::Protocol::UDP: _handle_udp(ip, size_guard); break; default: ; } } @@ -212,6 +220,12 @@ void Main::_handle_icmp_echo_reply(Ipv4_packet &ip, Icmp_packet &icmp, Size_guard &size_guard) { + /* drop packet if our request was no ICMP */ + if (_protocol != Protocol::ICMP) { + if (_verbose) { + log("bad IP protocol"); } + return; + } /* check IP source */ if (ip.src() != _dst_ip) { if (_verbose) { @@ -226,7 +240,7 @@ void Main::_handle_icmp_echo_reply(Ipv4_packet &ip, } /* check ICMP identifier */ uint16_t const icmp_id = icmp.query_id(); - if (icmp_id != ICMP_ID) { + if (icmp_id != _dst_port.value) { if (_verbose) { log("bad ICMP identifier"); } return; @@ -285,27 +299,58 @@ void Main::_handle_icmp_dst_unreachbl(Ipv4_packet &ip, log("bad IP checksum in payload of ICMP error"); } return; } - /* drop packet if the ICMP error is not about ICMP */ - if (embed_ip.protocol() != Ipv4_packet::Protocol::ICMP) { - if (_verbose) { - log("bad IP protocol in payload of ICMP error"); } - return; + /* select IP-encapsulated protocol */ + switch (_protocol) { + case Protocol::ICMP: + { + /* drop packet if the ICMP error is not about ICMP */ + if (embed_ip.protocol() != Ipv4_packet::Protocol::ICMP) { + if (_verbose) { + log("bad IP protocol in payload of ICMP error"); } + return; + } + /* drop packet if embedded ICMP identifier is invalid */ + Icmp_packet &embed_icmp = embed_ip.data(size_guard); + if (embed_icmp.query_id() != _dst_port.value) { + if (_verbose) { + log("bad ICMP identifier in payload of ICMP error"); } + return; + } + /* drop packet if embedded ICMP sequence number is invalid */ + uint16_t const embed_icmp_seq = embed_icmp.query_seq(); + if (embed_icmp_seq != _icmp_seq) { + if (_verbose) { + log("bad ICMP sequence number in payload of ICMP error"); } + return; + } + log("From ", ip.src(), " icmp_seq=", embed_icmp_seq, " Destination Unreachable"); + break; + } + case Protocol::UDP: + { + /* drop packet if the ICMP error is not about UDP */ + if (embed_ip.protocol() != Ipv4_packet::Protocol::UDP) { + if (_verbose) { + log("bad IP protocol in payload of ICMP error"); } + return; + } + /* drop packet if embedded UDP source port is invalid */ + Udp_packet &embed_udp = embed_ip.data(size_guard); + if (embed_udp.src_port().value != SRC_PORT) { + if (_verbose) { + log("bad UDP source port in payload of ICMP error"); } + return; + } + /* drop packet if embedded UDP destination port is invalid */ + if (embed_udp.dst_port().value != _dst_port.value) { + if (_verbose) { + log("bad UDP destination port in payload of ICMP error"); } + return; + } + log("From ", ip.src(), " Destination Unreachable"); + break; + } } - /* drop packet if embedded ICMP identifier is invalid */ - Icmp_packet &embed_icmp = embed_ip.data(size_guard); - if (embed_icmp.query_id() != ICMP_ID) { - if (_verbose) { - log("bad ICMP identifier in payload of ICMP error"); } - return; - } - /* drop packet if embedded ICMP sequence number is invalid */ - uint16_t const embed_icmp_seq = embed_icmp.query_seq(); - if (embed_icmp_seq != _icmp_seq) { - if (_verbose) { - log("bad ICMP sequence number in payload of ICMP error"); } - return; - } - log("From ", ip.src(), " icmp_seq=", embed_icmp_seq, " Destination Unreachable"); } @@ -331,6 +376,50 @@ void Main::_handle_icmp(Ipv4_packet &ip, } +void Main::_handle_udp(Ipv4_packet &ip, + Size_guard &size_guard) +{ + /* drop packet if our request was no UDP */ + if (_protocol != Protocol::UDP) { + if (_verbose) { + log("bad IP protocol"); } + return; + } + /* drop packet if UDP checksum is invalid */ + Udp_packet &udp = ip.data(size_guard); + if (udp.checksum_error(ip.src(), ip.dst())) { + if (_verbose) { + log("bad UDP checksum"); } + return; + } + /* drop packet if UDP source port is invalid */ + if (udp.src_port().value != _dst_port.value) { + if (_verbose) { + log("bad UDP source port"); } + return; + } + /* drop packet if UDP destination port is invalid */ + if (udp.dst_port().value != SRC_PORT) { + if (_verbose) { + log("bad UDP destination port"); } + return; + } + /* calculate time since the request was sent */ + unsigned long time_us = _timer.curr_time().trunc_to_plain_us().value - _send_time.value; + unsigned long const time_ms = time_us / 1000UL; + time_us = time_us - time_ms * 1000UL; + + /* print success message */ + log(udp.length(), " bytes from ", ip.src(), " ttl=", (unsigned long)IPV4_TIME_TO_LIVE, + " time=", time_ms, ".", time_us ," ms"); + + /* check exit condition */ + _count--; + if (!_count) { + _env.parent().exit(0); } +} + + void Main::_handle_arp(Ethernet_frame ð, Size_guard &size_guard) { @@ -450,27 +539,53 @@ void Main::_send_ping(Duration) ip.header_length(sizeof(Ipv4_packet) / 4); ip.version(4); ip.time_to_live(IPV4_TIME_TO_LIVE); - ip.protocol(Ipv4_packet::Protocol::ICMP); ip.src(ip_config().interface.address); ip.dst(_dst_ip); - /* create ICMP header */ - Icmp_packet &icmp = ip.construct_at_data(size_guard); - icmp.type(Icmp_packet::Type::ECHO_REQUEST); - icmp.code(Icmp_packet::Code::ECHO_REQUEST); - icmp.query_id(ICMP_ID); - icmp.query_seq(_icmp_seq); + /* select IP-encapsulated protocol */ + switch (_protocol) { + case Protocol::ICMP: + { + /* adapt IP header to ICMP */ + ip.protocol(Ipv4_packet::Protocol::ICMP); - /* fill ICMP data with characters from 'a' to 'z' */ - struct Data { char chr[ICMP_DATA_SIZE]; }; - Data &data = icmp.data(size_guard); - char chr = 'a'; - for (addr_t chr_id = 0; chr_id < ICMP_DATA_SIZE; chr_id++) { - data.chr[chr_id] = chr; - chr = chr < 'z' ? chr + 1 : 'a'; + /* create ICMP header */ + Icmp_packet &icmp = ip.construct_at_data(size_guard); + icmp.type(Icmp_packet::Type::ECHO_REQUEST); + icmp.code(Icmp_packet::Code::ECHO_REQUEST); + icmp.query_id(_dst_port.value); + icmp.query_seq(_icmp_seq); + + /* fill ICMP data with characters from 'a' to 'z' */ + struct Data { char chr[ICMP_DATA_SIZE]; }; + Data &data = icmp.data(size_guard); + char chr = 'a'; + for (addr_t chr_id = 0; chr_id < ICMP_DATA_SIZE; chr_id++) { + data.chr[chr_id] = chr; + chr = chr < 'z' ? chr + 1 : 'a'; + } + /* finish ICMP header */ + icmp.update_checksum(ICMP_DATA_SIZE); + break; + } + case Protocol::UDP: + { + /* 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(size_guard); + udp.src_port(Port(SRC_PORT)); + udp.dst_port(_dst_port); + + /* finish UDP header */ + udp.length(size_guard.head_size() - udp_off); + udp.update_checksum(ip.src(), ip.dst()); + break; + } } - /* fill in header values that require the packet to be complete */ - icmp.update_checksum(ICMP_DATA_SIZE); + /* finish IP header */ ip.total_length(size_guard.head_size() - ip_off); ip.update_checksum(); }); diff --git a/repos/os/src/app/ping/protocol.h b/repos/os/src/app/ping/protocol.h new file mode 100644 index 0000000000..03e774d8dd --- /dev/null +++ b/repos/os/src/app/ping/protocol.h @@ -0,0 +1,32 @@ +/* + * \brief Supported protocols + * \author Martin Stein + * \date 2018-03-27 + */ + +/* + * 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 __PROTOCOL_H_ +#define __PROTOCOL_H_ + +/* Genode includes */ +#include + +namespace Genode { enum class Protocol : uint16_t { ICMP, UDP }; } + +namespace Genode +{ + inline size_t ascii_to(char const *s, Protocol &result) + { + if (!strcmp(s, "icmp", 4)) { result = Protocol::ICMP; return 4; } + if (!strcmp(s, "udp", 3)) { result = Protocol::UDP; return 3; } + return 0; + } +} + +#endif /* __PROTOCOL_H_ */