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_ */