From 32b081eeac71d27862e5bcc7b359270d0ca25d85 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 3 Mar 2017 16:27:45 +0100 Subject: [PATCH 01/40] ICMP: Renamed icmpv4.hpp/cpp -> icmp4.hpp/cpp Ping functionality added and working on other ICMP messages --- api/net/inet.hpp | 2 + api/net/inet4.hpp | 8 +- api/net/inet64.hpp | 4 +- api/net/ip4/{icmpv4.hpp => icmp4.hpp} | 30 ++++- api/net/ip4/packet_icmp4.hpp | 41 +++++- src/CMakeLists.txt | 2 +- src/net/ip4/icmp4.cpp | 187 ++++++++++++++++++++++++++ src/net/ip4/icmpv4.cpp | 78 ----------- 8 files changed, 265 insertions(+), 87 deletions(-) rename api/net/ip4/{icmpv4.hpp => icmp4.hpp} (50%) create mode 100644 src/net/ip4/icmp4.cpp delete mode 100644 src/net/ip4/icmpv4.cpp diff --git a/api/net/inet.hpp b/api/net/inet.hpp index f06cc705ba..e1bac98c87 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -101,6 +101,8 @@ namespace net { virtual void move_to_this_cpu() = 0; + virtual void ping(typename IPV::addr ip) = 0; + }; //< class Inet } //< namespace net diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index c4f67f2857..2a2dcf887c 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -22,7 +22,7 @@ #include "ip4/arp.hpp" #include "ip4/ip4.hpp" #include "ip4/udp.hpp" -#include "ip4/icmpv4.hpp" +#include "ip4/icmp4.hpp" #include "dns/client.hpp" #include "tcp/tcp.hpp" #include @@ -194,6 +194,12 @@ namespace net { void move_to_this_cpu() override; + virtual void + ping(IP4::addr ip) override + { + icmp_.ping_request(ip); + } + /** Return the stack on the given Nic */ template static auto&& stack() diff --git a/api/net/inet64.hpp b/api/net/inet64.hpp index 1c3ec49d8e..9f43519e85 100644 --- a/api/net/inet64.hpp +++ b/api/net/inet64.hpp @@ -32,7 +32,7 @@ #include #include -#include "ip4/icmpv4.hpp" +#include "ip4/icmp4.hpp" namespace net { @@ -96,7 +96,7 @@ namespace net { static Inet& up() { if (_ip4_list.size() < 1) - printf(" Is bringing up an IP stack without any IP addresses"); + printf(" Is bringing up an IP stack without any IP addresses"); static Inet instance; return instance; } diff --git a/api/net/ip4/icmpv4.hpp b/api/net/ip4/icmp4.hpp similarity index 50% rename from api/net/ip4/icmpv4.hpp rename to api/net/ip4/icmp4.hpp index f177ced327..062908803c 100644 --- a/api/net/ip4/icmpv4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -36,13 +36,41 @@ namespace net { inline void set_network_out(downstream s) { network_layer_out_ = s; }; + void destination_unreachable(icmp4::Packet& req, icmp4::code::Dest_unreachable code); + + void time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code); + + void parameter_problem(icmp4::Packet& req); + + void source_quench(icmp4::Packet& req); + + void redirect(icmp4::Packet& req, icmp4::code::Redirect code); + + void timestamp_request(IP4::addr ip); + void timestamp_reply(icmp4::Packet& req); + + void information_request(IP4::addr ip); + void information_reply(icmp4::Packet& req); + + void ping_request(IP4::addr ip); + private: Stack& inet_; - downstream network_layer_out_ = nullptr; + downstream network_layer_out_ = nullptr; + uint8_t includeos_payload_[48] = {'I','N','C','L','U','D', + 'E','O','S',1,2,3,4,5, + 'A','B','C','D','E','F','G','H', + 'I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X', + 'Y','Z',1,2,3,4,5,6, + 7,8}; void ping_reply(icmp4::Packet&); + void send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); + void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code); + }; //< class ICMPv4 } //< namespace net diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 23f7eccb35..4ca8f1ee24 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -22,9 +22,43 @@ namespace net { namespace icmp4 { - // Known ICMP types - enum class Type : uint8_t - { ECHO_REPLY, ECHO = 8 }; + // ICMP types + enum class Type : uint8_t { + ECHO_REPLY, + DEST_UNREACHABLE = 3, + SRC_QUENCH = 4, + REDIRECT = 5, + ECHO = 8, + TIME_EXCEEDED = 11, + PARAMETER_PROBLEM = 12, + TIMESTAMP = 13, + TIMESTAMP_REPLY = 14, + INFO_REQUEST = 15, + INFO_REPLY = 16 + }; + + namespace code { + enum class Dest_unreachable : uint8_t { + NET, + HOST, + PROTOCOL, + PORT, + FRAGMENTATION, + SRC_ROUTE + }; + + enum class Redirect : uint8_t { + NET, + HOST, + TOS_NET, + TOS_HOST + }; + + enum class Time_exceeded : uint8_t { + TTL, + FRAGMENT_REASSEMBLY + }; + } // < namespace code class Packet { @@ -122,7 +156,6 @@ namespace icmp4 { private: IP4::IP_packet_ptr pckt_; - }; } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b4cff5665a..40680b7587 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,7 +44,7 @@ set(OS_OBJECTS net/ethernet/ethernet.cpp net/inet_common.cpp net/ip4/arp.cpp net/ip4/ip4.cpp net/tcp/tcp.cpp net/tcp/connection.cpp net/tcp/connection_states.cpp net/tcp/write_queue.cpp net/tcp/rttm.cpp net/tcp/listener.cpp - net/ip4/icmpv4.cpp net/ip4/udp.cpp net/ip4/udp_socket.cpp + net/ip4/icmp4.cpp net/ip4/udp.cpp net/ip4/udp_socket.cpp net/dns/dns.cpp net/dns/client.cpp net/dhcp/dh4client.cpp net/dhcp/dhcpd.cpp net/buffer_store.cpp net/inet4.cpp net/super_stack.cpp diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp new file mode 100644 index 0000000000..1eac0a788d --- /dev/null +++ b/src/net/ip4/icmp4.cpp @@ -0,0 +1,187 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2015 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// #define DEBUG +#include + +namespace net { + + ICMPv4::ICMPv4(Stack& inet) : + inet_{inet} + {} + + void ICMPv4::receive(Packet_ptr pckt) { + if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header + return; + + auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); + auto req = icmp4::Packet(std::move(pckt_ip4)); + + switch(req.type()) { + case (icmp4::Type::DEST_UNREACHABLE): + debug(" DESTINATION UNREACHABLE from %s\n", req.ip().src().str().c_str()); + // TODO + break; + case (icmp4::Type::SRC_QUENCH): + debug(" SOURCE QUENCH from %s\n", req.ip().src().str().c_str()); + // TODO + break; + case (icmp4::Type::REDIRECT): + debug(" REDIRECT from %s\n", req.ip().src().str().c_str()); + // TODO + break; + case (icmp4::Type::ECHO): + debug(" PING from %s\n", req.ip().src().str().c_str()); + ping_reply(req); + break; + case (icmp4::Type::TIME_EXCEEDED): + debug(" TIME EXCEEDED from %s\n", req.ip().src().str().c_str()); + // TODO + break; + case (icmp4::Type::PARAMETER_PROBLEM): + debug(" PARAMETER PROBLEM from %s\n", req.ip().src().str().c_str()); + // TODO + break; + case (icmp4::Type::TIMESTAMP): + debug(" TIMESTAMP from %s\n", req.ip().src().str().c_str()); + // TODO + timestamp_reply(req); + break; + case (icmp4::Type::TIMESTAMP_REPLY): + debug(" TIMESTAMP REPLY from %s\n", req.ip().src().str().c_str()); + // TODO + break; + case (icmp4::Type::INFO_REQUEST): + debug(" INFO REQUEST from %s\n", req.ip().src().str().c_str()); + // TODO + information_reply(req); + break; + case (icmp4::Type::INFO_REPLY): + debug(" INFO REPLY from %s\n", req.ip().src().str().c_str()); + // TODO + break; + case (icmp4::Type::ECHO_REPLY): + debug(" PING Reply from %s\n", req.ip().src().str().c_str()); + // TODO + break; + } + } + + void ICMPv4::destination_unreachable(icmp4::Packet& req, icmp4::code::Dest_unreachable code) { + send_response(req, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code); + } + + void ICMPv4::time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code) { + send_response(req, icmp4::Type::TIME_EXCEEDED, (uint8_t) code); + } + + void ICMPv4::parameter_problem(icmp4::Packet& req) { + send_response(req, icmp4::Type::PARAMETER_PROBLEM, 0); + } + + void ICMPv4::source_quench(icmp4::Packet& req) { + send_response(req, icmp4::Type::SRC_QUENCH, 0); + } + + void ICMPv4::redirect(icmp4::Packet& req, icmp4::code::Redirect code) { + send_response(req, icmp4::Type::REDIRECT, (uint8_t) code); + } + + void ICMPv4::timestamp_request(IP4::addr ip) { + // TODO + // send_request(ip, icmp4::Type::TIMESTAMP, 0, icmp4::Packet::Span(, )); + } + + void ICMPv4::timestamp_reply(icmp4::Packet& req) { + // TODO + // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0); + } + + void ICMPv4::information_request(IP4::addr ip) { + // TODO + // send_request(ip, icmp4::Type::INFO_REQUEST, 0, icmp4::Packet::Span(, )); + } + + void ICMPv4::information_reply(icmp4::Packet& req) { + send_response(req, icmp4::Type::INFO_REPLY, 0); + } + + void ICMPv4::ping_request(IP4::addr ip) { + send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48)); + } + + void ICMPv4::ping_reply(icmp4::Packet& req) { + send_response(req, icmp4::Type::ECHO_REPLY, 0); + } + + void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload) { + // Provision new IP4-packet + icmp4::Packet req(inet_.ip_packet_factory()); + + // Populate request IP header + req.ip().set_src(inet_.ip_addr()); + req.ip().set_dst(dest_ip); + + // Populate request ICMP header + req.set_type(icmp4::Type::ECHO); + req.set_code(code); + req.set_id(0); + req.set_sequence(0); + + debug(" Transmitting request to %s\n", dest_ip.to_string().c_str()); + + // Payload + req.set_payload(payload); + + // Add checksum + req.set_checksum(); + + debug(" Request size: %i payload size: %i, checksum: 0x%x\n", + req.ip().size(), req.payload().size(), req.compute_checksum()); + + network_layer_out_(req.release()); + } + + void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code) { + // Provision new IP4-packet + icmp4::Packet res(inet_.ip_packet_factory()); + + // Populate response IP header + res.ip().set_src(inet_.ip_addr()); + res.ip().set_dst(req.ip().src()); + + // Populate response ICMP header + res.set_type(type); + res.set_code(code); + res.set_id(req.id()); + res.set_sequence(req.sequence()); + + debug(" Transmitting answer to %s\n", res.ip().dst().str().c_str()); + + // Payload + res.set_payload(req.payload()); + + // Add checksum + res.set_checksum(); + + debug(" Response size: %i payload size: %i, checksum: 0x%x\n", + res.ip().size(), res.payload().size(), res.compute_checksum()); + + network_layer_out_(res.release()); + } + +} //< namespace net diff --git a/src/net/ip4/icmpv4.cpp b/src/net/ip4/icmpv4.cpp deleted file mode 100644 index 681666584c..0000000000 --- a/src/net/ip4/icmpv4.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// This file is a part of the IncludeOS unikernel - www.includeos.org -// -// Copyright 2015 Oslo and Akershus University College of Applied Sciences -// and Alfred Bratterud -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// #define DEBUG -#include - -namespace net { - - ICMPv4::ICMPv4(Stack& inet) : - inet_{inet} -{} - - void ICMPv4::receive(Packet_ptr pckt) { - - if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header - return; - - auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); - auto req = icmp4::Packet(std::move(pckt_ip4)); - - switch(req.type()) { - - case (icmp4::Type::ECHO): - debug(" PING from %s\n", req.ip().src().str().c_str()); - ping_reply(req); - break; - - case (icmp4::Type::ECHO_REPLY): - debug(" PING Reply from %s\n", req.ip().src().str().c_str()); - break; - } - } - - void ICMPv4::ping_reply(icmp4::Packet& req) { - - // Provision new IP4-packet - icmp4::Packet res(inet_.ip_packet_factory()); - - // Populate response IP header - res.ip().set_src(inet_.ip_addr()); - res.ip().set_dst(req.ip().src()); - - // Populate response ICMP header - res.set_type(icmp4::Type::ECHO_REPLY); - res.set_code(0); - res.set_id(req.id()); - res.set_sequence(req.sequence()); - - debug(" Transmitting answer to %s\n", res.ip().dst().str().c_str()); - - // Copy payload from old to new packet - res.set_payload(req.payload()); - - // Add checksum - res.set_checksum(); - - debug(" Response size: %i payload size: %i, checksum: 0x%x\n", - res.ip().size(), res.payload().size(), res.compute_checksum()); - - network_layer_out_(res.release()); - } - - -} //< namespace net From f2365b90aa4c835735ea5b97e7892651d8be823e Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Mon, 6 Mar 2017 17:54:37 +0100 Subject: [PATCH 02/40] Statman: End-iterators now correct, next_available_ now correct if creation of Stat throws Exception, empty name-input to get-method now throws immediate Exception --- api/util/statman.hpp | 9 +++++---- src/util/statman.cpp | 34 ++++++++++++++++++---------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/api/util/statman.hpp b/api/util/statman.hpp index ff07095d7c..adff491686 100644 --- a/api/util/statman.hpp +++ b/api/util/statman.hpp @@ -136,8 +136,9 @@ class Statman { using Span = gsl::span; public: - using Size_type = ptrdiff_t; - using Span_iterator = Span::iterator; + using Size_type = ptrdiff_t; + using Span_iterator = Span::iterator; + using Span_citerator = Span::const_iterator; /// /// @@ -212,7 +213,7 @@ class Statman { /// /// auto end() noexcept - { return stats_.end(); } + { return Span_iterator(&stats_, next_available_); } /// /// @@ -224,7 +225,7 @@ class Statman { /// /// auto cend() const noexcept - { return stats_.cend(); } + { return Span_citerator(&stats_, next_available_); } /// /// diff --git a/src/util/statman.cpp b/src/util/statman.cpp index 35eb9a5109..c78d7658b1 100644 --- a/src/util/statman.cpp +++ b/src/util/statman.cpp @@ -23,9 +23,8 @@ Stat::Stat(const Stat_type type, const int index_into_span, const std::string& n : type_{type} , index_into_span_{index_into_span} { - if(name.size() > MAX_NAME_LEN) { + if(name.size() > MAX_NAME_LEN) throw Stats_exception{"Creating stat: Name cannot be longer than " + std::to_string(MAX_NAME_LEN) + " characters"}; - } switch (type) { case UINT32: ui32 = 0; break; @@ -49,36 +48,32 @@ void Stat::operator++() { /////////////////////////////////////////////////////////////////////////////// float& Stat::get_float() { - if(type_ not_eq FLOAT) { + if(type_ not_eq FLOAT) throw Stats_exception{"Get stat: Stat_type is not a float"}; - } return f; } /////////////////////////////////////////////////////////////////////////////// uint32_t& Stat::get_uint32() { - if(type_ not_eq UINT32) { + if(type_ not_eq UINT32) throw Stats_exception{"Get stat: Stat_type is not an uint32_t"}; - } return ui32; } /////////////////////////////////////////////////////////////////////////////// uint64_t& Stat::get_uint64() { - if(type_ not_eq UINT64) { + if(type_ not_eq UINT64) throw Stats_exception{"Get stat: Stat_type is not an uint64_t"}; - } return ui64; } /////////////////////////////////////////////////////////////////////////////// Statman::Statman(const uintptr_t start, const Size_type num_bytes) { - if(num_bytes < 0) { + if(num_bytes < 0) throw Stats_exception{"Creating Statman: A negative number of bytes has been given"}; - } const Size_type num_stats_in_span = num_bytes / sizeof(Stat); @@ -98,20 +93,27 @@ Statman::Span_iterator Statman::last_used() { /////////////////////////////////////////////////////////////////////////////// Stat& Statman::create(const Stat::Stat_type type, const std::string& name) { + if (name.empty()) + throw Stats_exception{"Cannot create Stat with no name"}; + const int idx = next_available_; - if(idx >= stats_.size()) { + if(idx >= stats_.size()) throw Stats_out_of_memory{}; - } - return (*new (&stats_[next_available_++]) Stat{type, idx, name}); + auto& stat = (*new (&stats_[idx]) Stat{type, idx, name}); // Can throw + next_available_++; + return stat; } /////////////////////////////////////////////////////////////////////////////// Stat& Statman::get(const std::string& name) { - for (auto& stat : stats_) - if (stat.name() == name) - return stat; + if (name.empty()) + throw Stats_exception{"Stat name is empty"}; + + for (auto it = begin(); it != end(); ++it) + if ((*it).name() == name) + return (*it); throw Stats_exception{"No stat with name " + name + " exists"}; } From bc6b61b86c2ccda8eefb1a37d5decbfec5832876 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Mon, 6 Mar 2017 17:57:42 +0100 Subject: [PATCH 03/40] ICMP4: Added RFC 1122 codes --- api/net/ip4/packet_icmp4.hpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 4ca8f1ee24..c1940abac9 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -44,7 +44,14 @@ namespace icmp4 { PROTOCOL, PORT, FRAGMENTATION, - SRC_ROUTE + SRC_ROUTE, + NET_UNKNOWN, // RFC 1122 + HOST_UNKNOWN, + SRC_HOST_ISOLATED, + NET_PROHIBITED, + HOST_PROHIBITED, + NET_FOR_TOS, + HOST_FOR_TOS }; enum class Redirect : uint8_t { @@ -58,6 +65,12 @@ namespace icmp4 { TTL, FRAGMENT_REASSEMBLY }; + + enum class Parameter_problem : uint8_t { + POINTER_INDICATES_ERROR, + REQUIRED_OPT_MISSING // RFC 1122 + }; + } // < namespace code class Packet { From 6252d018dfe2b8f09d22c0f39ebbce5fd50baed6 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Mon, 6 Mar 2017 19:12:58 +0100 Subject: [PATCH 04/40] ICMP: Added more Destination Unreachable and Parameter Problem codes (RFC 1122). Removed deprecated ICMP messages (Source Quench and Information - RFC 6918 and 6633) --- api/net/inet4.hpp | 3 +-- api/net/ip4/icmp4.hpp | 9 ++----- api/net/ip4/packet_icmp4.hpp | 7 ++---- src/net/ip4/icmp4.cpp | 48 ++++++++++-------------------------- 4 files changed, 18 insertions(+), 49 deletions(-) diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index 2a2dcf887c..de2331c356 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -195,8 +195,7 @@ namespace net { void move_to_this_cpu() override; virtual void - ping(IP4::addr ip) override - { + ping(IP4::addr ip) override { icmp_.ping_request(ip); } diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index 062908803c..d9f704e85c 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -38,20 +38,15 @@ namespace net { void destination_unreachable(icmp4::Packet& req, icmp4::code::Dest_unreachable code); + void redirect(icmp4::Packet& req, icmp4::code::Redirect code); + void time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code); void parameter_problem(icmp4::Packet& req); - void source_quench(icmp4::Packet& req); - - void redirect(icmp4::Packet& req, icmp4::code::Redirect code); - void timestamp_request(IP4::addr ip); void timestamp_reply(icmp4::Packet& req); - void information_request(IP4::addr ip); - void information_reply(icmp4::Packet& req); - void ping_request(IP4::addr ip); private: diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index c1940abac9..f93c9f3d5d 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -26,15 +26,12 @@ namespace icmp4 { enum class Type : uint8_t { ECHO_REPLY, DEST_UNREACHABLE = 3, - SRC_QUENCH = 4, REDIRECT = 5, ECHO = 8, TIME_EXCEEDED = 11, PARAMETER_PROBLEM = 12, TIMESTAMP = 13, - TIMESTAMP_REPLY = 14, - INFO_REQUEST = 15, - INFO_REPLY = 16 + TIMESTAMP_REPLY = 14 }; namespace code { @@ -45,7 +42,7 @@ namespace icmp4 { PORT, FRAGMENTATION, SRC_ROUTE, - NET_UNKNOWN, // RFC 1122 + NET_UNKNOWN, // RFC 1122 HOST_UNKNOWN, SRC_HOST_ISOLATED, NET_PROHIBITED, diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index 1eac0a788d..fff09be35d 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -32,17 +32,19 @@ namespace net { auto req = icmp4::Packet(std::move(pckt_ip4)); switch(req.type()) { - case (icmp4::Type::DEST_UNREACHABLE): - debug(" DESTINATION UNREACHABLE from %s\n", req.ip().src().str().c_str()); + case (icmp4::Type::ECHO_REPLY): + debug(" PING Reply from %s\n", req.ip().src().str().c_str()); // TODO break; - case (icmp4::Type::SRC_QUENCH): - debug(" SOURCE QUENCH from %s\n", req.ip().src().str().c_str()); + case (icmp4::Type::DEST_UNREACHABLE): + debug(" DESTINATION UNREACHABLE from %s\n", req.ip().src().str().c_str()); // TODO + // Send to transport layer break; case (icmp4::Type::REDIRECT): debug(" REDIRECT from %s\n", req.ip().src().str().c_str()); // TODO + // Only sent by gateways. Update routing information based on the message break; case (icmp4::Type::ECHO): debug(" PING from %s\n", req.ip().src().str().c_str()); @@ -51,10 +53,12 @@ namespace net { case (icmp4::Type::TIME_EXCEEDED): debug(" TIME EXCEEDED from %s\n", req.ip().src().str().c_str()); // TODO + // Send to transport layer break; case (icmp4::Type::PARAMETER_PROBLEM): debug(" PARAMETER PROBLEM from %s\n", req.ip().src().str().c_str()); // TODO + // Send to transport layer break; case (icmp4::Type::TIMESTAMP): debug(" TIMESTAMP from %s\n", req.ip().src().str().c_str()); @@ -65,19 +69,6 @@ namespace net { debug(" TIMESTAMP REPLY from %s\n", req.ip().src().str().c_str()); // TODO break; - case (icmp4::Type::INFO_REQUEST): - debug(" INFO REQUEST from %s\n", req.ip().src().str().c_str()); - // TODO - information_reply(req); - break; - case (icmp4::Type::INFO_REPLY): - debug(" INFO REPLY from %s\n", req.ip().src().str().c_str()); - // TODO - break; - case (icmp4::Type::ECHO_REPLY): - debug(" PING Reply from %s\n", req.ip().src().str().c_str()); - // TODO - break; } } @@ -85,6 +76,10 @@ namespace net { send_response(req, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code); } + void ICMPv4::redirect(icmp4::Packet& req, icmp4::code::Redirect code) { + send_response(req, icmp4::Type::REDIRECT, (uint8_t) code); + } + void ICMPv4::time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code) { send_response(req, icmp4::Type::TIME_EXCEEDED, (uint8_t) code); } @@ -93,14 +88,6 @@ namespace net { send_response(req, icmp4::Type::PARAMETER_PROBLEM, 0); } - void ICMPv4::source_quench(icmp4::Packet& req) { - send_response(req, icmp4::Type::SRC_QUENCH, 0); - } - - void ICMPv4::redirect(icmp4::Packet& req, icmp4::code::Redirect code) { - send_response(req, icmp4::Type::REDIRECT, (uint8_t) code); - } - void ICMPv4::timestamp_request(IP4::addr ip) { // TODO // send_request(ip, icmp4::Type::TIMESTAMP, 0, icmp4::Packet::Span(, )); @@ -111,15 +98,6 @@ namespace net { // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0); } - void ICMPv4::information_request(IP4::addr ip) { - // TODO - // send_request(ip, icmp4::Type::INFO_REQUEST, 0, icmp4::Packet::Span(, )); - } - - void ICMPv4::information_reply(icmp4::Packet& req) { - send_response(req, icmp4::Type::INFO_REPLY, 0); - } - void ICMPv4::ping_request(IP4::addr ip) { send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48)); } @@ -137,7 +115,7 @@ namespace net { req.ip().set_dst(dest_ip); // Populate request ICMP header - req.set_type(icmp4::Type::ECHO); + req.set_type(type); req.set_code(code); req.set_id(0); req.set_sequence(0); From e4dc73d3cfe7f4c891d1370da7e435eec38c4bb8 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Tue, 7 Mar 2017 15:56:15 +0100 Subject: [PATCH 05/40] Test CMakeLists.txt: icmpv4.cpp -> icmp4.cpp --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cb054decb6..71cc03a87d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -137,7 +137,7 @@ set(OS_SOURCES ${SRC}/net/http/version.cpp ${SRC}/net/inet_common.cpp ${SRC}/net/ip4/arp.cpp - ${SRC}/net/ip4/icmpv4.cpp + ${SRC}/net/ip4/icmp4.cpp ${SRC}/net/ip4/ip4.cpp ${SRC}/net/ip4/udp.cpp ${SRC}/net/ip4/udp_socket.cpp From 2b920fd9a835fa5111b9fa392ad676d8bfa5b173 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 12:04:17 +0100 Subject: [PATCH 06/40] Statman unit test: Filling statman with stats using statman_.size() instead of previously updated statman_.end() iterator --- test/util/unit/statman.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/util/unit/statman.cpp b/test/util/unit/statman.cpp index ebd66c31fe..cef04b2485 100644 --- a/test/util/unit/statman.cpp +++ b/test/util/unit/statman.cpp @@ -185,14 +185,12 @@ CASE( "Filling Statman with Stats and running through Statman using iterators be EXPECT_NOT(statman_.full()); EXPECT(statman_.num_stats() == 0); - AND_WHEN( "Statman is filled with Stats using Statman iterators begin and end" ) + AND_WHEN( "Statman is filled with Stats" ) { EXPECT(statman_.empty()); EXPECT(statman_.size() == expected_num_elements); - int i = 0; - - for (auto it = statman_.begin(); it != statman_.end(); ++it) + for (int i = 0; i < statman_.size(); i++) { EXPECT(statman_.num_stats() == i); @@ -207,8 +205,6 @@ CASE( "Filling Statman with Stats and running through Statman using iterators be Stat& stat = statman_.create(Stat::FLOAT, "net.tcp." + std::to_string(i)); ++stat; } - - i++; } THEN("Statman is full and the Stats can be displayed using Statman iterators begin and end") From ce3fc68757957cd026782243183a7c3c223604b5 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 14:56:17 +0100 Subject: [PATCH 07/40] Inet: Getter for ICMPv4 object --- api/net/inet.hpp | 31 ++++++++++++++++--------------- api/net/inet4.hpp | 3 +++ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/api/net/inet.hpp b/api/net/inet.hpp index f6c837f843..34324b0930 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -24,17 +24,17 @@ namespace net { - - class TCP; - class UDP; - class DHClient; + class TCP; + class UDP; + class DHClient; + struct ICMPv4; /** * An abstract IP-stack interface. * Provides a common interface for IPv4 and (future) IPv6, simplified with * no constructors etc. **/ - template + template struct Inet { using Stack = Inet; using Forward_delg = delegate; @@ -44,16 +44,17 @@ namespace net { template using resolve_func = delegate; - virtual typename IPV::addr ip_addr() = 0; - virtual typename IPV::addr netmask() = 0; - virtual typename IPV::addr gateway() = 0; - virtual std::string ifname() const = 0; - virtual MAC::Addr link_addr() = 0; - virtual hw::Nic& nic() = 0; - - virtual IPV& ip_obj() = 0; - virtual TCP& tcp() = 0; - virtual UDP& udp() = 0; + virtual typename IPV::addr ip_addr() = 0; + virtual typename IPV::addr netmask() = 0; + virtual typename IPV::addr gateway() = 0; + virtual std::string ifname() const = 0; + virtual MAC::Addr link_addr() = 0; + virtual hw::Nic& nic() = 0; + + virtual IPV& ip_obj() = 0; + virtual TCP& tcp() = 0; + virtual UDP& udp() = 0; + virtual ICMPv4& icmp() = 0; virtual void set_forward_delg(Forward_delg) = 0; virtual void set_route_checker(Route_checker) = 0; diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index 5754a1178d..cdfa253096 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -68,6 +68,9 @@ namespace net { /** Get the UDP-object belonging to this stack */ UDP& udp() override { return udp_; } + /** Get the ICMP-object belonging to this stack */ + ICMPv4& icmp() override { return icmp_; } + /** Get the DHCP client (if any) */ auto dhclient() { return dhcp_; } From ef913370f26b440a91181270b054ed1eec7c8e2f Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 14:58:39 +0100 Subject: [PATCH 08/40] Inet common and packet_icmp4: Moved icmp4 codes into inet_common.hpp so that UDP and IP4 have access to them --- api/net/inet_common.hpp | 40 ++++++++++++++++++++++++++++++++++++ api/net/ip4/packet_icmp4.hpp | 36 -------------------------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/api/net/inet_common.hpp b/api/net/inet_common.hpp index 9e62dd0c0a..5510a53d0e 100644 --- a/api/net/inet_common.hpp +++ b/api/net/inet_common.hpp @@ -26,6 +26,46 @@ #include namespace net { + + /** ICMP4 Codes so that UDP and IP4 can use these */ + namespace icmp4 { + namespace code { + enum class Dest_unreachable : uint8_t { + NET, + HOST, + PROTOCOL, + PORT, + FRAGMENTATION, + SRC_ROUTE, + NET_UNKNOWN, // RFC 1122 + HOST_UNKNOWN, + SRC_HOST_ISOLATED, + NET_PROHIBITED, + HOST_PROHIBITED, + NET_FOR_TOS, + HOST_FOR_TOS + }; + + enum class Redirect : uint8_t { + NET, + HOST, + TOS_NET, + TOS_HOST + }; + + enum class Time_exceeded : uint8_t { + TTL, + FRAGMENT_REASSEMBLY + }; + + enum class Parameter_problem : uint8_t { + POINTER_INDICATES_ERROR, + REQUIRED_OPT_MISSING // RFC 1122 + }; + + } // < namespace code + } // < namespace icmp4 + // Packet must be forward declared to avoid circular dependency // i.e. IP uses Packet, and Packet uses IP headers class Packet; diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index f93c9f3d5d..803d870534 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -34,42 +34,6 @@ namespace icmp4 { TIMESTAMP_REPLY = 14 }; - namespace code { - enum class Dest_unreachable : uint8_t { - NET, - HOST, - PROTOCOL, - PORT, - FRAGMENTATION, - SRC_ROUTE, - NET_UNKNOWN, // RFC 1122 - HOST_UNKNOWN, - SRC_HOST_ISOLATED, - NET_PROHIBITED, - HOST_PROHIBITED, - NET_FOR_TOS, - HOST_FOR_TOS - }; - - enum class Redirect : uint8_t { - NET, - HOST, - TOS_NET, - TOS_HOST - }; - - enum class Time_exceeded : uint8_t { - TTL, - FRAGMENT_REASSEMBLY - }; - - enum class Parameter_problem : uint8_t { - POINTER_INDICATES_ERROR, - REQUIRED_OPT_MISSING // RFC 1122 - }; - - } // < namespace code - class Packet { struct Header { From bc9c5a1ef225cc588aac4443164f7c0acac2730d Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 14:59:28 +0100 Subject: [PATCH 09/40] IP4 Packet: Getter for TTL added --- api/net/ip4/packet_ip4.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/net/ip4/packet_ip4.hpp b/api/net/ip4/packet_ip4.hpp index a100d2db87..757fe9877f 100644 --- a/api/net/ip4/packet_ip4.hpp +++ b/api/net/ip4/packet_ip4.hpp @@ -48,6 +48,9 @@ namespace net { Protocol protocol() const noexcept { return static_cast(ip_header().protocol); } + uint8_t ttl() const noexcept + { return ip_header().ttl; } + uint16_t ip_segment_length() const noexcept { return ntohs(ip_header().tot_len); } From 235ae06f1d04e3de8507b516e05c0deef7595125 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 15:02:28 +0100 Subject: [PATCH 10/40] ICMP4: Support for sending ping, destination unreachable and other ICMP messages --- api/net/ip4/icmp4.hpp | 11 +++++++---- src/net/ip4/icmp4.cpp | 45 +++++++++++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index d9f704e85c..75b87be14a 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -36,7 +36,7 @@ namespace net { inline void set_network_out(downstream s) { network_layer_out_ = s; }; - void destination_unreachable(icmp4::Packet& req, icmp4::code::Dest_unreachable code); + void destination_unreachable(Packet_ptr pckt, icmp4::code::Dest_unreachable code); void redirect(icmp4::Packet& req, icmp4::code::Redirect code); @@ -44,10 +44,11 @@ namespace net { void parameter_problem(icmp4::Packet& req); + // May void timestamp_request(IP4::addr ip); void timestamp_reply(icmp4::Packet& req); - void ping_request(IP4::addr ip); + void ping(IP4::addr ip); private: @@ -60,11 +61,13 @@ namespace net { 'Q','R','S','T','U','V','W','X', 'Y','Z',1,2,3,4,5,6, 7,8}; + static int id_; void ping_reply(icmp4::Packet&); - void send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); - void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code); + void send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, + uint16_t sequence = 0); + void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); }; //< class ICMPv4 } //< namespace net diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index fff09be35d..6f4f4ecf06 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -20,6 +20,8 @@ namespace net { + int ICMPv4::id_ = 0; + ICMPv4::ICMPv4(Stack& inet) : inet_{inet} {} @@ -44,7 +46,7 @@ namespace net { case (icmp4::Type::REDIRECT): debug(" REDIRECT from %s\n", req.ip().src().str().c_str()); // TODO - // Only sent by gateways. Update routing information based on the message + // Only sent by gateways. Incoming: Update routing information based on the message break; case (icmp4::Type::ECHO): debug(" PING from %s\n", req.ip().src().str().c_str()); @@ -62,30 +64,38 @@ namespace net { break; case (icmp4::Type::TIMESTAMP): debug(" TIMESTAMP from %s\n", req.ip().src().str().c_str()); - // TODO - timestamp_reply(req); + // TODO May + // timestamp_reply(req); break; case (icmp4::Type::TIMESTAMP_REPLY): debug(" TIMESTAMP REPLY from %s\n", req.ip().src().str().c_str()); - // TODO + // TODO May break; } } - void ICMPv4::destination_unreachable(icmp4::Packet& req, icmp4::code::Dest_unreachable code) { - send_response(req, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code); + /** UDP (Port Unreachable) and IP4 (Protocol Unreachable) sending Destination Unreachable message */ + void ICMPv4::destination_unreachable(Packet_ptr pckt, icmp4::code::Dest_unreachable code) { + if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header + return; + + auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); + auto pckt_icmp4 = icmp4::Packet(std::move(pckt_ip4)); + + // TODO + send_response(pckt_icmp4, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code, pckt_icmp4.payload()); } void ICMPv4::redirect(icmp4::Packet& req, icmp4::code::Redirect code) { - send_response(req, icmp4::Type::REDIRECT, (uint8_t) code); + //send_response(req, icmp4::Type::REDIRECT, (uint8_t) code, icmp4::Packet::Span(, )); } void ICMPv4::time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code) { - send_response(req, icmp4::Type::TIME_EXCEEDED, (uint8_t) code); + //send_response(req, icmp4::Type::TIME_EXCEEDED, (uint8_t) code, icmp4::Packet::Span(, )); } void ICMPv4::parameter_problem(icmp4::Packet& req) { - send_response(req, icmp4::Type::PARAMETER_PROBLEM, 0); + //send_response(req, icmp4::Type::PARAMETER_PROBLEM, 0, icmp4::Packet::Span(, )); } void ICMPv4::timestamp_request(IP4::addr ip) { @@ -95,18 +105,19 @@ namespace net { void ICMPv4::timestamp_reply(icmp4::Packet& req) { // TODO - // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0); + // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0, icmp4::Packet::Span(, )); } - void ICMPv4::ping_request(IP4::addr ip) { + void ICMPv4::ping(IP4::addr ip) { send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48)); } void ICMPv4::ping_reply(icmp4::Packet& req) { - send_response(req, icmp4::Type::ECHO_REPLY, 0); + send_response(req, icmp4::Type::ECHO_REPLY, 0, req.payload()); } - void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload) { + void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, + uint16_t sequence) { // Provision new IP4-packet icmp4::Packet req(inet_.ip_packet_factory()); @@ -117,8 +128,8 @@ namespace net { // Populate request ICMP header req.set_type(type); req.set_code(code); - req.set_id(0); - req.set_sequence(0); + req.set_id(id_++); + req.set_sequence(sequence); debug(" Transmitting request to %s\n", dest_ip.to_string().c_str()); @@ -134,7 +145,7 @@ namespace net { network_layer_out_(req.release()); } - void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code) { + void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload) { // Provision new IP4-packet icmp4::Packet res(inet_.ip_packet_factory()); @@ -151,7 +162,7 @@ namespace net { debug(" Transmitting answer to %s\n", res.ip().dst().str().c_str()); // Payload - res.set_payload(req.payload()); + res.set_payload(payload); // Add checksum res.set_checksum(); From 6f08c0da881b633a32ab14507b7b8c806492293d Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 15:04:00 +0100 Subject: [PATCH 11/40] IP4 and UDP: Sending protocol unreachable and port unreachable (ICMP messages) --- src/net/ip4/ip4.cpp | 7 +++++-- src/net/ip4/udp.cpp | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/net/ip4/ip4.cpp b/src/net/ip4/ip4.cpp index d1120ceeca..dd575a3e26 100644 --- a/src/net/ip4/ip4.cpp +++ b/src/net/ip4/ip4.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace net { @@ -44,8 +45,8 @@ namespace net { packets_rx_++; debug2("\t Source IP: %s Dest.IP: %s Type: 0x%x\n", - packet->src().str().c_str(), - packet->dst().str().c_str(), + packet->src().str().c_str(), + packet->dst().str().c_str(), packet->protocol()); // Drop if my ip address doesn't match destination ip address or broadcast @@ -79,6 +80,8 @@ namespace net { break; default: debug("\t Type: UNKNOWN %i\n", hdr->protocol); + // Sending ICMP message of type Destination Unreachable and code PROTOCOL + stack_.icmp().destination_unreachable(std::move(packet), icmp4::code::Dest_unreachable::PROTOCOL); break; } } diff --git a/src/net/ip4/udp.cpp b/src/net/ip4/udp.cpp index 4250ad8610..8b756ba5d4 100644 --- a/src/net/ip4/udp.cpp +++ b/src/net/ip4/udp.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace net { @@ -34,22 +35,26 @@ namespace net { void UDP::receive(net::Packet_ptr pckt) { - auto udp = static_unique_ptr_cast(std::move(pckt)); + auto udp_packet = static_unique_ptr_cast(std::move(pckt)); debug("<%s> UDP", stack_.ifname().c_str()); debug("\t Source port: %u, Dest. Port: %u Length: %u\n", - udp->src_port(), udp->dst_port(), udp->length()); + udp_packet->src_port(), udp_packet->dst_port(), udp_packet->length()); - auto it = ports_.find(udp->dst_port()); + auto it = ports_.find(udp_packet->dst_port()); if (LIKELY(it != ports_.end())) { - debug("<%s> UDP found listener on port %u\n", - stack_.ifname().c_str(), udp->dst_port()); - it->second.internal_read(std::move(udp)); + debug("<%s> UDP found listener on port %u\n", + stack_.ifname().c_str(), udp_packet->dst_port()); + it->second.internal_read(std::move(udp_packet)); return; } - debug("<%s> UDP: nobody listening on %u. Drop!\n", - stack_.ifname().c_str(), udp->dst_port()); + debug("<%s> UDP: nobody listening on %u. Drop!\n", + stack_.ifname().c_str(), udp_packet->dst_port()); + + // Sending ICMP message of type Destination Unreachable and code PORT + auto ip4_packet = static_unique_ptr_cast(std::move(udp_packet)); + stack_.icmp().destination_unreachable(std::move(ip4_packet), icmp4::code::Dest_unreachable::PORT); } UDPSocket& UDP::bind(UDP::port_t port) @@ -168,7 +173,7 @@ namespace net { debug("<%s> UDP: %i bytes to write, need %i packets \n", udp.stack().ifname().c_str(), - remaining(), + remaining(), remaining() / udp.max_datagram_size() + (remaining() % udp.max_datagram_size() ? 1 : 0)); while (remaining()) { From 001563a6dd22df91812ced9e67fc56963842a342 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 15:07:10 +0100 Subject: [PATCH 12/40] ICMP integration test: Files added --- test/net/integration/icmp/CMakeLists.txt | 33 ++++++++++++++++++++++++ test/net/integration/icmp/README.md | 1 + test/net/integration/icmp/service.cpp | 33 ++++++++++++++++++++++++ test/net/integration/icmp/test.py | 21 +++++++++++++++ test/net/integration/icmp/vm.json | 5 ++++ 5 files changed, 93 insertions(+) create mode 100644 test/net/integration/icmp/CMakeLists.txt create mode 100644 test/net/integration/icmp/README.md create mode 100644 test/net/integration/icmp/service.cpp create mode 100755 test/net/integration/icmp/test.py create mode 100644 test/net/integration/icmp/vm.json diff --git a/test/net/integration/icmp/CMakeLists.txt b/test/net/integration/icmp/CMakeLists.txt new file mode 100644 index 0000000000..7f9f72581e --- /dev/null +++ b/test/net/integration/icmp/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 2.8.9) + +# IncludeOS install location +if (NOT DEFINED ENV{INCLUDEOS_PREFIX}) + set(ENV{INCLUDEOS_PREFIX} /usr/local) +endif() + +set(CMAKE_TOOLCHAIN_FILE $ENV{INCLUDEOS_PREFIX}/includeos/i686-elf-toolchain.cmake) + +project(test_icmp) + +# Human-readable name of your service +set(SERVICE_NAME "IncludeOS ICMP test") + +# Name of your service binary +set(BINARY "test_icmp") + +# Maximum memory can be hard-coded into the binary +set(MAX_MEM 128) + +# Source files to be linked with OS library parts to form bootable image +set(SOURCES + service.cpp + ) + +# DRIVERS / PLUGINS: + +set(DRIVERS + virtionet + ) + +# include service build script +include($ENV{INCLUDEOS_PREFIX}/includeos/service.cmake) diff --git a/test/net/integration/icmp/README.md b/test/net/integration/icmp/README.md new file mode 100644 index 0000000000..4ec60bc4d0 --- /dev/null +++ b/test/net/integration/icmp/README.md @@ -0,0 +1 @@ +# ICMP integration test diff --git a/test/net/integration/icmp/service.cpp b/test/net/integration/icmp/service.cpp new file mode 100644 index 0000000000..ff0fd4561f --- /dev/null +++ b/test/net/integration/icmp/service.cpp @@ -0,0 +1,33 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2015 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +using namespace net; + +void Service::start(const std::string&) +{ + auto& inet = Inet4::stack<0>(); + inet.network_config({ 10, 0, 0, 45 }, // IP + { 255, 255, 0, 0 }, // Netmask + { 10, 0, 0, 1 } ); // Gateway + printf("Service IP address is %s\n", inet.ip_addr().str().c_str()); + + inet.icmp().ping(IP4::addr{193,90,147,109}); // google.com + // inet.icmp().ping(IP4::addr{10,0,0,42}); // acorn +} diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py new file mode 100755 index 0000000000..39c8cd4450 --- /dev/null +++ b/test/net/integration/icmp/test.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python + +import sys +import os +import time +import subprocess + +includeos_src = os.environ.get('INCLUDEOS_SRC', + os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))).split('/test')[0]) +sys.path.insert(0,includeos_src) + +from vmrunner import vmrunner +import socket + +from vmrunner.prettify import color + +# Get an auto-created VM from the vmrunner +vm = vmrunner.vms[0] + +# Boot the VM, taking a timeout as parameter +vm.cmake().boot(20).clean() diff --git a/test/net/integration/icmp/vm.json b/test/net/integration/icmp/vm.json new file mode 100644 index 0000000000..134f351755 --- /dev/null +++ b/test/net/integration/icmp/vm.json @@ -0,0 +1,5 @@ +{ + "image" : "test_icmp.img", + "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], + "mem" : 128 +} From 97b81b37852b90241d19188f0f3c394a99ccda3a Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 15:34:00 +0100 Subject: [PATCH 13/40] ICMP integration test: Added vm.exit to test.py --- test/net/integration/icmp/test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index 39c8cd4450..484c6d49c0 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -17,5 +17,10 @@ # Get an auto-created VM from the vmrunner vm = vmrunner.vms[0] +def init(trigger_line): + vm.exit(0, " ICMP test succeeded. Process returned 0 exit status") + +vm.on_output("Service", init); + # Boot the VM, taking a timeout as parameter vm.cmake().boot(20).clean() From bfc797a18daa5b217c8604ded46c5b8fbdcc8e8a Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 8 Mar 2017 15:50:38 +0100 Subject: [PATCH 14/40] ICMP4 codes separated into its own file and included into inet_common.hpp --- api/net/inet_common.hpp | 41 +----------------------- api/net/ip4/icmp4_codes.hpp | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 api/net/ip4/icmp4_codes.hpp diff --git a/api/net/inet_common.hpp b/api/net/inet_common.hpp index 5510a53d0e..043e115f6d 100644 --- a/api/net/inet_common.hpp +++ b/api/net/inet_common.hpp @@ -24,48 +24,9 @@ #include #include #include +#include namespace net { - - /** ICMP4 Codes so that UDP and IP4 can use these */ - namespace icmp4 { - namespace code { - enum class Dest_unreachable : uint8_t { - NET, - HOST, - PROTOCOL, - PORT, - FRAGMENTATION, - SRC_ROUTE, - NET_UNKNOWN, // RFC 1122 - HOST_UNKNOWN, - SRC_HOST_ISOLATED, - NET_PROHIBITED, - HOST_PROHIBITED, - NET_FOR_TOS, - HOST_FOR_TOS - }; - - enum class Redirect : uint8_t { - NET, - HOST, - TOS_NET, - TOS_HOST - }; - - enum class Time_exceeded : uint8_t { - TTL, - FRAGMENT_REASSEMBLY - }; - - enum class Parameter_problem : uint8_t { - POINTER_INDICATES_ERROR, - REQUIRED_OPT_MISSING // RFC 1122 - }; - - } // < namespace code - } // < namespace icmp4 - // Packet must be forward declared to avoid circular dependency // i.e. IP uses Packet, and Packet uses IP headers class Packet; diff --git a/api/net/ip4/icmp4_codes.hpp b/api/net/ip4/icmp4_codes.hpp new file mode 100644 index 0000000000..b20fc468af --- /dev/null +++ b/api/net/ip4/icmp4_codes.hpp @@ -0,0 +1,64 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2017 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NET_IP4_ICMP4_CODES_HPP +#define NET_IP4_ICMP4_CODES_HPP + +namespace net { + + /** ICMP4 Codes so that UDP and IP4 can use these */ + namespace icmp4 { + namespace code { + enum class Dest_unreachable : uint8_t { + NET, + HOST, + PROTOCOL, + PORT, + FRAGMENTATION, + SRC_ROUTE, + NET_UNKNOWN, // RFC 1122 + HOST_UNKNOWN, + SRC_HOST_ISOLATED, + NET_PROHIBITED, + HOST_PROHIBITED, + NET_FOR_TOS, + HOST_FOR_TOS + }; + + enum class Redirect : uint8_t { + NET, + HOST, + TOS_NET, + TOS_HOST + }; + + enum class Time_exceeded : uint8_t { + TTL, + FRAGMENT_REASSEMBLY + }; + + enum class Parameter_problem : uint8_t { + POINTER_INDICATES_ERROR, + REQUIRED_OPT_MISSING // RFC 1122 + }; + + } // < namespace code + } // < namespace icmp4 + +} // < namespace net + +#endif From 9b6542da0da290128303c9a5fe7cc5e859ab8a8b Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 09:43:20 +0100 Subject: [PATCH 15/40] ICMP4: Added method for getting destination unreachable payload in icmp4::Packet --- api/net/dhcp/dhcpd.hpp | 2 +- api/net/ip4/packet_icmp4.hpp | 10 +++++++--- api/net/packet.hpp | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/api/net/dhcp/dhcpd.hpp b/api/net/dhcp/dhcpd.hpp index c6ca5b3875..1db8b401c3 100644 --- a/api/net/dhcp/dhcpd.hpp +++ b/api/net/dhcp/dhcpd.hpp @@ -156,7 +156,7 @@ namespace dhcp { void clear_offered_ip(IP4::addr ip); void clear_offered_ips(); - void print(const dhcp_packet_t* msg, const dhcp_option_t* opts) { + void print(const dhcp_packet_t* /* msg */, const dhcp_option_t* opts) { debug("Printing:\n"); debug("OP: %u\n", msg->op); diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 803d870534..882bbd9df8 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -74,9 +74,13 @@ namespace icmp4 { { return header().sequence; } Span payload() - { - return {&(header().payload[0]), pckt_->data_end() - &(header().payload[0]) }; - } + { return {&(header().payload[0]), pckt_->data_end() - &(header().payload[0]) }; } + + /** Several ICMP messages require the payload to be the header and 64 bits of the + * data of the original datagram + */ + Span header_and_data() + { return {pckt_->layer_begin(), pckt_->ip_header_length() + 8}; } void set_type(Type t) noexcept { header().type = t; } diff --git a/api/net/packet.hpp b/api/net/packet.hpp index 3d13cabbd3..9a68ce45df 100644 --- a/api/net/packet.hpp +++ b/api/net/packet.hpp @@ -75,7 +75,9 @@ namespace net const Byte* buf() const noexcept { return &buf_[0]; } - /** Get the start of the layer currently being accessed */ + /** Get the start of the layer currently being accessed + * Returns a pointer to the start of the header + */ Byte_ptr layer_begin() const noexcept { return layer_begin_; } From be46d50ad3401abb215d2986305c5758193f1c9a Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 09:46:10 +0100 Subject: [PATCH 16/40] ICMP4: Ping with callback, send response with and without id and sequence --- api/net/ip4/icmp4.hpp | 114 +++++++++++++++++++++++++++++++++++++++--- src/net/ip4/icmp4.cpp | 80 ++++++++++++++++++++++------- 2 files changed, 171 insertions(+), 23 deletions(-) diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index 75b87be14a..1d2b2d0eba 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -19,12 +19,18 @@ #define NET_IP4_ICMPv4_HPP #include "packet_icmp4.hpp" +#include +#include namespace net { + struct ICMP_packet; + struct ICMPv4 { using Stack = IP4::Stack; + using Tuple = std::pair; // identifier and sequence number + using icmp_func = delegate; // Initialize ICMPv4(Stack&); @@ -36,12 +42,11 @@ namespace net { inline void set_network_out(downstream s) { network_layer_out_ = s; }; + /** Destination Unreachable sent from host because of port (UDP) or protocol (IP4) unreachable */ void destination_unreachable(Packet_ptr pckt, icmp4::code::Dest_unreachable code); void redirect(icmp4::Packet& req, icmp4::code::Redirect code); - void time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code); - void parameter_problem(icmp4::Packet& req); // May @@ -49,9 +54,10 @@ namespace net { void timestamp_reply(icmp4::Packet& req); void ping(IP4::addr ip); + void ping(IP4::addr ip, icmp_func callback); private: - + static int request_id_; // message identifier for messages originating from IncludeOS Stack& inet_; downstream network_layer_out_ = nullptr; uint8_t includeos_payload_[48] = {'I','N','C','L','U','D', @@ -61,15 +67,111 @@ namespace net { 'Q','R','S','T','U','V','W','X', 'Y','Z',1,2,3,4,5,6, 7,8}; - static int id_; - + struct ICMP_callback { + using icmp_func = ICMPv4::icmp_func; + using Tuple = ICMPv4::Tuple; + + Tuple tuple; + icmp_func callback; + Timers::id_t timer_id; + + ICMP_callback() + { + timer_id = Timers::oneshot(std::chrono::microseconds(1000000), [&](Timers::id_t) { + // TODO + // Should not be called if this has already been erased (found reply): + // callback(ICMP_packet{0, 0, IP4::addr{0,0,0,0}, IP4::addr{0,0,0,0}, icmp4::Type::ECHO_REPLY, + // 0, 0, ICMP_packet::Span(nullptr, 0)}); + }); + } + }; // < struct ICMP_callback + + std::map ping_callbacks_; + + /** + * Responding to a ping (echo) request + * Called from receive-method + */ void ping_reply(icmp4::Packet&); void send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, - uint16_t sequence = 0); + icmp_func callback = nullptr, uint16_t sequence = 0); + + /** Send response without id and sequence number */ void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); + /** Send response with id and sequence number */ + void send_response_with_id(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); }; //< class ICMPv4 + + /** User friendly ICMP packet used in callback (icmp_func) */ + struct ICMP_packet { + using Span = gsl::span; + + uint16_t id_; + uint16_t seq_; + IP4::addr src_; + IP4::addr dst_; + icmp4::Type type_; + uint8_t code_; + uint16_t checksum_; + Span payload_; + + public: + ICMP_packet(uint16_t id, uint16_t seq, IP4::addr src, IP4::addr dst, icmp4::Type type, uint8_t code, uint16_t checksum, const Span& payload) + : id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, checksum_{checksum}, payload_{payload} + {} + + uint16_t id() const noexcept + { return id_; } + + uint16_t seq() const noexcept + { return seq_; } + + IP4::addr src() const noexcept + { return src_; } + + IP4::addr dst() const noexcept + { return dst_; } + + icmp4::Type type() const noexcept + { return type_; } + + uint8_t code() const noexcept + { return code_; } + + uint16_t checksum() const noexcept + { return checksum_; } + + Span payload() const noexcept + { return payload_; } + + std::string to_string() { + const std::string t = [&]() { + switch (type_) { + using namespace icmp4; + case Type::ECHO_REPLY: return "ECHO REPLY"; + case Type::DEST_UNREACHABLE: return "DESTINATION UNREACHABLE"; + case Type::REDIRECT: return "REDIRECT"; + case Type::ECHO: return "ECHO"; + case Type::TIME_EXCEEDED: return "TIME EXCEEDED"; + case Type::PARAMETER_PROBLEM: return "PARAMETER PROBLEM"; + case Type::TIMESTAMP: return "TIMESTAMP"; + case Type::TIMESTAMP_REPLY: return "TIMESTAMP REPLY"; + } + }(); + + return "Identifier: " + std::to_string(id_) + "\n" + + "Sequence number: " + std::to_string(seq_) + "\n" + + "Source: " + src_.to_string() + "\n" + + "Destination: " + dst_.to_string() + "\n" + + "Type: " + t + "\n" + + "Code: " + std::to_string(code_) + "\n" + + "Checksum: " + std::to_string(checksum_) + "\n" + + "Data: " + std::string{payload_.begin(), payload_.end()}; + } + }; // < class ICMP_packet + } //< namespace net #endif //< NET_IP4_ICMPv4_HPP diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index 6f4f4ecf06..ce8c0374b9 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -20,7 +20,7 @@ namespace net { - int ICMPv4::id_ = 0; + int ICMPv4::request_id_ = 0; ICMPv4::ICMPv4(Stack& inet) : inet_{inet} @@ -32,11 +32,21 @@ namespace net { auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); auto req = icmp4::Packet(std::move(pckt_ip4)); + std::map::iterator it; switch(req.type()) { case (icmp4::Type::ECHO_REPLY): debug(" PING Reply from %s\n", req.ip().src().str().c_str()); - // TODO + // Find callback matching the reply + it = ping_callbacks_.find(std::make_pair(req.id(), req.sequence())); + if (it != ping_callbacks_.end()) { + it->second.callback(ICMP_packet{req.id(), req.sequence(), req.ip().src(), req.ip().dst(), req.type(), req.code(), + req.checksum(), req.payload()}); + + // Timers::stop(it->second.timer_id); + + ping_callbacks_.erase(it); + } break; case (icmp4::Type::DEST_UNREACHABLE): debug(" DESTINATION UNREACHABLE from %s\n", req.ip().src().str().c_str()); @@ -74,36 +84,32 @@ namespace net { } } - /** UDP (Port Unreachable) and IP4 (Protocol Unreachable) sending Destination Unreachable message */ void ICMPv4::destination_unreachable(Packet_ptr pckt, icmp4::code::Dest_unreachable code) { if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header return; - auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); auto pckt_icmp4 = icmp4::Packet(std::move(pckt_ip4)); - - // TODO - send_response(pckt_icmp4, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code, pckt_icmp4.payload()); + send_response(pckt_icmp4, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code, pckt_icmp4.header_and_data()); } - void ICMPv4::redirect(icmp4::Packet& req, icmp4::code::Redirect code) { - //send_response(req, icmp4::Type::REDIRECT, (uint8_t) code, icmp4::Packet::Span(, )); + void ICMPv4::redirect(icmp4::Packet& /* req */, icmp4::code::Redirect /* code */) { + // send_response(req, icmp4::Type::REDIRECT, (uint8_t) code, icmp4::Packet::Span(, )); } - void ICMPv4::time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code) { - //send_response(req, icmp4::Type::TIME_EXCEEDED, (uint8_t) code, icmp4::Packet::Span(, )); + void ICMPv4::time_exceeded(icmp4::Packet& /* req */, icmp4::code::Time_exceeded /* code */) { + // send_response(req, icmp4::Type::TIME_EXCEEDED, (uint8_t) code, icmp4::Packet::Span(, )); } - void ICMPv4::parameter_problem(icmp4::Packet& req) { - //send_response(req, icmp4::Type::PARAMETER_PROBLEM, 0, icmp4::Packet::Span(, )); + void ICMPv4::parameter_problem(icmp4::Packet& /* req */) { + // send_response(req, icmp4::Type::PARAMETER_PROBLEM, 0, icmp4::Packet::Span(, )); } - void ICMPv4::timestamp_request(IP4::addr ip) { + void ICMPv4::timestamp_request(IP4::addr /* ip */) { // TODO // send_request(ip, icmp4::Type::TIMESTAMP, 0, icmp4::Packet::Span(, )); } - void ICMPv4::timestamp_reply(icmp4::Packet& req) { + void ICMPv4::timestamp_reply(icmp4::Packet& /* req */) { // TODO // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0, icmp4::Packet::Span(, )); } @@ -111,13 +117,17 @@ namespace net { void ICMPv4::ping(IP4::addr ip) { send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48)); } + void ICMPv4::ping(IP4::addr ip, icmp_func callback) { + send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48), callback); + } void ICMPv4::ping_reply(icmp4::Packet& req) { send_response(req, icmp4::Type::ECHO_REPLY, 0, req.payload()); } void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, - uint16_t sequence) { + icmp_func callback, uint16_t sequence) { + // Provision new IP4-packet icmp4::Packet req(inet_.ip_packet_factory()); @@ -125,12 +135,21 @@ namespace net { req.ip().set_src(inet_.ip_addr()); req.ip().set_dst(dest_ip); + uint16_t temp_id = request_id_; // Populate request ICMP header req.set_type(type); req.set_code(code); - req.set_id(id_++); + req.set_id(request_id_++); req.set_sequence(sequence); + if (callback) { + Tuple t = std::make_pair(temp_id, sequence); // Key into ping_callbacks_ map + ICMP_callback c; + c.tuple = t; + c.callback = callback; + ping_callbacks_.emplace(std::make_pair(t, c)); + } + debug(" Transmitting request to %s\n", dest_ip.to_string().c_str()); // Payload @@ -156,6 +175,33 @@ namespace net { // Populate response ICMP header res.set_type(type); res.set_code(code); + + debug(" Transmitting answer to %s\n", res.ip().dst().str().c_str()); + + // Payload + res.set_payload(payload); + + // Add checksum + res.set_checksum(); + + debug(" Response size: %i payload size: %i, checksum: 0x%x\n", + res.ip().size(), res.payload().size(), res.compute_checksum()); + + network_layer_out_(res.release()); + } + + void ICMPv4::send_response_with_id(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload) { + // Provision new IP4-packet + icmp4::Packet res(inet_.ip_packet_factory()); + + // Populate response IP header + res.ip().set_src(inet_.ip_addr()); + res.ip().set_dst(req.ip().src()); + + // Populate response ICMP header + res.set_type(type); + res.set_code(code); + // Incl. id and sequence number res.set_id(req.id()); res.set_sequence(req.sequence()); From f7c75e618b02d28cfd17d874ba96e4f170c5f3d9 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 09:48:55 +0100 Subject: [PATCH 17/40] ICMP integration test: ping with callback to validate sent and received message, port and protocol unreachable validated in test.py --- test/net/integration/icmp/service.cpp | 23 ++++++-- test/net/integration/icmp/test.py | 76 ++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/test/net/integration/icmp/service.cpp b/test/net/integration/icmp/service.cpp index ff0fd4561f..0fbb5bce74 100644 --- a/test/net/integration/icmp/service.cpp +++ b/test/net/integration/icmp/service.cpp @@ -23,11 +23,24 @@ using namespace net; void Service::start(const std::string&) { auto& inet = Inet4::stack<0>(); - inet.network_config({ 10, 0, 0, 45 }, // IP - { 255, 255, 0, 0 }, // Netmask - { 10, 0, 0, 1 } ); // Gateway + inet.network_config({ 10, 0, 0, 45 }, // IP + { 255, 255, 0, 0 }, // Netmask + { 10, 0, 0, 1 } ); // Gateway printf("Service IP address is %s\n", inet.ip_addr().str().c_str()); - inet.icmp().ping(IP4::addr{193,90,147,109}); // google.com - // inet.icmp().ping(IP4::addr{10,0,0,42}); // acorn + // ping google.com with callback + inet.icmp().ping(IP4::addr{193,90,147,109}, [](ICMP_packet pckt) { + printf("Received packet\n%s\n", pckt.to_string().c_str()); + }); + + // Also possible + // inet.icmp().ping(IP4::addr{193,90,147,109}); // google.com + + /* No reply-ping + inet.icmp().ping(IP4::addr{23,143,23,33}, [](ICMP_packet pckt) { + printf("Received packet\n%s\n", pckt.to_string().c_str()); + }); + inet.icmp().ping(IP4::addr{23,143,23,33}, [](ICMP_packet pckt) { + printf("Received packet\n%s\n", pckt.to_string().c_str()); + });*/ } diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index 484c6d49c0..ca00266d30 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -2,7 +2,6 @@ import sys import os -import time import subprocess includeos_src = os.environ.get('INCLUDEOS_SRC', @@ -10,17 +9,82 @@ sys.path.insert(0,includeos_src) from vmrunner import vmrunner -import socket - from vmrunner.prettify import color +import socket + # Get an auto-created VM from the vmrunner vm = vmrunner.vms[0] -def init(trigger_line): - vm.exit(0, " ICMP test succeeded. Process returned 0 exit status") +# 1. Check that a ping request is received from google.com (193.90.147.109) +# 2. Check that sending a udp packet to 10.0.0.45 (the IncludeOS service's IP) and port 8080 returns +# an ICMP message with Destination unreachable (type 3), port unreachable (code 3). +# sudo hping 10.0.0.45 --udp -p 8080 -c 1 +# (count = 1) +# 3. Check that sending an ip packet to 10.0.0.45 (the IncludeOS service's IP) with protocol 16 f.ex. +# returns an ICMP message with Destination unreachable (type 3), protocol unreachable (code 2). +# sudo hping 10.0.0.45 -d 20 -0 --ipproto 16 -c 1 +# (count = 1) + +num_successes = 0 + +def start_icmp_test(trigger_line): + global num_successes + + # 1 Ping: Checking output from callback in service.cpp + print color.INFO(""), "Performing ping test" + + output_data = "" + for x in range(0, 9): + output_data += vm.readline() + + if "Received packet" in output_data and \ + "Identifier: 0" in output_data and \ + "Sequence number: 0" in output_data and \ + "Source: 193.90.147.109" in output_data and \ + "Destination: 10.0.0.45" in output_data and \ + "Type: ECHO REPLY" in output_data and \ + "Code: 0" in output_data: + num_successes += 1 + print color.INFO(""), "Ping test succeeded" + else: + print color.FAIL(""), "Ping test FAILED" + + # 2 Port unreachable + print color.INFO(""), "Performing Destination Unreachable (port) test" + # Sending 1 udp packet to 10.0.0.45 to port 8080 + udp_port_output = subprocess.check_output(["sudo", "hping", "10.0.0.45", "--udp", "-p", "8080", "-c", "1"]) + print udp_port_output + + # Validate content in udp_port_output: + if "ICMP Port Unreachable from ip=10.0.0.45" in udp_port_output: + print color.INFO(""), "Port Unreachable test succeeded" + num_successes += 1 + else: + print color.FAIL(""), "Port Unreachable test FAILED" + + # 3 Protocol unreachable + print color.INFO(""), "Performing Destination Unreachable (protocol) test" + # Sending 1 raw ip packet to 10.0.0.45 with protocol 16 + rawip_protocol_output = subprocess.check_output(["sudo", "hping", "10.0.0.45", "-d", "20", "-0", "--ipproto", "16", "-c", "1"]) + print rawip_protocol_output + + # Validate content in rawip_protocol_output: + if "ICMP Protocol Unreachable from ip=10.0.0.45" in rawip_protocol_output: + print color.INFO(""), "Protocol Unreachable test succeeded" + num_successes += 1 + else: + print color.FAIL(""), "Protocol Unreachable test FAILED" + + # 4 Check result of tests + if num_successes == 3: + vm.exit(0, " All ICMP tests succeeded. Process returned 0 exit status") + else: + num_fails = 3 - num_successes + res = " " + str(num_fails) + " ICMP test(s) failed" + vm.exit(1, res) -vm.on_output("Service", init); +vm.on_output("Service IP address is 10.0.0.45", start_icmp_test); # Boot the VM, taking a timeout as parameter vm.cmake().boot(20).clean() From cf29a150f4ed1ec0b352e043b2b8da807d121f71 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 10:05:33 +0100 Subject: [PATCH 18/40] ICMP4 ping reply: Calling new method send_response_with_id when responding to incoming ping requests --- src/net/ip4/icmp4.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index ce8c0374b9..f1da3995b7 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -59,7 +59,7 @@ namespace net { // Only sent by gateways. Incoming: Update routing information based on the message break; case (icmp4::Type::ECHO): - debug(" PING from %s\n", req.ip().src().str().c_str()); + printf(" PING from %s\n", req.ip().src().str().c_str()); ping_reply(req); break; case (icmp4::Type::TIME_EXCEEDED): @@ -122,7 +122,7 @@ namespace net { } void ICMPv4::ping_reply(icmp4::Packet& req) { - send_response(req, icmp4::Type::ECHO_REPLY, 0, req.payload()); + send_response_with_id(req, icmp4::Type::ECHO_REPLY, 0, req.payload()); } void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, From 3a77b8420f643bbd229762977612905844e42b73 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 10:28:24 +0100 Subject: [PATCH 19/40] ICMP integration test: Printing out result from ping callback --- test/net/integration/icmp/test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index ca00266d30..d1f1dc2e70 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -38,6 +38,8 @@ def start_icmp_test(trigger_line): for x in range(0, 9): output_data += vm.readline() + print output_data + if "Received packet" in output_data and \ "Identifier: 0" in output_data and \ "Sequence number: 0" in output_data and \ From bfb84da012b154ea4963814df917f7d6e68bee1b Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 10:50:46 +0100 Subject: [PATCH 20/40] Statman: end-iterator back to returning end of span, not last stat in span. Get(name) method using last_used-iterator instead of end --- api/util/statman.hpp | 2 +- src/util/statman.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/util/statman.hpp b/api/util/statman.hpp index adff491686..99ad974ff7 100644 --- a/api/util/statman.hpp +++ b/api/util/statman.hpp @@ -213,7 +213,7 @@ class Statman { /// /// auto end() noexcept - { return Span_iterator(&stats_, next_available_); } + { return stats_.end(); } /// /// diff --git a/src/util/statman.cpp b/src/util/statman.cpp index ebd4fdb97b..0c756eb361 100644 --- a/src/util/statman.cpp +++ b/src/util/statman.cpp @@ -111,7 +111,7 @@ Stat& Statman::get(const std::string& name) { if (name.empty()) throw Stats_exception{"Stat name is empty"}; - for (auto it = begin(); it != end(); ++it) + for (auto it = begin(); it != last_used(); ++it) if ((*it).name() == name) return (*it); From b4b4301aaf33891d923a28ab35880f2e7f5a2c79 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 11:13:58 +0100 Subject: [PATCH 21/40] ICMP integration test: Marked as intrusive and installing hping (just on linux for now) --- src/net/buffer_store.cpp | 1 + test/net/integration/icmp/setup.sh | 2 ++ test/net/integration/icmp/test.py | 3 +++ test/net/integration/icmp/vm.json | 3 ++- 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100755 test/net/integration/icmp/setup.sh diff --git a/src/net/buffer_store.cpp b/src/net/buffer_store.cpp index a8be8ffe58..394606d710 100644 --- a/src/net/buffer_store.cpp +++ b/src/net/buffer_store.cpp @@ -20,6 +20,7 @@ #if !defined(__MACH__) #include #else +#include extern void *memalign(size_t, size_t); #endif #include diff --git a/test/net/integration/icmp/setup.sh b/test/net/integration/icmp/setup.sh new file mode 100755 index 0000000000..06c9f73961 --- /dev/null +++ b/test/net/integration/icmp/setup.sh @@ -0,0 +1,2 @@ +#!/bin/bash +sudo apt-get update; sudo apt-get install hping diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index d1f1dc2e70..ccd494087c 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -31,6 +31,9 @@ def start_icmp_test(trigger_line): global num_successes + # Installing hping on linux + subprocess.call(["./setup.sh"]) + # 1 Ping: Checking output from callback in service.cpp print color.INFO(""), "Performing ping test" diff --git a/test/net/integration/icmp/vm.json b/test/net/integration/icmp/vm.json index 134f351755..cae9a2a028 100644 --- a/test/net/integration/icmp/vm.json +++ b/test/net/integration/icmp/vm.json @@ -1,5 +1,6 @@ { "image" : "test_icmp.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "mem" : 128 + "mem" : 128, + "intrusive" : "True" } From 7552954bdccdcfba7e808bbde0c0bd7f82d5b6b8 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 15:16:05 +0100 Subject: [PATCH 22/40] ICMP4 incl. integration test: Complete handling of ping-callbacks (removing them on timeout), updating integration test correspondingly and setting timeout to 40 (reply) and 50 (test) --- api/net/ip4/icmp4.hpp | 190 ++++++++++++++++---------- api/net/ip4/packet_icmp4.hpp | 3 +- src/net/ip4/icmp4.cpp | 21 +-- test/net/integration/icmp/service.cpp | 26 ++-- test/net/integration/icmp/setup.sh | 2 - test/net/integration/icmp/test.py | 23 ++-- 6 files changed, 155 insertions(+), 110 deletions(-) delete mode 100755 test/net/integration/icmp/setup.sh diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index 1d2b2d0eba..9e72bd8fe5 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -24,7 +24,88 @@ namespace net { - struct ICMP_packet; + /** + * User friendly ICMP packet used in callback (icmp_func) + */ + struct ICMP_packet { + using Span = gsl::span; + + bool got_reply_{false}; + uint16_t id_{0}; + uint16_t seq_{0}; + IP4::addr src_{0,0,0,0}; + IP4::addr dst_{0,0,0,0}; + icmp4::Type type_{icmp4::Type::NO_REPLY}; + uint8_t code_{0}; + uint16_t checksum_{0}; + Span payload_{nullptr, 0}; + + public: + ICMP_packet(uint16_t id, uint16_t seq) + : id_{id}, seq_{seq} + {} + + ICMP_packet(uint16_t id, uint16_t seq, IP4::addr src, IP4::addr dst, icmp4::Type type, uint8_t code, uint16_t checksum, const Span& payload) + : got_reply_{true}, id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, checksum_{checksum}, payload_{payload} + {} + + bool got_reply() const noexcept + { return got_reply_; } + + uint16_t id() const noexcept + { return id_; } + + uint16_t seq() const noexcept + { return seq_; } + + IP4::addr src() const noexcept + { return src_; } + + IP4::addr dst() const noexcept + { return dst_; } + + icmp4::Type type() const noexcept + { return type_; } + + uint8_t code() const noexcept + { return code_; } + + uint16_t checksum() const noexcept + { return checksum_; } + + Span payload() const noexcept + { return payload_; } + + std::string to_string() { + if (not got_reply()) + return "No reply received"; + + const std::string t = [&]() { + switch (type_) { + using namespace icmp4; + case Type::ECHO_REPLY: return "ECHO REPLY"; + case Type::DEST_UNREACHABLE: return "DESTINATION UNREACHABLE"; + case Type::REDIRECT: return "REDIRECT"; + case Type::ECHO: return "ECHO"; + case Type::TIME_EXCEEDED: return "TIME EXCEEDED"; + case Type::PARAMETER_PROBLEM: return "PARAMETER PROBLEM"; + case Type::TIMESTAMP: return "TIMESTAMP"; + case Type::TIMESTAMP_REPLY: return "TIMESTAMP REPLY"; + case Type::NO_REPLY: return "NO REPLY"; + } + }(); + + return "Identifier: " + std::to_string(id_) + "\n" + + "Sequence number: " + std::to_string(seq_) + "\n" + + "Source: " + src_.to_string() + "\n" + + "Destination: " + dst_.to_string() + "\n" + + "Type: " + t + "\n" + + "Code: " + std::to_string(code_) + "\n" + + "Checksum: " + std::to_string(checksum_) + "\n" + + "Data: " + std::string{payload_.begin(), payload_.end()}; + } + }; // < class ICMP_packet + struct ICMPv4 { @@ -67,6 +148,7 @@ namespace net { 'Q','R','S','T','U','V','W','X', 'Y','Z',1,2,3,4,5,6, 7,8}; + struct ICMP_callback { using icmp_func = ICMPv4::icmp_func; using Tuple = ICMPv4::Tuple; @@ -75,19 +157,45 @@ namespace net { icmp_func callback; Timers::id_t timer_id; - ICMP_callback() + ICMP_callback(ICMPv4& icmp, Tuple t, icmp_func cb) + : tuple{t}, callback{cb} { - timer_id = Timers::oneshot(std::chrono::microseconds(1000000), [&](Timers::id_t) { - // TODO - // Should not be called if this has already been erased (found reply): - // callback(ICMP_packet{0, 0, IP4::addr{0,0,0,0}, IP4::addr{0,0,0,0}, icmp4::Type::ECHO_REPLY, - // 0, 0, ICMP_packet::Span(nullptr, 0)}); + timer_id = Timers::oneshot(std::chrono::seconds(40), [&icmp, t](Timers::id_t) { + icmp.remove_ping_callback(t); }); } }; // < struct ICMP_callback std::map ping_callbacks_; + /** + * Find the ping-callback that this packet is a response to, execute it and erase the object + * from the ping_callbacks_ map + */ + inline void execute_ping_callback(icmp4::Packet& ping_response) { + // Find callback matching the reply + auto it = ping_callbacks_.find(std::make_pair(ping_response.id(), ping_response.sequence())); + + if (it != ping_callbacks_.end()) { + it->second.callback(ICMP_packet{ping_response.id(), ping_response.sequence(), ping_response.ip().src(), + ping_response.ip().dst(), ping_response.type(), ping_response.code(), ping_response.checksum(), ping_response.payload()}); + Timers::stop(it->second.timer_id); + ping_callbacks_.erase(it); + } + } + + /** Remove ICMP_callback from ping_callbacks_ map when its timer timeouts */ + inline void remove_ping_callback(Tuple key) { + auto it = ping_callbacks_.find(key); + + if (it != ping_callbacks_.end()) { + // Data back to user if no response found + it->second.callback(ICMP_packet{key.first, key.second}); + Timers::stop(it->second.timer_id); + ping_callbacks_.erase(it); + } + } + /** * Responding to a ping (echo) request * Called from receive-method @@ -104,74 +212,6 @@ namespace net { }; //< class ICMPv4 - /** User friendly ICMP packet used in callback (icmp_func) */ - struct ICMP_packet { - using Span = gsl::span; - - uint16_t id_; - uint16_t seq_; - IP4::addr src_; - IP4::addr dst_; - icmp4::Type type_; - uint8_t code_; - uint16_t checksum_; - Span payload_; - - public: - ICMP_packet(uint16_t id, uint16_t seq, IP4::addr src, IP4::addr dst, icmp4::Type type, uint8_t code, uint16_t checksum, const Span& payload) - : id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, checksum_{checksum}, payload_{payload} - {} - - uint16_t id() const noexcept - { return id_; } - - uint16_t seq() const noexcept - { return seq_; } - - IP4::addr src() const noexcept - { return src_; } - - IP4::addr dst() const noexcept - { return dst_; } - - icmp4::Type type() const noexcept - { return type_; } - - uint8_t code() const noexcept - { return code_; } - - uint16_t checksum() const noexcept - { return checksum_; } - - Span payload() const noexcept - { return payload_; } - - std::string to_string() { - const std::string t = [&]() { - switch (type_) { - using namespace icmp4; - case Type::ECHO_REPLY: return "ECHO REPLY"; - case Type::DEST_UNREACHABLE: return "DESTINATION UNREACHABLE"; - case Type::REDIRECT: return "REDIRECT"; - case Type::ECHO: return "ECHO"; - case Type::TIME_EXCEEDED: return "TIME EXCEEDED"; - case Type::PARAMETER_PROBLEM: return "PARAMETER PROBLEM"; - case Type::TIMESTAMP: return "TIMESTAMP"; - case Type::TIMESTAMP_REPLY: return "TIMESTAMP REPLY"; - } - }(); - - return "Identifier: " + std::to_string(id_) + "\n" + - "Sequence number: " + std::to_string(seq_) + "\n" + - "Source: " + src_.to_string() + "\n" + - "Destination: " + dst_.to_string() + "\n" + - "Type: " + t + "\n" + - "Code: " + std::to_string(code_) + "\n" + - "Checksum: " + std::to_string(checksum_) + "\n" + - "Data: " + std::string{payload_.begin(), payload_.end()}; - } - }; // < class ICMP_packet - } //< namespace net #endif //< NET_IP4_ICMPv4_HPP diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 882bbd9df8..9698e9787e 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -31,7 +31,8 @@ namespace icmp4 { TIME_EXCEEDED = 11, PARAMETER_PROBLEM = 12, TIMESTAMP = 13, - TIMESTAMP_REPLY = 14 + TIMESTAMP_REPLY = 14, + NO_REPLY = 100 // Custom }; class Packet { diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index f1da3995b7..1f8ebd21c0 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -37,16 +37,7 @@ namespace net { switch(req.type()) { case (icmp4::Type::ECHO_REPLY): debug(" PING Reply from %s\n", req.ip().src().str().c_str()); - // Find callback matching the reply - it = ping_callbacks_.find(std::make_pair(req.id(), req.sequence())); - if (it != ping_callbacks_.end()) { - it->second.callback(ICMP_packet{req.id(), req.sequence(), req.ip().src(), req.ip().dst(), req.type(), req.code(), - req.checksum(), req.payload()}); - - // Timers::stop(it->second.timer_id); - - ping_callbacks_.erase(it); - } + execute_ping_callback(req); break; case (icmp4::Type::DEST_UNREACHABLE): debug(" DESTINATION UNREACHABLE from %s\n", req.ip().src().str().c_str()); @@ -81,6 +72,8 @@ namespace net { debug(" TIMESTAMP REPLY from %s\n", req.ip().src().str().c_str()); // TODO May break; + default: + return; } } @@ -143,11 +136,9 @@ namespace net { req.set_sequence(sequence); if (callback) { - Tuple t = std::make_pair(temp_id, sequence); // Key into ping_callbacks_ map - ICMP_callback c; - c.tuple = t; - c.callback = callback; - ping_callbacks_.emplace(std::make_pair(t, c)); + ping_callbacks_.emplace(std::piecewise_construct, + std::forward_as_tuple(std::make_pair(temp_id, sequence)), + std::forward_as_tuple(ICMP_callback{*this, std::make_pair(temp_id, sequence), callback})); } debug(" Transmitting request to %s\n", dest_ip.to_string().c_str()); diff --git a/test/net/integration/icmp/service.cpp b/test/net/integration/icmp/service.cpp index 0fbb5bce74..fa348fef72 100644 --- a/test/net/integration/icmp/service.cpp +++ b/test/net/integration/icmp/service.cpp @@ -30,17 +30,27 @@ void Service::start(const std::string&) // ping google.com with callback inet.icmp().ping(IP4::addr{193,90,147,109}, [](ICMP_packet pckt) { - printf("Received packet\n%s\n", pckt.to_string().c_str()); + if (pckt.got_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + printf("Received packet\n%s\n", pckt.to_string().c_str()); + else + printf("No reply received from 193.90.147.109. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); }); - // Also possible - // inet.icmp().ping(IP4::addr{193,90,147,109}); // google.com - - /* No reply-ping + // No reply-ping inet.icmp().ping(IP4::addr{23,143,23,33}, [](ICMP_packet pckt) { - printf("Received packet\n%s\n", pckt.to_string().c_str()); + if (pckt.got_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + printf("Received packet\n%s\n", pckt.to_string().c_str()); + else + printf("No reply received from 23.143.23.33. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); }); + inet.icmp().ping(IP4::addr{23,143,23,33}, [](ICMP_packet pckt) { - printf("Received packet\n%s\n", pckt.to_string().c_str()); - });*/ + if (pckt.got_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + printf("Received packet\n%s\n", pckt.to_string().c_str()); + else + printf("No reply received from 23.143.23.33. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + }); + + // Also possible without callback: + // inet.icmp().ping(IP4::addr{193,90,147,109}); // google.com } diff --git a/test/net/integration/icmp/setup.sh b/test/net/integration/icmp/setup.sh deleted file mode 100755 index 06c9f73961..0000000000 --- a/test/net/integration/icmp/setup.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -sudo apt-get update; sudo apt-get install hping diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index ccd494087c..6930ce066d 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -19,11 +19,11 @@ # 1. Check that a ping request is received from google.com (193.90.147.109) # 2. Check that sending a udp packet to 10.0.0.45 (the IncludeOS service's IP) and port 8080 returns # an ICMP message with Destination unreachable (type 3), port unreachable (code 3). -# sudo hping 10.0.0.45 --udp -p 8080 -c 1 +# sudo hping3 10.0.0.45 --udp -p 8080 -c 1 # (count = 1) # 3. Check that sending an ip packet to 10.0.0.45 (the IncludeOS service's IP) with protocol 16 f.ex. # returns an ICMP message with Destination unreachable (type 3), protocol unreachable (code 2). -# sudo hping 10.0.0.45 -d 20 -0 --ipproto 16 -c 1 +# sudo hping3 10.0.0.45 -d 20 -0 --ipproto 16 -c 1 # (count = 1) num_successes = 0 @@ -31,14 +31,17 @@ def start_icmp_test(trigger_line): global num_successes - # Installing hping on linux - subprocess.call(["./setup.sh"]) + # Installing hping3 on linux + subprocess.call(["sudo", "apt-get", "update"]) + subprocess.call(["sudo", "apt-get", "install", "hping3"]) + # Installing hping3 on macOS + # subprocess.call(["brew", "install", "hping"]) # 1 Ping: Checking output from callback in service.cpp print color.INFO(""), "Performing ping test" output_data = "" - for x in range(0, 9): + for x in range(0, 11): output_data += vm.readline() print output_data @@ -49,7 +52,9 @@ def start_icmp_test(trigger_line): "Source: 193.90.147.109" in output_data and \ "Destination: 10.0.0.45" in output_data and \ "Type: ECHO REPLY" in output_data and \ - "Code: 0" in output_data: + "Code: 0" in output_data and \ + "No reply received from 23.143.23.33. Identifier: 1. Sequence number: 0" in output_data and \ + "No reply received from 23.143.23.33. Identifier: 2. Sequence number: 0" in output_data: num_successes += 1 print color.INFO(""), "Ping test succeeded" else: @@ -58,7 +63,7 @@ def start_icmp_test(trigger_line): # 2 Port unreachable print color.INFO(""), "Performing Destination Unreachable (port) test" # Sending 1 udp packet to 10.0.0.45 to port 8080 - udp_port_output = subprocess.check_output(["sudo", "hping", "10.0.0.45", "--udp", "-p", "8080", "-c", "1"]) + udp_port_output = subprocess.check_output(["sudo", "hping3", "10.0.0.45", "--udp", "-p", "8080", "-c", "1"]) print udp_port_output # Validate content in udp_port_output: @@ -71,7 +76,7 @@ def start_icmp_test(trigger_line): # 3 Protocol unreachable print color.INFO(""), "Performing Destination Unreachable (protocol) test" # Sending 1 raw ip packet to 10.0.0.45 with protocol 16 - rawip_protocol_output = subprocess.check_output(["sudo", "hping", "10.0.0.45", "-d", "20", "-0", "--ipproto", "16", "-c", "1"]) + rawip_protocol_output = subprocess.check_output(["sudo", "hping3", "10.0.0.45", "-d", "20", "-0", "--ipproto", "16", "-c", "1"]) print rawip_protocol_output # Validate content in rawip_protocol_output: @@ -92,4 +97,4 @@ def start_icmp_test(trigger_line): vm.on_output("Service IP address is 10.0.0.45", start_icmp_test); # Boot the VM, taking a timeout as parameter -vm.cmake().boot(20).clean() +vm.cmake().boot(50).clean() From 54d7574f2b30743e58c7e234ed15e52804992aa7 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 16:48:43 +0100 Subject: [PATCH 23/40] ICMP integration test: -y added to install hping3 --- test/net/integration/icmp/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index 6930ce066d..bd428ad03b 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -33,7 +33,7 @@ def start_icmp_test(trigger_line): # Installing hping3 on linux subprocess.call(["sudo", "apt-get", "update"]) - subprocess.call(["sudo", "apt-get", "install", "hping3"]) + subprocess.call(["sudo", "apt-get", "-y", "install", "hping3"]) # Installing hping3 on macOS # subprocess.call(["brew", "install", "hping"]) From df60f528bcd80eb61675d8e4e2fb30060294c47c Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 10 Mar 2017 17:00:14 +0100 Subject: [PATCH 24/40] ICMP4: Parameter problem (with pointer indicating the error) and time exceeded messages implemented --- api/net/ip4/icmp4.hpp | 27 +++++++++++++++++++++++---- api/net/ip4/packet_icmp4.hpp | 6 ++++++ src/net/ip4/icmp4.cpp | 21 ++++++++++++++++----- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index 9e72bd8fe5..18bed26c0f 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -123,12 +123,31 @@ namespace net { inline void set_network_out(downstream s) { network_layer_out_ = s; }; - /** Destination Unreachable sent from host because of port (UDP) or protocol (IP4) unreachable */ + /** + * Destination Unreachable sent from host because of port (UDP) or protocol (IP4) unreachable + */ void destination_unreachable(Packet_ptr pckt, icmp4::code::Dest_unreachable code); + /** + * + */ void redirect(icmp4::Packet& req, icmp4::code::Redirect code); - void time_exceeded(icmp4::Packet& req, icmp4::code::Time_exceeded code); - void parameter_problem(icmp4::Packet& req); + + /** + * Sending a Time Exceeded message from a host when fragment reassembly time exceeded (code 1) + * Sending a Time Exceeded message from a gateway when time to live exceeded in transit (code 0) + */ + void time_exceeded(Packet_ptr pckt, icmp4::code::Time_exceeded code); + + /** + * Sending a Parameter Problem message if the gateway or host processing a datagram finds a problem with + * the header parameters such that it cannot complete processing the datagram. The message is only sent if + * the error caused the datagram to be discarded + * Code 0 means Pointer (uint8_t after checksum) indicates the error/identifies the octet where an error was detected + * in the IP header + * Code 1 means that a required option is missing + */ + void parameter_problem(Packet_ptr pckt, uint8_t error); // May void timestamp_request(IP4::addr ip); @@ -206,7 +225,7 @@ namespace net { icmp_func callback = nullptr, uint16_t sequence = 0); /** Send response without id and sequence number */ - void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); + void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, uint8_t error = 255); /** Send response with id and sequence number */ void send_response_with_id(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 9698e9787e..5cd1129868 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -95,6 +95,12 @@ namespace icmp4 { void set_sequence(uint16_t s) noexcept { header().sequence = s; } + /** + * RFC 792 Parameter problem f.ex.: error (Pointer) is placed in the first byte after checksum + * (identifier and sequence is not used when pointer is used) + */ + void set_pointer(uint8_t error) + { header().identifier = error; } uint16_t compute_checksum() const noexcept { diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index 1f8ebd21c0..d97875d667 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -89,12 +89,20 @@ namespace net { // send_response(req, icmp4::Type::REDIRECT, (uint8_t) code, icmp4::Packet::Span(, )); } - void ICMPv4::time_exceeded(icmp4::Packet& /* req */, icmp4::code::Time_exceeded /* code */) { - // send_response(req, icmp4::Type::TIME_EXCEEDED, (uint8_t) code, icmp4::Packet::Span(, )); + void ICMPv4::time_exceeded(Packet_ptr pckt, icmp4::code::Time_exceeded code) { + if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header + return; + auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); + auto pckt_icmp4 = icmp4::Packet(std::move(pckt_ip4)); + send_response(pckt_icmp4, icmp4::Type::TIME_EXCEEDED, (uint8_t) code, pckt_icmp4.header_and_data()); } - void ICMPv4::parameter_problem(icmp4::Packet& /* req */) { - // send_response(req, icmp4::Type::PARAMETER_PROBLEM, 0, icmp4::Packet::Span(, )); + void ICMPv4::parameter_problem(Packet_ptr pckt, uint8_t error) { + if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header + return; + auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); + auto pckt_icmp4 = icmp4::Packet(std::move(pckt_ip4)); + send_response(pckt_icmp4, icmp4::Type::PARAMETER_PROBLEM, 0, pckt_icmp4.header_and_data(), error); } void ICMPv4::timestamp_request(IP4::addr /* ip */) { @@ -155,7 +163,7 @@ namespace net { network_layer_out_(req.release()); } - void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload) { + void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, uint8_t error) { // Provision new IP4-packet icmp4::Packet res(inet_.ip_packet_factory()); @@ -175,6 +183,9 @@ namespace net { // Add checksum res.set_checksum(); + if (error != 255) + res.set_pointer(error); + debug(" Response size: %i payload size: %i, checksum: 0x%x\n", res.ip().size(), res.payload().size(), res.compute_checksum()); From d8360bf096442208287b832ace54b2f7772699f8 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Tue, 14 Mar 2017 13:41:07 +0100 Subject: [PATCH 25/40] ICMP4: got_reply_ -> is_reply_, integration test pinging gateway (no need for IP forwarding) --- api/net/ip4/icmp4.hpp | 10 ++++----- test/net/integration/icmp/service.cpp | 32 ++++++++++++++++++--------- test/net/integration/icmp/test.py | 12 +++++----- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index 18bed26c0f..d3a3f58cd2 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -30,7 +30,7 @@ namespace net { struct ICMP_packet { using Span = gsl::span; - bool got_reply_{false}; + bool is_reply_{false}; uint16_t id_{0}; uint16_t seq_{0}; IP4::addr src_{0,0,0,0}; @@ -46,11 +46,11 @@ namespace net { {} ICMP_packet(uint16_t id, uint16_t seq, IP4::addr src, IP4::addr dst, icmp4::Type type, uint8_t code, uint16_t checksum, const Span& payload) - : got_reply_{true}, id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, checksum_{checksum}, payload_{payload} + : is_reply_{true}, id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, checksum_{checksum}, payload_{payload} {} - bool got_reply() const noexcept - { return got_reply_; } + bool is_reply() const noexcept + { return is_reply_; } uint16_t id() const noexcept { return id_; } @@ -77,7 +77,7 @@ namespace net { { return payload_; } std::string to_string() { - if (not got_reply()) + if (not is_reply()) return "No reply received"; const std::string t = [&]() { diff --git a/test/net/integration/icmp/service.cpp b/test/net/integration/icmp/service.cpp index fa348fef72..7b4926f3b8 100644 --- a/test/net/integration/icmp/service.cpp +++ b/test/net/integration/icmp/service.cpp @@ -28,27 +28,39 @@ void Service::start(const std::string&) { 10, 0, 0, 1 } ); // Gateway printf("Service IP address is %s\n", inet.ip_addr().str().c_str()); + // ping gateway + inet.icmp().ping(inet.gateway(), [](ICMP_packet pckt) { + if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + printf("Received packet from gateway\n%s\n", pckt.to_string().c_str()); + else + printf("No reply received from gateway. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + }); + + /* If IP forwarding on: // ping google.com with callback inet.icmp().ping(IP4::addr{193,90,147,109}, [](ICMP_packet pckt) { - if (pckt.got_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY printf("Received packet\n%s\n", pckt.to_string().c_str()); else printf("No reply received from 193.90.147.109. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); }); + */ - // No reply-ping - inet.icmp().ping(IP4::addr{23,143,23,33}, [](ICMP_packet pckt) { - if (pckt.got_reply()) // or pckt.type() != icmp4::Type::NO_REPLY - printf("Received packet\n%s\n", pckt.to_string().c_str()); + // No reply-pings + // Waiting 30 seconds for reply + inet.icmp().ping(IP4::addr{10,0,0,42}, [](ICMP_packet pckt) { + if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + printf("Received packet from 10.0.0.42\n%s\n", pckt.to_string().c_str()); else - printf("No reply received from 23.143.23.33. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + printf("No reply received from 10.0.0.42. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); }); - inet.icmp().ping(IP4::addr{23,143,23,33}, [](ICMP_packet pckt) { - if (pckt.got_reply()) // or pckt.type() != icmp4::Type::NO_REPLY - printf("Received packet\n%s\n", pckt.to_string().c_str()); + // Waiting 30 seconds for reply + inet.icmp().ping(IP4::addr{10,0,0,43}, [](ICMP_packet pckt) { + if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + printf("Received packet from 10.0.0.43\n%s\n", pckt.to_string().c_str()); else - printf("No reply received from 23.143.23.33. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + printf("No reply received from 10.0.0.43. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); }); // Also possible without callback: diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index bd428ad03b..7b9cb13f70 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -20,11 +20,11 @@ # 2. Check that sending a udp packet to 10.0.0.45 (the IncludeOS service's IP) and port 8080 returns # an ICMP message with Destination unreachable (type 3), port unreachable (code 3). # sudo hping3 10.0.0.45 --udp -p 8080 -c 1 -# (count = 1) # 3. Check that sending an ip packet to 10.0.0.45 (the IncludeOS service's IP) with protocol 16 f.ex. # returns an ICMP message with Destination unreachable (type 3), protocol unreachable (code 2). # sudo hping3 10.0.0.45 -d 20 -0 --ipproto 16 -c 1 -# (count = 1) + +# ICMP waiting 30 seconds for ping reply num_successes = 0 @@ -46,15 +46,15 @@ def start_icmp_test(trigger_line): print output_data - if "Received packet" in output_data and \ + if "Received packet from gateway" in output_data and \ "Identifier: 0" in output_data and \ "Sequence number: 0" in output_data and \ - "Source: 193.90.147.109" in output_data and \ + "Source: 10.0.0.1" in output_data and \ "Destination: 10.0.0.45" in output_data and \ "Type: ECHO REPLY" in output_data and \ "Code: 0" in output_data and \ - "No reply received from 23.143.23.33. Identifier: 1. Sequence number: 0" in output_data and \ - "No reply received from 23.143.23.33. Identifier: 2. Sequence number: 0" in output_data: + "No reply received from 10.0.0.42. Identifier: 1. Sequence number: 0" in output_data and \ + "No reply received from 10.0.0.43. Identifier: 2. Sequence number: 0" in output_data: num_successes += 1 print color.INFO(""), "Ping test succeeded" else: From 92c5f80e97482e45125cfe4acd8f2d1419baab3c Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Tue, 14 Mar 2017 14:37:15 +0100 Subject: [PATCH 26/40] Ethernet: Trailer nego and encapsulation (ethertype) added with counter --- api/net/ethernet/ethernet.hpp | 4 ++++ api/net/ethernet/ethertype.hpp | 17 ++++++++++------- src/net/ethernet/ethernet.cpp | 11 ++++++++++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/api/net/ethernet/ethernet.hpp b/api/net/ethernet/ethernet.hpp index ac28d1b4bd..c9896daa0d 100644 --- a/api/net/ethernet/ethernet.hpp +++ b/api/net/ethernet/ethernet.hpp @@ -90,6 +90,9 @@ namespace net { uint64_t get_packets_dropped() { return packets_dropped_; } + uint32_t get_trailer_packets_dropped() + { return trailer_packets_dropped_; } + private: const addr& mac_; @@ -97,6 +100,7 @@ namespace net { uint64_t& packets_rx_; uint64_t& packets_tx_; uint32_t& packets_dropped_; + uint32_t& trailer_packets_dropped_; /** Upstream OUTPUT connections */ upstream ip4_upstream_ = nullptr; diff --git a/api/net/ethernet/ethertype.hpp b/api/net/ethernet/ethertype.hpp index 476c3cd15c..943b856cc7 100644 --- a/api/net/ethernet/ethertype.hpp +++ b/api/net/ethernet/ethertype.hpp @@ -25,13 +25,16 @@ namespace net { /** Little-endian ethertypes. More entries here: http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml */ enum class Ethertype : uint16_t { - IP4 = 0x8, - ARP = 0x608, - WOL = 0x4208, - IP6 = 0xdd86, - FLOW = 0x888, - JUMBO = 0x7088, - VLAN = 0x81 + IP4 = 0x8, + ARP = 0x608, + WOL = 0x4208, + IP6 = 0xdd86, + FLOW = 0x888, + JUMBO = 0x7088, + VLAN = 0x81, + TRAILER_NEGO = 0x0010, // RFC 893 Trailer negotiation + TRAILER_FIRST = 0x0110, // RFC 893, 1122: Trailer encapsulation - NB: byte order + TRAILER_LAST = 0x0f10 }; } diff --git a/src/net/ethernet/ethernet.cpp b/src/net/ethernet/ethernet.cpp index 7677bd477e..a39c5276f5 100644 --- a/src/net/ethernet/ethernet.cpp +++ b/src/net/ethernet/ethernet.cpp @@ -37,6 +37,7 @@ namespace net { packets_rx_{Statman::get().create(Stat::UINT64, ".ethernet.packets_rx").get_uint64()}, packets_tx_{Statman::get().create(Stat::UINT64, ".ethernet.packets_tx").get_uint64()}, packets_dropped_{Statman::get().create(Stat::UINT32, ".ethernet.packets_dropped").get_uint32()}, + trailer_packets_dropped_{Statman::get().create(Stat::UINT32, ".ethernet.trailer_packets_dropped").get_uint32()}, ip4_upstream_{ignore}, ip6_upstream_{ignore}, arp_upstream_{ignore}, @@ -123,12 +124,20 @@ namespace net { break; default: + // Trailer negotiation and encapsulation RFC 893 and 1122 + if (eth->type() == Ethertype::TRAILER_NEGO or (eth->type() >= Ethertype::TRAILER_FIRST and eth->type() <= Ethertype::TRAILER_LAST)) { + dropped = true; + trailer_packets_dropped_++; + debug2("Trailer packet\n"); + break; + } + dropped = true; uint16_t length_field = net::ntohs(static_cast(eth->type())); // This might be 802.3 LLC traffic if (length_field > 1500) { debug2(" UNKNOWN ethertype 0x%x\n", ntohs(eth->type())); - }else { + } else { debug2("IEEE802.3 Length field: 0x%x\n", ntohs(eth->type())); } break; From aa4cd6937dc98ea2fa3d88974459d314b9d84dfb Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Tue, 14 Mar 2017 14:44:26 +0100 Subject: [PATCH 27/40] ICMP_packet: to_string implementation moved to cpp file --- api/net/ip4/icmp4.hpp | 29 +---------------------------- src/net/ip4/icmp4.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index d3a3f58cd2..7027428e5e 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -76,34 +76,7 @@ namespace net { Span payload() const noexcept { return payload_; } - std::string to_string() { - if (not is_reply()) - return "No reply received"; - - const std::string t = [&]() { - switch (type_) { - using namespace icmp4; - case Type::ECHO_REPLY: return "ECHO REPLY"; - case Type::DEST_UNREACHABLE: return "DESTINATION UNREACHABLE"; - case Type::REDIRECT: return "REDIRECT"; - case Type::ECHO: return "ECHO"; - case Type::TIME_EXCEEDED: return "TIME EXCEEDED"; - case Type::PARAMETER_PROBLEM: return "PARAMETER PROBLEM"; - case Type::TIMESTAMP: return "TIMESTAMP"; - case Type::TIMESTAMP_REPLY: return "TIMESTAMP REPLY"; - case Type::NO_REPLY: return "NO REPLY"; - } - }(); - - return "Identifier: " + std::to_string(id_) + "\n" + - "Sequence number: " + std::to_string(seq_) + "\n" + - "Source: " + src_.to_string() + "\n" + - "Destination: " + dst_.to_string() + "\n" + - "Type: " + t + "\n" + - "Code: " + std::to_string(code_) + "\n" + - "Checksum: " + std::to_string(checksum_) + "\n" + - "Data: " + std::string{payload_.begin(), payload_.end()}; - } + std::string to_string(); }; // < class ICMP_packet diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index d97875d667..72a2992714 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -20,6 +20,39 @@ namespace net { + // ---------------------------- ICMP_packet ---------------------------- + + std::string ICMP_packet::to_string() { + if (not is_reply()) + return "No reply received"; + + const std::string t = [&]() { + switch (type_) { + using namespace icmp4; + case Type::ECHO_REPLY: return "ECHO REPLY"; + case Type::DEST_UNREACHABLE: return "DESTINATION UNREACHABLE"; + case Type::REDIRECT: return "REDIRECT"; + case Type::ECHO: return "ECHO"; + case Type::TIME_EXCEEDED: return "TIME EXCEEDED"; + case Type::PARAMETER_PROBLEM: return "PARAMETER PROBLEM"; + case Type::TIMESTAMP: return "TIMESTAMP"; + case Type::TIMESTAMP_REPLY: return "TIMESTAMP REPLY"; + case Type::NO_REPLY: return "NO REPLY"; + } + }(); + + return "Identifier: " + std::to_string(id_) + "\n" + + "Sequence number: " + std::to_string(seq_) + "\n" + + "Source: " + src_.to_string() + "\n" + + "Destination: " + dst_.to_string() + "\n" + + "Type: " + t + "\n" + + "Code: " + std::to_string(code_) + "\n" + + "Checksum: " + std::to_string(checksum_) + "\n" + + "Data: " + std::string{payload_.begin(), payload_.end()}; + } + + // ------------------------------ ICMPv4 ------------------------------ + int ICMPv4::request_id_ = 0; ICMPv4::ICMPv4(Stack& inet) : From 6805695fb597d8b39548a4b209124ace69973747 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Wed, 15 Mar 2017 09:12:56 +0100 Subject: [PATCH 28/40] Ethernet: Trailer encapsulation now taking byte order into account. Check on transmitted packets as well --- api/net/ethernet/ethertype.hpp | 2 +- src/net/ethernet/ethernet.cpp | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/api/net/ethernet/ethertype.hpp b/api/net/ethernet/ethertype.hpp index 943b856cc7..e8940fdd83 100644 --- a/api/net/ethernet/ethertype.hpp +++ b/api/net/ethernet/ethertype.hpp @@ -33,7 +33,7 @@ namespace net { JUMBO = 0x7088, VLAN = 0x81, TRAILER_NEGO = 0x0010, // RFC 893 Trailer negotiation - TRAILER_FIRST = 0x0110, // RFC 893, 1122: Trailer encapsulation - NB: byte order + TRAILER_FIRST = 0x0110, // RFC 893, 1122 Trailer encapsulation TRAILER_LAST = 0x0f10 }; diff --git a/src/net/ethernet/ethernet.cpp b/src/net/ethernet/ethernet.cpp index a39c5276f5..7facef3cf0 100644 --- a/src/net/ethernet/ethernet.cpp +++ b/src/net/ethernet/ethernet.cpp @@ -47,6 +47,14 @@ namespace net { void Ethernet::transmit(net::Packet_ptr pckt, addr dest, Ethertype type) { + uint16_t t = net::ntohs(static_cast(type)); + // Trailer negotiation and encapsulation RFC 893 and 1122 + if (UNLIKELY(t == net::ntohs(static_cast(Ethertype::TRAILER_NEGO)) or + (t >= net::ntohs(static_cast(Ethertype::TRAILER_FIRST)) and + t <= net::ntohs(static_cast(Ethertype::TRAILER_LAST))))) { + debug(" Ethernet type Trailer is not supported. Packet is not transmitted\n"); + return; + } debug(" Transmitting %i b, from %s -> %s. Type: 0x%x\n", pckt->size(), mac_.str().c_str(), dest.str().c_str(), type); @@ -124,22 +132,23 @@ namespace net { break; default: + uint16_t type = net::ntohs(static_cast(eth->type())); + dropped = true; + // Trailer negotiation and encapsulation RFC 893 and 1122 - if (eth->type() == Ethertype::TRAILER_NEGO or (eth->type() >= Ethertype::TRAILER_FIRST and eth->type() <= Ethertype::TRAILER_LAST)) { - dropped = true; + if (UNLIKELY(type == net::ntohs(static_cast(Ethertype::TRAILER_NEGO)) or + (type >= net::ntohs(static_cast(Ethertype::TRAILER_FIRST)) and + type <= net::ntohs(static_cast(Ethertype::TRAILER_LAST))))) { trailer_packets_dropped_++; debug2("Trailer packet\n"); break; } - dropped = true; - uint16_t length_field = net::ntohs(static_cast(eth->type())); // This might be 802.3 LLC traffic - if (length_field > 1500) { + if (type > 1500) debug2(" UNKNOWN ethertype 0x%x\n", ntohs(eth->type())); - } else { + else debug2("IEEE802.3 Length field: 0x%x\n", ntohs(eth->type())); - } break; } From 1f0d980dffe6e0c1e8f615f70ed7279f4b71292b Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Thu, 16 Mar 2017 11:37:28 +0100 Subject: [PATCH 29/40] Test vm.json: Removed cpu host to be able to run boot . without getting error CPU model host requires KVM --- test/hw/integration/serial/vm.json | 3 +-- test/net/integration/router/vm.json | 1 - test/net/integration/tcp/vm.json | 1 - test/net/integration/transmit/vm.json | 1 - test/net/integration/udp/vm.json | 1 - test/posix/integration/conf/vm.json | 1 - test/posix/integration/file_fd/vm.json | 1 - test/posix/integration/stat/vm.json | 1 - test/posix/integration/syslog_default/vm.json | 1 - test/posix/integration/syslog_plugin/vm.json | 1 - test/posix/integration/udp/vm.json | 1 - test/posix/integration/utsname/vm.json | 1 - test/stress/vm.json | 1 - test/util/integration/tar/vm.json | 1 - test/util/integration/tar_gz/vm.json | 1 - 15 files changed, 1 insertion(+), 16 deletions(-) diff --git a/test/hw/integration/serial/vm.json b/test/hw/integration/serial/vm.json index 808cf0863a..1dc6811ce1 100644 --- a/test/hw/integration/serial/vm.json +++ b/test/hw/integration/serial/vm.json @@ -1,4 +1,3 @@ { - "image" : "test_serial.img", - "cpu" : "host" + "image" : "test_serial.img" } diff --git a/test/net/integration/router/vm.json b/test/net/integration/router/vm.json index 4bf938370e..f2a57a78a8 100644 --- a/test/net/integration/router/vm.json +++ b/test/net/integration/router/vm.json @@ -1,6 +1,5 @@ { "image": "test_router.img", - "cpu" : "host", "mem" : 320, "net" : [{"device" : "virtio"}, {"device" : "virtio"}], diff --git a/test/net/integration/tcp/vm.json b/test/net/integration/tcp/vm.json index 1b55c1cead..2f0cd16ca1 100644 --- a/test/net/integration/tcp/vm.json +++ b/test/net/integration/tcp/vm.json @@ -1,6 +1,5 @@ { "image" : "test_tcp.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 256 } diff --git a/test/net/integration/transmit/vm.json b/test/net/integration/transmit/vm.json index 26fde4f3b9..8bdfa04e78 100644 --- a/test/net/integration/transmit/vm.json +++ b/test/net/integration/transmit/vm.json @@ -3,6 +3,5 @@ "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a", "log" : "net_result.pcap"}], - "cpu" : "host", "mem" : 256 } diff --git a/test/net/integration/udp/vm.json b/test/net/integration/udp/vm.json index a7e0aae2ae..cac6ea2460 100644 --- a/test/net/integration/udp/vm.json +++ b/test/net/integration/udp/vm.json @@ -1,6 +1,5 @@ { "image" : "test_udp.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 256 } diff --git a/test/posix/integration/conf/vm.json b/test/posix/integration/conf/vm.json index 51f2046b2d..2a04416f47 100644 --- a/test/posix/integration/conf/vm.json +++ b/test/posix/integration/conf/vm.json @@ -1,6 +1,5 @@ { "image" : "test_conf.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } diff --git a/test/posix/integration/file_fd/vm.json b/test/posix/integration/file_fd/vm.json index 6e71e7b248..40a33734d6 100644 --- a/test/posix/integration/file_fd/vm.json +++ b/test/posix/integration/file_fd/vm.json @@ -1,6 +1,5 @@ { "image" : "test_file_fd.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } diff --git a/test/posix/integration/stat/vm.json b/test/posix/integration/stat/vm.json index a739461eaf..68cdfd43fa 100644 --- a/test/posix/integration/stat/vm.json +++ b/test/posix/integration/stat/vm.json @@ -1,6 +1,5 @@ { "image" : "test_posix_stat.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } diff --git a/test/posix/integration/syslog_default/vm.json b/test/posix/integration/syslog_default/vm.json index 7d23e7e6b1..0bac2a471c 100644 --- a/test/posix/integration/syslog_default/vm.json +++ b/test/posix/integration/syslog_default/vm.json @@ -1,6 +1,5 @@ { "image" : "test_syslog_default.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } diff --git a/test/posix/integration/syslog_plugin/vm.json b/test/posix/integration/syslog_plugin/vm.json index 950f6f3515..d9235b3153 100644 --- a/test/posix/integration/syslog_plugin/vm.json +++ b/test/posix/integration/syslog_plugin/vm.json @@ -1,6 +1,5 @@ { "image" : "test_syslog_plugin.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } diff --git a/test/posix/integration/udp/vm.json b/test/posix/integration/udp/vm.json index 6b18ad6ee4..922e9089ec 100644 --- a/test/posix/integration/udp/vm.json +++ b/test/posix/integration/udp/vm.json @@ -1,7 +1,6 @@ { "image" : "test_posix_udp.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128, "time_sensitive" : "True" } diff --git a/test/posix/integration/utsname/vm.json b/test/posix/integration/utsname/vm.json index f1dd45c30a..0cc46287ef 100644 --- a/test/posix/integration/utsname/vm.json +++ b/test/posix/integration/utsname/vm.json @@ -1,6 +1,5 @@ { "image" : "test_posix_utsname.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } diff --git a/test/stress/vm.json b/test/stress/vm.json index 7b2c7d4b6e..f86b163a8d 100644 --- a/test/stress/vm.json +++ b/test/stress/vm.json @@ -1,6 +1,5 @@ { "image" : "test_stresstest.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 300 } diff --git a/test/util/integration/tar/vm.json b/test/util/integration/tar/vm.json index c211783e42..170e7a8a36 100644 --- a/test/util/integration/tar/vm.json +++ b/test/util/integration/tar/vm.json @@ -1,6 +1,5 @@ { "image" : "test_tar.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } diff --git a/test/util/integration/tar_gz/vm.json b/test/util/integration/tar_gz/vm.json index 1cfd427369..95c1475e6b 100644 --- a/test/util/integration/tar_gz/vm.json +++ b/test/util/integration/tar_gz/vm.json @@ -1,6 +1,5 @@ { "image" : "test_tar_gz.img", "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], - "cpu" : "host", "mem" : 128 } From 555046a9e717a749d8491434431d018bdcbcc6d7 Mon Sep 17 00:00:00 2001 From: AnnikaH Date: Fri, 17 Mar 2017 11:38:13 +0100 Subject: [PATCH 30/40] ICMP4: printf -> debug --- src/net/ip4/icmp4.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index 72a2992714..20a58c5be1 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -83,7 +83,7 @@ namespace net { // Only sent by gateways. Incoming: Update routing information based on the message break; case (icmp4::Type::ECHO): - printf(" PING from %s\n", req.ip().src().str().c_str()); + debug(" PING from %s\n", req.ip().src().str().c_str()); ping_reply(req); break; case (icmp4::Type::TIME_EXCEEDED): From 523b84df9124cb40ac8768e8f10c4781f92e8954 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 17 Mar 2017 12:38:26 +0100 Subject: [PATCH 31/40] ICMP forwarding error messages to UDP and TCP through Inet --- api/net/inet.hpp | 10 ++ api/net/inet4.hpp | 21 ++++ api/net/inet_common.hpp | 18 ++-- api/net/ip4/icmp4.hpp | 6 +- api/net/ip4/icmp4_codes.hpp | 64 ------------ api/net/ip4/icmp4_common.hpp | 183 +++++++++++++++++++++++++++++++++++ api/net/ip4/packet_icmp4.hpp | 13 --- api/net/ip4/udp.hpp | 11 ++- api/net/ip4/udp_socket.hpp | 39 +++++--- api/net/tcp/tcp.hpp | 6 ++ src/net/ip4/icmp4.cpp | 53 +++------- src/net/ip4/udp.cpp | 18 ++++ src/net/ip4/udp_socket.cpp | 11 ++- src/net/tcp/tcp.cpp | 7 ++ 14 files changed, 315 insertions(+), 145 deletions(-) delete mode 100644 api/net/ip4/icmp4_codes.hpp create mode 100644 api/net/ip4/icmp4_common.hpp diff --git a/api/net/inet.hpp b/api/net/inet.hpp index 34324b0930..917071c674 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -41,6 +41,10 @@ namespace net { using Route_checker = delegate; using IP_packet_factory = delegate; + using Span = gsl::span; + using Error_type = icmp4::Type; + using Error_code = uint8_t; + template using resolve_func = delegate; @@ -56,6 +60,12 @@ namespace net { virtual UDP& udp() = 0; virtual ICMPv4& icmp() = 0; + /** + * Error report in accordance with RFC 1122 + * An ICMP error message has been received - forward to transport layer (UDP or TCP) + */ + virtual void error_report(Error_type type, Error_code code, Span icmp_payload) = 0; + virtual void set_forward_delg(Forward_delg) = 0; virtual void set_route_checker(Route_checker) = 0; virtual void cache_link_ip(typename IPV::addr, MAC::Addr) = 0; diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index cdfa253096..d493f1fe02 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -74,6 +74,27 @@ namespace net { /** Get the DHCP client (if any) */ auto dhclient() { return dhcp_; } + /** + * Error report in accordance with RFC 1122 + * An ICMP error message has been received - forward to transport layer (UDP or TCP) + */ + void error_report(Error_type type, Error_code code, Span icmp_payload) override { + // Payload contains info about the sent packet that failed to be delivered + IP4::header* header = (IP4::header*) icmp_payload.data(); + + if ((Protocol) header->protocol == Protocol::UDP) { + UDP::header* udp_header = (UDP::header*) (icmp_payload.data() + sizeof(IP4::header)); + auto src_port = htons(udp_header->sport); + auto dest_port = htons(udp_header->dport); + udp_.error_report(type, code, header->saddr, src_port, header->daddr, dest_port); + } else if ((Protocol) header->protocol == Protocol::TCP) { + tcp::Header* tcp_header = (tcp::Header*) (icmp_payload.data() + sizeof(IP4::header)); + auto src_port = htons(tcp_header->source_port); + auto dest_port = htons(tcp_header->destination_port); + tcp_.error_report(type, code, header->saddr, src_port, header->daddr, dest_port); + } + } + /** * Set the forwarding delegate used by this stack. * If set it will get all incoming packets not intended for this stack. diff --git a/api/net/inet_common.hpp b/api/net/inet_common.hpp index 043e115f6d..a3d218d889 100644 --- a/api/net/inet_common.hpp +++ b/api/net/inet_common.hpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include namespace net { // Packet must be forward declared to avoid circular dependency @@ -85,15 +85,13 @@ namespace net { */ enum class Protocol : uint8_t { HOPOPT = 0, - ICMPv4 = 1, - IP4v4 = 4, // IPv4 encapsulation - TCP = 6, - UDP = 17, - IPv6 = 41, // IPv6 encapsulation - ICMPv6 = 58 - }; - - + ICMPv4 = 1, + IPv4 = 4, // IPv4 encapsulation + TCP = 6, + UDP = 17, + IPv6 = 41, // IPv6 encapsulation + ICMPv6 = 58 + }; inline uint16_t new_ephemeral_port() noexcept { return port_ranges::DYNAMIC_START + rand() % (port_ranges::DYNAMIC_END - port_ranges::DYNAMIC_START); } diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index 7027428e5e..b49f70b125 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -77,7 +77,7 @@ namespace net { { return payload_; } std::string to_string(); - }; // < class ICMP_packet + }; // < struct ICMP_packet struct ICMPv4 { @@ -94,7 +94,7 @@ namespace net { // Delegate output to network layer inline void set_network_out(downstream s) - { network_layer_out_ = s; }; + { network_layer_out_ = s; }; /** * Destination Unreachable sent from host because of port (UDP) or protocol (IP4) unreachable @@ -160,6 +160,8 @@ namespace net { std::map ping_callbacks_; + void forward_to_transport_layer(icmp4::Packet& req); + /** * Find the ping-callback that this packet is a response to, execute it and erase the object * from the ping_callbacks_ map diff --git a/api/net/ip4/icmp4_codes.hpp b/api/net/ip4/icmp4_codes.hpp deleted file mode 100644 index b20fc468af..0000000000 --- a/api/net/ip4/icmp4_codes.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// This file is a part of the IncludeOS unikernel - www.includeos.org -// -// Copyright 2017 Oslo and Akershus University College of Applied Sciences -// and Alfred Bratterud -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef NET_IP4_ICMP4_CODES_HPP -#define NET_IP4_ICMP4_CODES_HPP - -namespace net { - - /** ICMP4 Codes so that UDP and IP4 can use these */ - namespace icmp4 { - namespace code { - enum class Dest_unreachable : uint8_t { - NET, - HOST, - PROTOCOL, - PORT, - FRAGMENTATION, - SRC_ROUTE, - NET_UNKNOWN, // RFC 1122 - HOST_UNKNOWN, - SRC_HOST_ISOLATED, - NET_PROHIBITED, - HOST_PROHIBITED, - NET_FOR_TOS, - HOST_FOR_TOS - }; - - enum class Redirect : uint8_t { - NET, - HOST, - TOS_NET, - TOS_HOST - }; - - enum class Time_exceeded : uint8_t { - TTL, - FRAGMENT_REASSEMBLY - }; - - enum class Parameter_problem : uint8_t { - POINTER_INDICATES_ERROR, - REQUIRED_OPT_MISSING // RFC 1122 - }; - - } // < namespace code - } // < namespace icmp4 - -} // < namespace net - -#endif diff --git a/api/net/ip4/icmp4_common.hpp b/api/net/ip4/icmp4_common.hpp new file mode 100644 index 0000000000..fc4c6f540a --- /dev/null +++ b/api/net/ip4/icmp4_common.hpp @@ -0,0 +1,183 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2017 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#ifndef NET_IP4_ICMP4_COMMON_HPP +#define NET_IP4_ICMP4_COMMON_HPP + +namespace net { + + /** ICMP4 Codes so that UDP and IP4 can use these */ + namespace icmp4 { + + // ICMP types + enum class Type : uint8_t { + ECHO_REPLY, + DEST_UNREACHABLE = 3, + REDIRECT = 5, + ECHO = 8, + TIME_EXCEEDED = 11, + PARAMETER_PROBLEM = 12, + TIMESTAMP = 13, + TIMESTAMP_REPLY = 14, + NO_REPLY = 100 // Custom: Type in ICMP_packet if no ping reply received + }; + + namespace code { + + enum class Dest_unreachable : uint8_t { + NET, + HOST, + PROTOCOL, + PORT, + FRAGMENTATION, + SRC_ROUTE, + NET_UNKNOWN, // RFC 1122 + HOST_UNKNOWN, + SRC_HOST_ISOLATED, + NET_PROHIBITED, + HOST_PROHIBITED, + NET_FOR_TOS, + HOST_FOR_TOS + }; + + enum class Redirect : uint8_t { + NET, + HOST, + TOS_NET, + TOS_HOST + }; + + enum class Time_exceeded : uint8_t { + TTL, + FRAGMENT_REASSEMBLY + }; + + enum class Parameter_problem : uint8_t { + POINTER_INDICATES_ERROR, + REQUIRED_OPT_MISSING // RFC 1122 + }; + + } // < namespace code + + static std::string __attribute__((unused)) get_type_string(Type type) { + return [&type] () { + switch (type) { + case Type::ECHO: + return "ECHO (8)"; + case Type::ECHO_REPLY: + return "ECHO REPLY (0)"; + case Type::DEST_UNREACHABLE: + return "DESTINATION UNREACHABLE (3)"; + case Type::REDIRECT: + return "REDIRECT (5)"; + case Type::TIME_EXCEEDED: + return "TIME EXCEEDED (11)"; + case Type::PARAMETER_PROBLEM: + return "PARAMETER PROBLEM (12)"; + case Type::TIMESTAMP: + return "TIMESTAMP (13)"; + case Type::TIMESTAMP_REPLY: + return "TIMESTAMP REPLY (14)"; + case Type::NO_REPLY: + return "NO REPLY"; + }; + }(); + } + + static std::string __attribute__((unused)) get_code_string(Type type, uint8_t code) { + return [&type, &code] () { + switch (type) { + case Type::ECHO: // Have only code 0 + case Type::ECHO_REPLY: + case Type::TIMESTAMP: + case Type::TIMESTAMP_REPLY: + case Type::NO_REPLY: + return "DEFAULT (0)"; + case Type::DEST_UNREACHABLE: + switch ( (code::Dest_unreachable) code ) { + case code::Dest_unreachable::NET: + return "NET (0)"; + case code::Dest_unreachable::HOST: + return "HOST (1)"; + case code::Dest_unreachable::PROTOCOL: + return "PROTOCOL (2)"; + case code::Dest_unreachable::PORT: + return "PORT (3)"; + case code::Dest_unreachable::FRAGMENTATION: + return "FRAGMENTATION (4)"; + case code::Dest_unreachable::SRC_ROUTE: + return "SOURCE ROUTE (5)"; + case code::Dest_unreachable::NET_UNKNOWN: + return "NET UNKNOWN (6)"; + case code::Dest_unreachable::HOST_UNKNOWN: + return "HOST UNKNOWN (7)"; + case code::Dest_unreachable::SRC_HOST_ISOLATED: + return "SOURCE HOST ISOLATED (8)"; + case code::Dest_unreachable::NET_PROHIBITED: + return "NET PROHIBITED (9)"; + case code::Dest_unreachable::HOST_PROHIBITED: + return "HOST PROHIBITED (10)"; + case code::Dest_unreachable::NET_FOR_TOS: + return "NET FOR Type-of-Service (11)"; + case code::Dest_unreachable::HOST_FOR_TOS: + return "HOST FOR Type-of-Service (12)"; + default: + return "-"; + } + case Type::REDIRECT: + switch ( (code::Redirect) code ) { + case code::Redirect::NET: + return "NET (0)"; + case code::Redirect::HOST: + return "HOST (1)"; + case code::Redirect::TOS_NET: + return "Type-of-Service NET (2)"; + case code::Redirect::TOS_HOST: + return "Type-of-Service HOST (3)"; + default: + return "-"; + } + case Type::TIME_EXCEEDED: + switch ( (code::Time_exceeded) code ) { + case code::Time_exceeded::TTL: + return "TTL (0)"; + case code::Time_exceeded::FRAGMENT_REASSEMBLY: + return "FRAGMENT REASSEMBLY (1)"; + default: + return "-"; + } + case Type::PARAMETER_PROBLEM: + switch ( (code::Parameter_problem) code ) { + case code::Parameter_problem::POINTER_INDICATES_ERROR: + return "POINTER INDICATES ERROR (0)"; + case code::Parameter_problem::REQUIRED_OPT_MISSING: + return "REQUIRED OPTION MISSING (1)"; + default: + return "-"; + } + default: + return "-"; + }; + }(); + } + + } // < namespace icmp4 + +} // < namespace net + +#endif diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 5cd1129868..61d76b9c3a 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -22,19 +22,6 @@ namespace net { namespace icmp4 { - // ICMP types - enum class Type : uint8_t { - ECHO_REPLY, - DEST_UNREACHABLE = 3, - REDIRECT = 5, - ECHO = 8, - TIME_EXCEEDED = 11, - PARAMETER_PROBLEM = 12, - TIMESTAMP = 13, - TIMESTAMP_REPLY = 14, - NO_REPLY = 100 // Custom - }; - class Packet { struct Header { diff --git a/api/net/ip4/udp.hpp b/api/net/ip4/udp.hpp index d191a9f169..1ed3dcb6c2 100644 --- a/api/net/ip4/udp.hpp +++ b/api/net/ip4/udp.hpp @@ -36,8 +36,10 @@ namespace net { using addr_t = IP4::addr; using port_t = uint16_t; - using Packet_ptr = std::unique_ptr>; - using Stack = IP4::Stack; + using Packet_ptr = std::unique_ptr>; + using Stack = IP4::Stack; + using Error_type = Inet::Error_type; + using Error_code = Inet::Error_code; typedef delegate sendto_handler; @@ -95,6 +97,9 @@ namespace net { void set_network_out(downstream del) { network_layer_out_ = del; } + void error_report(Error_type type, Error_code code, + IP4::addr src_addr, port_t src_port, IP4::addr dest_addr, port_t dest_port); + /** Send UDP datagram from source ip/port to destination ip/port. @param sip Local IP-address @@ -137,7 +142,7 @@ namespace net { Port_in_use_exception(UDP::port_t p) : port_(p) {} virtual const char* what() const noexcept { - return "UDP port allready in use"; + return "UDP port already in use"; } UDP::port_t port(){ diff --git a/api/net/ip4/udp_socket.hpp b/api/net/ip4/udp_socket.hpp index d395338655..0ee207a310 100644 --- a/api/net/ip4/udp_socket.hpp +++ b/api/net/ip4/udp_socket.hpp @@ -26,11 +26,16 @@ namespace net class UDPSocket { public: + + using Error_type = UDP::Error_type; + using Error_code = UDP::Error_code; + typedef UDP::port_t port_t; typedef IP4::addr addr_t; typedef IP4::addr multicast_group_addr; typedef delegate recvfrom_handler; + typedef delegate recvfromicmp_handler; typedef UDP::sendto_handler sendto_handler; // constructors @@ -43,16 +48,19 @@ namespace net // functions void on_read(recvfrom_handler callback) - { - on_read_handler = callback; - } + { on_read_handler = callback; } + void sendto(addr_t destIP, port_t port, const void* buffer, size_t length, sendto_handler cb = [] {}); + void bcast(addr_t srcIP, port_t port, const void* buffer, size_t length, sendto_handler cb = [] {}); + void on_error(recvfromicmp_handler callback) + { on_error_handler = callback; } + void close() { udp_.close(l_port); } @@ -61,26 +69,33 @@ namespace net // stuff addr_t local_addr() const - { - return udp_.local_ip(); - } + { return udp_.local_ip(); } + port_t local_port() const - { - return l_port; - } + { return l_port; } - UDP& udp(){ - return udp_; - } + UDP& udp() + { return udp_; } private: void packet_init(UDP::Packet_ptr, addr_t, addr_t, port_t, uint16_t); void internal_read(UDP::Packet_ptr); + void error_read(Error_type error_type, Error_code error_code, + IP4::addr src_addr, port_t src_port, IP4::addr dest_addr, port_t dest_port); UDP& udp_; port_t l_port; recvfrom_handler on_read_handler = [] (addr_t, port_t, const char*, size_t) {}; + recvfromicmp_handler on_error_handler = + [](Error_type type, Error_code code, + addr_t src_addr, port_t src_port, addr_t dest_addr, port_t dest_port) { + + // Default behavior: Report to application layer about ICMP error messages + printf(" Error %s : %s occurred when sending data to %s port %u from %s port %u\n", + icmp4::get_type_string(type).c_str(), icmp4::get_code_string(type, code).c_str(), + dest_addr.to_string().c_str(), dest_port, src_addr.to_string().c_str(), src_port); + }; bool reuse_addr; bool loopback; // true means multicast data is looped back to sender diff --git a/api/net/tcp/tcp.hpp b/api/net/tcp/tcp.hpp index a4693314bd..bdb8cf2478 100644 --- a/api/net/tcp/tcp.hpp +++ b/api/net/tcp/tcp.hpp @@ -49,6 +49,9 @@ namespace net { using Listeners = std::map>; using Connections = std::map; + using Error_type = Inet::Error_type; + using Error_code = Inet::Error_code; + public: /////// TCP Stuff - Relevant to the protocol ///// @@ -352,6 +355,9 @@ namespace net { IPStack& stack() const { return inet_; } + void error_report(Error_type type, Error_code code, + tcp::Address src_addr, tcp::port_t src_port, tcp::Address dest_addr, tcp::port_t dest_port); + private: IPStack& inet_; Listeners listeners_; diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index 72a2992714..e8ed94d12c 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -26,27 +26,12 @@ namespace net { if (not is_reply()) return "No reply received"; - const std::string t = [&]() { - switch (type_) { - using namespace icmp4; - case Type::ECHO_REPLY: return "ECHO REPLY"; - case Type::DEST_UNREACHABLE: return "DESTINATION UNREACHABLE"; - case Type::REDIRECT: return "REDIRECT"; - case Type::ECHO: return "ECHO"; - case Type::TIME_EXCEEDED: return "TIME EXCEEDED"; - case Type::PARAMETER_PROBLEM: return "PARAMETER PROBLEM"; - case Type::TIMESTAMP: return "TIMESTAMP"; - case Type::TIMESTAMP_REPLY: return "TIMESTAMP REPLY"; - case Type::NO_REPLY: return "NO REPLY"; - } - }(); - return "Identifier: " + std::to_string(id_) + "\n" + "Sequence number: " + std::to_string(seq_) + "\n" + "Source: " + src_.to_string() + "\n" + "Destination: " + dst_.to_string() + "\n" + - "Type: " + t + "\n" + - "Code: " + std::to_string(code_) + "\n" + + "Type: " + icmp4::get_type_string(type_) + "\n" + + "Code: " + icmp4::get_code_string(type_, code_) + "\n" + "Checksum: " + std::to_string(checksum_) + "\n" + "Data: " + std::string{payload_.begin(), payload_.end()}; } @@ -65,36 +50,22 @@ namespace net { auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); auto req = icmp4::Packet(std::move(pckt_ip4)); - std::map::iterator it; switch(req.type()) { + case (icmp4::Type::ECHO): + debug(" PING from %s\n", req.ip().src().str().c_str()); + ping_reply(req); + break; case (icmp4::Type::ECHO_REPLY): debug(" PING Reply from %s\n", req.ip().src().str().c_str()); execute_ping_callback(req); break; case (icmp4::Type::DEST_UNREACHABLE): - debug(" DESTINATION UNREACHABLE from %s\n", req.ip().src().str().c_str()); - // TODO - // Send to transport layer - break; case (icmp4::Type::REDIRECT): - debug(" REDIRECT from %s\n", req.ip().src().str().c_str()); - // TODO - // Only sent by gateways. Incoming: Update routing information based on the message - break; - case (icmp4::Type::ECHO): - printf(" PING from %s\n", req.ip().src().str().c_str()); - ping_reply(req); - break; case (icmp4::Type::TIME_EXCEEDED): - debug(" TIME EXCEEDED from %s\n", req.ip().src().str().c_str()); - // TODO - // Send to transport layer - break; case (icmp4::Type::PARAMETER_PROBLEM): - debug(" PARAMETER PROBLEM from %s\n", req.ip().src().str().c_str()); - // TODO - // Send to transport layer + debug(" ICMP error message from %s\n", req.ip().src().str().c_str()); + forward_to_transport_layer(req); break; case (icmp4::Type::TIMESTAMP): debug(" TIMESTAMP from %s\n", req.ip().src().str().c_str()); @@ -105,11 +76,16 @@ namespace net { debug(" TIMESTAMP REPLY from %s\n", req.ip().src().str().c_str()); // TODO May break; - default: + default: // icmp4::NO_REPLY return; } } + void ICMPv4::forward_to_transport_layer(icmp4::Packet& req) { + // inet forwards to transport layer (UDP or TCP) + inet_.error_report(req.type(), req.code(), req.payload()); + } + void ICMPv4::destination_unreachable(Packet_ptr pckt, icmp4::code::Dest_unreachable code) { if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header return; @@ -151,6 +127,7 @@ namespace net { void ICMPv4::ping(IP4::addr ip) { send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48)); } + void ICMPv4::ping(IP4::addr ip, icmp_func callback) { send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48), callback); } diff --git a/src/net/ip4/udp.cpp b/src/net/ip4/udp.cpp index 8b756ba5d4..adbee77505 100644 --- a/src/net/ip4/udp.cpp +++ b/src/net/ip4/udp.cpp @@ -57,6 +57,24 @@ namespace net { stack_.icmp().destination_unreachable(std::move(ip4_packet), icmp4::code::Dest_unreachable::PORT); } + void UDP::error_report(Error_type type, Error_code code, + IP4::addr src_addr, port_t src_port, IP4::addr dest_addr, port_t dest_port) { + // Report to application layer that got an ICMP error message of type and code (reason and subreason) + // Should be possible to enable and disable this error report + + // Find UDPSocket + auto it = ports_.find(src_port); + if (LIKELY(it != ports_.end())) { + debug("<%s> UDP Error report: Found listener on port %u\n", + stack_.ifname().c_str(), src_port); + it->second.error_read(type, code, src_addr, src_port, dest_addr, dest_port); + return; + } + + debug("<%s> UDP Error report: Nobody listening on %u. Drop!\n", + stack_.ifname().c_str(), src_port); + } + UDPSocket& UDP::bind(UDP::port_t port) { debug("<%s> UDP bind to port %d\n", stack_.ifname().c_str(), port); diff --git a/src/net/ip4/udp_socket.cpp b/src/net/ip4/udp_socket.cpp index a1b38c3827..434548c3ff 100644 --- a/src/net/ip4/udp_socket.cpp +++ b/src/net/ip4/udp_socket.cpp @@ -45,7 +45,13 @@ namespace net on_read_handler(udp->src(), udp->src_port(), (const char*) udp->data(), udp->data_length()); } - void UDPSocket::sendto( + void UDPSocket::error_read(Error_type type, Error_code code, + IP4::addr src_addr, port_t src_port, IP4::addr dest_addr, port_t dest_port) + { + on_error_handler(type, code, src_addr, src_port, dest_addr, dest_port); + } + + void UDPSocket::sendto ( addr_t destIP, port_t port, const void* buffer, @@ -60,7 +66,7 @@ namespace net // UDP packets are meant to be sent immediately, so try flushing udp_.flush(); } - void UDPSocket::bcast( + void UDPSocket::bcast ( addr_t srcIP, port_t port, const void* buffer, @@ -75,5 +81,4 @@ namespace net // UDP packets are meant to be sent immediately, so try flushing udp_.flush(); } - } diff --git a/src/net/tcp/tcp.cpp b/src/net/tcp/tcp.cpp index e38ce8c1b0..ab50a8f430 100644 --- a/src/net/tcp/tcp.cpp +++ b/src/net/tcp/tcp.cpp @@ -196,6 +196,13 @@ string TCP::to_string() const { return ss.str(); } +void TCP::error_report(Error_type type, Error_code code, + tcp::Address src_addr, tcp::port_t src_port, tcp::Address dest_addr, tcp::port_t dest_port) { + printf(" Error %s : %s occurred when sending data to %s port %u from %s port %u\n", + icmp4::get_type_string(type).c_str(), icmp4::get_code_string(type, code).c_str(), + dest_addr.to_string().c_str(), dest_port, src_addr.to_string().c_str(), src_port); +} + void TCP::transmit(tcp::Packet_ptr packet) { // Generate checksum. packet->set_checksum(TCP::checksum(*packet)); From 82f84d780a8ad74c48beeb6f62bb2f706d142609 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 17 Mar 2017 14:19:09 +0100 Subject: [PATCH 32/40] Inet getter for dns added and DHCPD (server) now uses this. Services with dns set to 10.0.0.1 changed to 8.8.8.8 --- api/net/inet.hpp | 7 +++++-- api/net/inet4.hpp | 7 +++++-- examples/demo_service/service.cpp | 10 +++++----- examples/scoped_profiler/service.cpp | 8 ++++---- src/net/dhcp/dhcpd.cpp | 2 +- src/net/inet4.cpp | 4 ++-- test/net/integration/dhcpd/service.cpp | 2 +- test/net/integration/dhcpd_dhclient_linux/service.cpp | 2 +- test/net/integration/transmit/service.cpp | 2 +- 9 files changed, 25 insertions(+), 19 deletions(-) diff --git a/api/net/inet.hpp b/api/net/inet.hpp index 9048368ded..891ce3e462 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -55,14 +55,17 @@ namespace net { /// /** Get IP address of this interface **/ - virtual typename IPV::addr ip_addr() = 0; + virtual typename IPV::addr ip_addr() = 0; /** Get netmask of this interface **/ - virtual typename IPV::addr netmask() = 0; + virtual typename IPV::addr netmask() = 0; /** Get default gateway for this interface **/ virtual typename IPV::addr gateway() = 0; + /** Get default dns for this interface **/ + virtual typename IPV::addr dns() = 0; + /** Set default gateway for this interface */ virtual void set_gateway(typename IPV::addr server) = 0; diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index d2f3bf14fe..a5ea09691e 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -53,6 +53,9 @@ namespace net { IP4::addr gateway() override { return gateway_; } + IP4::addr dns() override + { return dns_server; } + IP4& ip_obj() override { return ip4_; } @@ -149,7 +152,7 @@ namespace net { void resolve(const std::string& hostname, resolve_func func) override { - dns.resolve(this->dns_server, hostname, func); + dns_.resolve(this->dns_server, hostname, func); } void set_gateway(IP4::addr gateway) override @@ -277,7 +280,7 @@ namespace net { TCP tcp_; // we need this to store the cache per-stack - DNSClient dns; + DNSClient dns_; std::shared_ptr dhcp_{}; diff --git a/examples/demo_service/service.cpp b/examples/demo_service/service.cpp index 03faa0e8c5..e9046e4d25 100644 --- a/examples/demo_service/service.cpp +++ b/examples/demo_service/service.cpp @@ -30,7 +30,7 @@ std::string HTML_RESPONSE() { const int color = rand(); - // Generate some HTML + // Generate some HTML std::stringstream stream; stream << "" << "cpu_id = SMP::cpu_id(); - INFO("Inet4", "Bringing up %s on CPU %d", + INFO("Inet4", "Bringing up %s on CPU %d", ifname().c_str(), this->get_cpu_id()); /** Upstream delegates */ diff --git a/test/net/integration/dhcpd/service.cpp b/test/net/integration/dhcpd/service.cpp index 4a1250a751..5c3c7a6612 100644 --- a/test/net/integration/dhcpd/service.cpp +++ b/test/net/integration/dhcpd/service.cpp @@ -33,7 +33,7 @@ void Service::start(const std::string&) { 10,0,100,1 }, // IP { 255,255,255,0 }, // Netmask { 10,0,0,1 }, // Gateway - { 10,0,0,1 }); // DNS + { 8,8,8,8 }); // DNS IP4::addr pool_start{10,0,100,10}; IP4::addr pool_end{10,0,100,20}; diff --git a/test/net/integration/dhcpd_dhclient_linux/service.cpp b/test/net/integration/dhcpd_dhclient_linux/service.cpp index 21866a3772..f5801588e1 100644 --- a/test/net/integration/dhcpd_dhclient_linux/service.cpp +++ b/test/net/integration/dhcpd_dhclient_linux/service.cpp @@ -33,7 +33,7 @@ void Service::start(const std::string&) { 10,200,0,1 }, // IP { 255,255,0,0 }, // Netmask { 10,0,0,1 }, // Gateway - { 10,0,0,1 }); // DNS + { 8,8,8,8 }); // DNS IP4::addr pool_start{10,200,100,20}; IP4::addr pool_end{10,200,100,30}; diff --git a/test/net/integration/transmit/service.cpp b/test/net/integration/transmit/service.cpp index 46b81fdc2f..b360a9258b 100644 --- a/test/net/integration/transmit/service.cpp +++ b/test/net/integration/transmit/service.cpp @@ -30,7 +30,7 @@ void Service::start(const std::string&) { 10,0,0,45 }, // IP { 255,255,255,0 }, // Netmask { 10,0,0,1 }, // Gateway - { 10,0,0,1 }); // DNS + { 8,8,8,8 }); // DNS printf("Service IP address is %s\n", inet.ip_addr().str().c_str()); From a74b134395a53cb1c48d2de281229c3c23c5daac Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 17 Mar 2017 14:45:48 +0100 Subject: [PATCH 33/40] Testing: Commented out tar unittests to check Jenkins' reaction - to be undone later --- test/util/unit/tar_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/util/unit/tar_test.cpp b/test/util/unit/tar_test.cpp index cab8be1c40..8e4b9eef85 100644 --- a/test/util/unit/tar_test.cpp +++ b/test/util/unit/tar_test.cpp @@ -21,7 +21,7 @@ #include #include #include - +/* CASE("Reading single entry tar file") { tar::Reader r; @@ -126,3 +126,4 @@ CASE("Reading tar.gz inside tar file") EXPECT(inner_e.name().find(".tar.gz") == std::string::npos); close(fd); } +*/ From 733162a14dacb8633fd6ace4c3814e0558df5c30 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Fri, 17 Mar 2017 15:06:03 +0100 Subject: [PATCH 34/40] ICMP integration test: Update test to reflect new to_string method in ICMP_packet and small cleanups --- api/net/ip4/udp_socket.hpp | 6 +++--- test/net/integration/dhcpd/test.py | 2 -- test/net/integration/icmp/test.py | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/api/net/ip4/udp_socket.hpp b/api/net/ip4/udp_socket.hpp index 0ee207a310..b87e63062b 100644 --- a/api/net/ip4/udp_socket.hpp +++ b/api/net/ip4/udp_socket.hpp @@ -88,11 +88,11 @@ namespace net recvfrom_handler on_read_handler = [] (addr_t, port_t, const char*, size_t) {}; recvfromicmp_handler on_error_handler = - [](Error_type type, Error_code code, - addr_t src_addr, port_t src_port, addr_t dest_addr, port_t dest_port) { + [](Error_type /*type*/, Error_code /*code*/, + addr_t /*src_addr*/, port_t /*src_port*/, addr_t /*dest_addr*/, port_t /*dest_port*/) { // Default behavior: Report to application layer about ICMP error messages - printf(" Error %s : %s occurred when sending data to %s port %u from %s port %u\n", + debug(" Error %s : %s occurred when sending data to %s port %u from %s port %u\n", icmp4::get_type_string(type).c_str(), icmp4::get_code_string(type, code).c_str(), dest_addr.to_string().c_str(), dest_port, src_addr.to_string().c_str(), src_port); }; diff --git a/test/net/integration/dhcpd/test.py b/test/net/integration/dhcpd/test.py index af3bf1dce4..f9c997a632 100755 --- a/test/net/integration/dhcpd/test.py +++ b/test/net/integration/dhcpd/test.py @@ -10,8 +10,6 @@ sys.path.insert(0,includeos_src) from vmrunner import vmrunner -import socket - from vmrunner.prettify import color # Get an auto-created VM from the vmrunner diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index 7b9cb13f70..bba01457e5 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -51,8 +51,8 @@ def start_icmp_test(trigger_line): "Sequence number: 0" in output_data and \ "Source: 10.0.0.1" in output_data and \ "Destination: 10.0.0.45" in output_data and \ - "Type: ECHO REPLY" in output_data and \ - "Code: 0" in output_data and \ + "Type: ECHO REPLY (0)" in output_data and \ + "Code: DEFAULT (0)" in output_data and \ "No reply received from 10.0.0.42. Identifier: 1. Sequence number: 0" in output_data and \ "No reply received from 10.0.0.43. Identifier: 2. Sequence number: 0" in output_data: num_successes += 1 From aea8c7fc871a622951e8ec29206d6d69ce6c057d Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Sun, 19 Mar 2017 22:30:18 +0100 Subject: [PATCH 35/40] ICMP_packet and integration test update: Added bool operator - no need for is_reply() --- api/net/ip4/icmp4.hpp | 19 +++++++++---------- src/net/ip4/icmp4.cpp | 2 +- test/net/integration/icmp/service.cpp | 12 ++++++------ test/net/integration/icmp/test.py | 4 ++-- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index b114fc6599..f61ce5f448 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -30,7 +30,6 @@ namespace net { struct ICMP_packet { using Span = gsl::span; - bool is_reply_{false}; uint16_t id_{0}; uint16_t seq_{0}; IP4::addr src_{0,0,0,0}; @@ -41,17 +40,14 @@ namespace net { Span payload_{nullptr, 0}; public: - ICMP_packet(uint16_t id, uint16_t seq) - : id_{id}, seq_{seq} - {} + ICMP_packet() {} - ICMP_packet(uint16_t id, uint16_t seq, IP4::addr src, IP4::addr dst, icmp4::Type type, uint8_t code, uint16_t checksum, const Span& payload) - : is_reply_{true}, id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, checksum_{checksum}, payload_{payload} + ICMP_packet(uint16_t id, uint16_t seq, IP4::addr src, IP4::addr dst, icmp4::Type type, uint8_t code, + uint16_t checksum, const Span& payload) + : id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, + checksum_{checksum}, payload_{payload} {} - bool is_reply() const noexcept - { return is_reply_; } - uint16_t id() const noexcept { return id_; } @@ -76,6 +72,9 @@ namespace net { Span payload() const noexcept { return payload_; } + operator bool() const noexcept + { return type_ != icmp4::Type::NO_REPLY; } + std::string to_string(); }; // < struct ICMP_packet @@ -184,7 +183,7 @@ namespace net { if (it != ping_callbacks_.end()) { // Data back to user if no response found - it->second.callback(ICMP_packet{key.first, key.second}); + it->second.callback(ICMP_packet{}); Timers::stop(it->second.timer_id); ping_callbacks_.erase(it); } diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index d2ee1d0048..f48cc404f5 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -23,7 +23,7 @@ namespace net { // ---------------------------- ICMP_packet ---------------------------- std::string ICMP_packet::to_string() { - if (not is_reply()) + if (type_ == icmp4::Type::NO_REPLY) return "No reply received"; return "Identifier: " + std::to_string(id_) + "\n" + diff --git a/test/net/integration/icmp/service.cpp b/test/net/integration/icmp/service.cpp index 7b4926f3b8..8701a1fc2e 100644 --- a/test/net/integration/icmp/service.cpp +++ b/test/net/integration/icmp/service.cpp @@ -30,10 +30,10 @@ void Service::start(const std::string&) // ping gateway inet.icmp().ping(inet.gateway(), [](ICMP_packet pckt) { - if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + if (pckt) printf("Received packet from gateway\n%s\n", pckt.to_string().c_str()); else - printf("No reply received from gateway. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + printf("No reply received from gateway\n"); }); /* If IP forwarding on: @@ -49,18 +49,18 @@ void Service::start(const std::string&) // No reply-pings // Waiting 30 seconds for reply inet.icmp().ping(IP4::addr{10,0,0,42}, [](ICMP_packet pckt) { - if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + if (pckt) printf("Received packet from 10.0.0.42\n%s\n", pckt.to_string().c_str()); else - printf("No reply received from 10.0.0.42. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + printf("No reply received from 10.0.0.42\n"); }); // Waiting 30 seconds for reply inet.icmp().ping(IP4::addr{10,0,0,43}, [](ICMP_packet pckt) { - if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + if (pckt) printf("Received packet from 10.0.0.43\n%s\n", pckt.to_string().c_str()); else - printf("No reply received from 10.0.0.43. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + printf("No reply received from 10.0.0.43\n"); }); // Also possible without callback: diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index bba01457e5..52055f09d2 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -53,8 +53,8 @@ def start_icmp_test(trigger_line): "Destination: 10.0.0.45" in output_data and \ "Type: ECHO REPLY (0)" in output_data and \ "Code: DEFAULT (0)" in output_data and \ - "No reply received from 10.0.0.42. Identifier: 1. Sequence number: 0" in output_data and \ - "No reply received from 10.0.0.43. Identifier: 2. Sequence number: 0" in output_data: + "No reply received from 10.0.0.42" in output_data and \ + "No reply received from 10.0.0.43" in output_data: num_successes += 1 print color.INFO(""), "Ping test succeeded" else: From 7cc24cb2e4a123d00ac3c1c808bece5549cfdf7f Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Mon, 20 Mar 2017 00:45:51 +0100 Subject: [PATCH 36/40] IP4 and UDP sending ICMP dest unreachable: Only when destination is not broadcast or multicast (RFC 1122) --- src/net/ip4/ip4.cpp | 13 ++++++++++--- src/net/ip4/udp.cpp | 9 +++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/net/ip4/ip4.cpp b/src/net/ip4/ip4.cpp index e1be93b28b..9f788016cf 100644 --- a/src/net/ip4/ip4.cpp +++ b/src/net/ip4/ip4.cpp @@ -65,7 +65,7 @@ namespace net { return; } - switch(packet->ip_protocol()){ + switch (packet->ip_protocol()) { case Protocol::ICMPv4: debug2("\t Type: ICMP\n"); icmp_handler_(std::move(packet)); @@ -80,8 +80,15 @@ namespace net { break; default: debug("\t Type: UNKNOWN %hhu\n", packet->ip_protocol()); - // Sending ICMP message of type Destination Unreachable and code PROTOCOL - stack_.icmp().destination_unreachable(std::move(packet), icmp4::code::Dest_unreachable::PROTOCOL); + + // Sending ICMP error message of type Destination Unreachable and code PROTOCOL + // But only if the destination IP address is not broadcast or multicast + if (packet->ip_dst() != IP4::ADDR_BCAST and (packet->ip_dst().part(3) <= 224 or + packet->ip_dst().part(3) >= 239)) + { + stack_.icmp().destination_unreachable(std::move(packet), icmp4::code::Dest_unreachable::PROTOCOL); + } + break; } } diff --git a/src/net/ip4/udp.cpp b/src/net/ip4/udp.cpp index 953bebf7ee..4e9b326e4d 100644 --- a/src/net/ip4/udp.cpp +++ b/src/net/ip4/udp.cpp @@ -52,9 +52,14 @@ namespace net { debug("<%s> UDP: nobody listening on %u. Drop!\n", stack_.ifname().c_str(), udp_packet->dst_port()); - // Sending ICMP message of type Destination Unreachable and code PORT + // Sending ICMP error message of type Destination Unreachable and code PORT + // But only if the destination IP address is not broadcast or multicast auto ip4_packet = static_unique_ptr_cast(std::move(udp_packet)); - stack_.icmp().destination_unreachable(std::move(ip4_packet), icmp4::code::Dest_unreachable::PORT); + if (ip4_packet->ip_dst() != IP4::ADDR_BCAST and (ip4_packet->ip_dst().part(3) <= 224 or + ip4_packet->ip_dst().part(3) >= 239)) + { + stack_.icmp().destination_unreachable(std::move(ip4_packet), icmp4::code::Dest_unreachable::PORT); + } } void UDP::error_report(Error_type type, Error_code code, From 72cfe98ed4fb43dd12dff796fd76e3ebdf5e79f9 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Mon, 20 Mar 2017 14:46:12 +0100 Subject: [PATCH 37/40] Rename back: icmp4 -> icmpv4 to later do git move on this change instead --- api/net/ip4/{icmp4.hpp => icmpv4.hpp} | 0 src/net/ip4/{icmp4.cpp => icmpv4.cpp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename api/net/ip4/{icmp4.hpp => icmpv4.hpp} (100%) rename src/net/ip4/{icmp4.cpp => icmpv4.cpp} (100%) diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmpv4.hpp similarity index 100% rename from api/net/ip4/icmp4.hpp rename to api/net/ip4/icmpv4.hpp diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmpv4.cpp similarity index 100% rename from src/net/ip4/icmp4.cpp rename to src/net/ip4/icmpv4.cpp From bfb7f7651b9541a215bab6259dd6338c631d333c Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Mon, 20 Mar 2017 14:49:53 +0100 Subject: [PATCH 38/40] Git mv icmpv4 -> icmp4 to keep history --- api/net/ip4/{icmpv4.hpp => icmp4.hpp} | 0 src/net/ip4/{icmpv4.cpp => icmp4.cpp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename api/net/ip4/{icmpv4.hpp => icmp4.hpp} (100%) rename src/net/ip4/{icmpv4.cpp => icmp4.cpp} (100%) diff --git a/api/net/ip4/icmpv4.hpp b/api/net/ip4/icmp4.hpp similarity index 100% rename from api/net/ip4/icmpv4.hpp rename to api/net/ip4/icmp4.hpp diff --git a/src/net/ip4/icmpv4.cpp b/src/net/ip4/icmp4.cpp similarity index 100% rename from src/net/ip4/icmpv4.cpp rename to src/net/ip4/icmp4.cpp From 69b0ec396b1cbb91aeb37ebbabe906d576c982fc Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Mon, 20 Mar 2017 16:43:45 +0100 Subject: [PATCH 39/40] Inet, Inet4, ICMP4, PacketICMP4: error_report taking Packet_ptr instead of Span (move away from raw pointers) --- api/net/inet.hpp | 3 +-- api/net/inet4.hpp | 17 +----------- api/net/ip4/icmp4.hpp | 14 +++++----- api/net/ip4/packet_icmp4.hpp | 3 +++ src/net/inet4.cpp | 14 ++++++++++ src/net/ip4/icmp4.cpp | 52 ++++++++++++++++++------------------ 6 files changed, 51 insertions(+), 52 deletions(-) diff --git a/api/net/inet.hpp b/api/net/inet.hpp index 891ce3e462..f55e5a2b66 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -43,7 +43,6 @@ namespace net { using Route_checker = delegate; using IP_packet_factory = delegate; - using Span = gsl::span; using Error_type = icmp4::Type; using Error_code = uint8_t; @@ -107,7 +106,7 @@ namespace net { * Error report in accordance with RFC 1122 * An ICMP error message has been received - forward to transport layer (UDP or TCP) */ - virtual void error_report(Error_type type, Error_code code, Span icmp_payload) = 0; + virtual void error_report(Error_type type, Error_code code, Packet_ptr orig_pckt) = 0; /// diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index a5ea09691e..84265445ab 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -84,22 +84,7 @@ namespace net { * Error report in accordance with RFC 1122 * An ICMP error message has been received - forward to transport layer (UDP or TCP) */ - void error_report(Error_type type, Error_code code, Span icmp_payload) override { - // Payload contains info about the sent packet that failed to be delivered - IP4::header* header = (IP4::header*) icmp_payload.data(); - - if ((Protocol) header->protocol == Protocol::UDP) { - UDP::header* udp_header = (UDP::header*) (icmp_payload.data() + sizeof(IP4::header)); - auto src_port = htons(udp_header->sport); - auto dest_port = htons(udp_header->dport); - udp_.error_report(type, code, header->saddr, src_port, header->daddr, dest_port); - } else if ((Protocol) header->protocol == Protocol::TCP) { - tcp::Header* tcp_header = (tcp::Header*) (icmp_payload.data() + sizeof(IP4::header)); - auto src_port = htons(tcp_header->source_port); - auto dest_port = htons(tcp_header->destination_port); - tcp_.error_report(type, code, header->saddr, src_port, header->daddr, dest_port); - } - } + void error_report(Error_type type, Error_code code, Packet_ptr orig_pckt) override; /** * Set the forwarding delegate used by this stack. diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index f61ce5f448..8a4bfb891c 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -189,20 +189,18 @@ namespace net { } } + void send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, + icmp_func callback = nullptr, uint16_t sequence = 0); + + /** Send response without id and sequence number */ + void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, uint8_t error = std::numeric_limits::max()); + /** * Responding to a ping (echo) request * Called from receive-method */ void ping_reply(icmp4::Packet&); - void send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, - icmp_func callback = nullptr, uint16_t sequence = 0); - - /** Send response without id and sequence number */ - void send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, uint8_t error = 255); - /** Send response with id and sequence number */ - void send_response_with_id(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload); - }; //< class ICMPv4 } //< namespace net diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 61d76b9c3a..496b673026 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -61,6 +61,9 @@ namespace icmp4 { uint16_t sequence() const noexcept { return header().sequence; } + int payload_index() + { return pckt_->data_end() - &(header().payload[0]); } + Span payload() { return {&(header().payload[0]), pckt_->data_end() - &(header().payload[0]) }; } diff --git a/src/net/inet4.cpp b/src/net/inet4.cpp index 6596742ad8..cd3367e45e 100644 --- a/src/net/inet4.cpp +++ b/src/net/inet4.cpp @@ -95,6 +95,20 @@ Inet4::Inet4(hw::Nic& nic) #endif } +void Inet4::error_report(Error_type type, Error_code code, Packet_ptr orig_pckt) { + auto pckt_ip4 = static_unique_ptr_cast(std::move(orig_pckt)); + + if (pckt_ip4->ip_protocol() == Protocol::UDP) { + auto pckt_udp = static_unique_ptr_cast(std::move(pckt_ip4)); + udp_.error_report(type, code, pckt_udp->ip_src(), pckt_udp->src_port(), + pckt_udp->ip_dst(), pckt_udp->dst_port()); + } else if (pckt_ip4->ip_protocol() == Protocol::TCP) { + auto pckt_tcp = static_unique_ptr_cast(std::move(pckt_ip4)); + tcp_.error_report(type, code, pckt_tcp->ip_src(), pckt_tcp->src_port(), + pckt_tcp->ip_dst(), pckt_tcp->dst_port()); + } +} + void Inet4::negotiate_dhcp(double timeout, dhcp_timeout_func handler) { INFO("Inet4", "Negotiating DHCP..."); if (!dhcp_) diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index f48cc404f5..4973b379de 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -82,8 +82,12 @@ namespace net { } void ICMPv4::forward_to_transport_layer(icmp4::Packet& req) { + int payload_idx = req.payload_index(); + auto packet_ptr = req.release(); + packet_ptr->increment_layer_begin(payload_idx); + // inet forwards to transport layer (UDP or TCP) - inet_.error_report(req.type(), req.code(), req.payload()); + inet_.error_report(req.type(), req.code(), std::move(packet_ptr)); } void ICMPv4::destination_unreachable(Packet_ptr pckt, icmp4::code::Dest_unreachable code) { @@ -91,11 +95,11 @@ namespace net { return; auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); auto pckt_icmp4 = icmp4::Packet(std::move(pckt_ip4)); - send_response(pckt_icmp4, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code, pckt_icmp4.header_and_data()); + send_response(pckt_icmp4, icmp4::Type::DEST_UNREACHABLE, (uint8_t) code); } void ICMPv4::redirect(icmp4::Packet& /* req */, icmp4::code::Redirect /* code */) { - // send_response(req, icmp4::Type::REDIRECT, (uint8_t) code, icmp4::Packet::Span(, )); + // send_response(req, icmp4::Type::REDIRECT, (uint8_t) code, ...); } void ICMPv4::time_exceeded(Packet_ptr pckt, icmp4::code::Time_exceeded code) { @@ -103,7 +107,7 @@ namespace net { return; auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); auto pckt_icmp4 = icmp4::Packet(std::move(pckt_ip4)); - send_response(pckt_icmp4, icmp4::Type::TIME_EXCEEDED, (uint8_t) code, pckt_icmp4.header_and_data()); + send_response(pckt_icmp4, icmp4::Type::TIME_EXCEEDED, (uint8_t) code); } void ICMPv4::parameter_problem(Packet_ptr pckt, uint8_t error) { @@ -111,32 +115,26 @@ namespace net { return; auto pckt_ip4 = static_unique_ptr_cast(std::move(pckt)); auto pckt_icmp4 = icmp4::Packet(std::move(pckt_ip4)); - send_response(pckt_icmp4, icmp4::Type::PARAMETER_PROBLEM, 0, pckt_icmp4.header_and_data(), error); + send_response(pckt_icmp4, icmp4::Type::PARAMETER_PROBLEM, 0, error); } void ICMPv4::timestamp_request(IP4::addr /* ip */) { // TODO - // send_request(ip, icmp4::Type::TIMESTAMP, 0, icmp4::Packet::Span(, )); + // send_request(ip, icmp4::Type::TIMESTAMP, 0, ...); } void ICMPv4::timestamp_reply(icmp4::Packet& /* req */) { // TODO - // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0, icmp4::Packet::Span(, )); + // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0, ...); } - void ICMPv4::ping(IP4::addr ip) { - send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48)); - } + void ICMPv4::ping(IP4::addr ip) + { send_request(ip, icmp4::Type::ECHO, 0); } - void ICMPv4::ping(IP4::addr ip, icmp_func callback) { - send_request(ip, icmp4::Type::ECHO, 0, icmp4::Packet::Span(includeos_payload_, 48), callback); - } + void ICMPv4::ping(IP4::addr ip, icmp_func callback) + { send_request(ip, icmp4::Type::ECHO, 0, callback); } - void ICMPv4::ping_reply(icmp4::Packet& req) { - send_response_with_id(req, icmp4::Type::ECHO_REPLY, 0, req.payload()); - } - - void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, + void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, icmp_func callback, uint16_t sequence) { // Provision new IP4-packet @@ -162,7 +160,8 @@ namespace net { debug(" Transmitting request to %s\n", dest_ip.to_string().c_str()); // Payload - req.set_payload(payload); + // Default: includeos_payload_ + req.set_payload(icmp4::Packet::Span(includeos_payload_, 48)); // Add checksum req.set_checksum(); @@ -173,7 +172,7 @@ namespace net { network_layer_out_(req.release()); } - void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload, uint8_t error) { + void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, uint8_t error) { // Provision new IP4-packet icmp4::Packet res(inet_.ip_packet_factory()); @@ -188,12 +187,13 @@ namespace net { debug(" Transmitting answer to %s\n", res.ip().dst().str().c_str()); // Payload - res.set_payload(payload); + // Default: Header and 64 bits (8 bytes) of original payload + res.set_payload(req.header_and_data()); // Add checksum res.set_checksum(); - if (error != 255) + if (error != std::numeric_limits::max()) res.set_pointer(error); debug(" Response size: %i payload size: %i, checksum: 0x%x\n", @@ -202,7 +202,7 @@ namespace net { network_layer_out_(res.release()); } - void ICMPv4::send_response_with_id(icmp4::Packet& req, icmp4::Type type, uint8_t code, icmp4::Packet::Span payload) { + void ICMPv4::ping_reply(icmp4::Packet& req) { // Provision new IP4-packet icmp4::Packet res(inet_.ip_packet_factory()); @@ -211,8 +211,8 @@ namespace net { res.ip().set_ip_dst(req.ip().ip_src()); // Populate response ICMP header - res.set_type(type); - res.set_code(code); + res.set_type(icmp4::Type::ECHO_REPLY); + res.set_code(0); // Incl. id and sequence number res.set_id(req.id()); res.set_sequence(req.sequence()); @@ -220,7 +220,7 @@ namespace net { debug(" Transmitting answer to %s\n", res.ip().dst().str().c_str()); // Payload - res.set_payload(payload); + res.set_payload(req.payload()); // Add checksum res.set_checksum(); From ea4ca7667ba677e14aa4784ceed30a1ba08f6f88 Mon Sep 17 00:00:00 2001 From: Annika Hammervoll Date: Tue, 21 Mar 2017 15:07:12 +0100 Subject: [PATCH 40/40] Ethernet: Merge fix (variable name miss) --- src/net/ethernet/ethernet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/ethernet/ethernet.cpp b/src/net/ethernet/ethernet.cpp index d8b1591bc7..b52d45fa5c 100644 --- a/src/net/ethernet/ethernet.cpp +++ b/src/net/ethernet/ethernet.cpp @@ -144,7 +144,7 @@ namespace net { } // This might be 802.3 LLC traffic - if (length_field > 1500) { + if (type > 1500) { debug2(" UNKNOWN ethertype 0x%hx\n", eth->type()); } else { debug2("IEEE802.3 Length field: 0x%hx\n", eth->type());