diff --git a/api/net/dns/client.hpp b/api/net/dns/client.hpp index f823aeb9bc..7e5efce0ea 100644 --- a/api/net/dns/client.hpp +++ b/api/net/dns/client.hpp @@ -77,7 +77,10 @@ namespace net } void finish() - { callback(request.getFirstIP4()); } + { + Error err; + callback(request.getFirstIP4(), err); + } void start_timeout(Timer::duration_t timeout) { timer.start(timeout); } diff --git a/api/net/http/client.hpp b/api/net/http/client.hpp index 73205532bb..585e4162af 100644 --- a/api/net/http/client.hpp +++ b/api/net/http/client.hpp @@ -33,7 +33,7 @@ namespace http { class Client { public: using TCP = net::TCP; - using Host = net::tcp::Socket; + using Host = net::Socket; using Response_handler = Client_connection::Response_handler; struct Options; @@ -67,7 +67,7 @@ namespace http { }; private: - using ResolveCallback = delegate; + using ResolveCallback = delegate; public: explicit Client(TCP& tcp, Request_handler on_send = nullptr); diff --git a/api/net/http/connection.hpp b/api/net/http/connection.hpp index 7020adf779..54e105a58e 100644 --- a/api/net/http/connection.hpp +++ b/api/net/http/connection.hpp @@ -31,7 +31,7 @@ namespace http { public: using Stream = net::tcp::Connection::Stream; using Stream_ptr = std::unique_ptr; - using Peer = net::tcp::Socket; + using Peer = net::Socket; using buffer_t = net::tcp::buffer_t; public: diff --git a/api/net/http/websocket.hpp b/api/net/http/websocket.hpp index 14c9574fba..61d517bd3f 100644 --- a/api/net/http/websocket.hpp +++ b/api/net/http/websocket.hpp @@ -30,7 +30,7 @@ class WebSocket { // When a handshake is established and the WebSocket is created using Connect_handler = delegate; // Whether to accept the client or not before handshake - using Accept_handler = delegate; + using Accept_handler = delegate; // data read (data, length) typedef delegate read_func; // closed (status code) diff --git a/api/net/http/ws_connector.hpp b/api/net/http/ws_connector.hpp index 91bc1f8259..627082200e 100644 --- a/api/net/http/ws_connector.hpp +++ b/api/net/http/ws_connector.hpp @@ -44,7 +44,7 @@ class WS_connector { */ class WS_server_connector : public WS_connector { public: - using AcceptCallback = delegate; + using AcceptCallback = delegate; using Request_handler = delegate; /** diff --git a/api/net/inet.hpp b/api/net/inet.hpp index 53fba2bb5e..9b6bdd9099 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -30,7 +30,7 @@ namespace net { class TCP; class UDP; class DHClient; - struct ICMPv4; + class ICMPv4; /** * An abstract IP-stack interface. @@ -44,11 +44,8 @@ namespace net { using Route_checker = delegate; using IP_packet_factory = delegate; - using Error_type = icmp4::Type; - using Error_code = uint8_t; - template - using resolve_func = delegate; + using resolve_func = delegate; using Vip_list = std::unordered_set; /// @@ -56,16 +53,19 @@ 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; + virtual typename IPV::addr gateway() = 0; /** Get default dns for this interface **/ - virtual typename IPV::addr dns() = 0; + virtual typename IPV::addr dns_addr() = 0; + + /** Get broadcast address for this interface **/ + virtual typename IPV::addr broadcast_addr() = 0; /** Set default gateway for this interface */ virtual void set_gateway(typename IPV::addr server) = 0; @@ -120,13 +120,15 @@ namespace net { virtual UDP& udp() = 0; /** Get the ICMP protocol object for this interface */ - virtual ICMPv4& icmp() = 0; + virtual ICMPv4& icmp() = 0; /** - * Error report in accordance with RFC 1122 + * Error reporting + * Incl. ICMP 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, Packet_ptr orig_pckt) = 0; + virtual void error_report(Error& err, Packet_ptr orig_pckt) = 0; + /// @@ -135,7 +137,7 @@ namespace net { /** DNS resolution */ virtual void resolve(const std::string& hostname, resolve_func func) = 0; - + virtual void resolve(const std::string& hostname, typename IPV::addr server, resolve_func func) = 0; /// /// LINK LAYER diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index 57d4828280..96def4b51a 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -58,8 +58,11 @@ namespace net { IP4::addr gateway() override { return gateway_; } - IP4::addr dns() override - { return dns_server; } + IP4::addr dns_addr() override + { return dns_server_; } + + IP4::addr broadcast_addr() override + { return ip4_addr_ | ( ~ netmask_); } IP4& ip_obj() override { return ip4_; } @@ -86,10 +89,11 @@ namespace net { auto dhclient() { return dhcp_; } /** - * Error report in accordance with RFC 1122 + * Error reporting + * Incl. ICMP 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, Packet_ptr orig_pckt) override; + void error_report(Error& err, Packet_ptr orig_pckt) override; /** * Set the forwarding delegate used by this stack. @@ -141,7 +145,14 @@ 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 resolve(const std::string& hostname, + IP4::addr server, + resolve_func func) override + { + dns_.resolve(server, hostname, func); } void set_gateway(IP4::addr gateway) override @@ -151,7 +162,7 @@ namespace net { void set_dns_server(IP4::addr server) override { - this->dns_server = server; + this->dns_server_ = server; } /** @@ -181,12 +192,12 @@ namespace net { this->ip4_addr_ = addr; this->netmask_ = nmask; this->gateway_ = gateway; - this->dns_server = (dns == IP4::ADDR_ANY) ? gateway : dns; + this->dns_server_ = (dns == IP4::ADDR_ANY) ? gateway : dns; INFO("Inet4", "Network configured"); INFO2("IP: \t\t%s", ip4_addr_.str().c_str()); INFO2("Netmask: \t%s", netmask_.str().c_str()); INFO2("Gateway: \t%s", gateway_.str().c_str()); - INFO2("DNS Server: \t%s", dns_server.str().c_str()); + INFO2("DNS Server: \t%s", dns_server_.str().c_str()); } virtual void @@ -298,7 +309,7 @@ namespace net { IP4::addr ip4_addr_; IP4::addr netmask_; IP4::addr gateway_; - IP4::addr dns_server; + IP4::addr dns_server_; Vip4_list vip4s_ = {{127,0,0,1}}; diff --git a/api/net/inet_common.hpp b/api/net/inet_common.hpp index 54998c5c5f..8eb63fac66 100644 --- a/api/net/inet_common.hpp +++ b/api/net/inet_common.hpp @@ -59,7 +59,6 @@ namespace net { return std::static_pointer_cast(packet); } - template auto static_unique_ptr_cast( std::unique_ptr&& p ) { @@ -67,37 +66,91 @@ namespace net { return std::unique_ptr(d); } + /** + * General Error class for the OS + * ICMP_error f.ex. inherits from this class + */ class Error { public: + enum class Type : uint8_t { no_error, general_IO, ifdown, ICMP // Add more as needed - }; - - virtual Type type() - { return t_; } - - virtual const char* what() - { return msg_; } - - virtual ~Error() = default; + }; Error() = default; Error(Type t, const char* msg) : t_{t}, msg_{msg} - {}; + {} - operator bool() + virtual ~Error() = default; + + Type type() + { return t_; } + + operator bool() const noexcept { return t_ != Type::no_error; } + bool is_icmp() const noexcept + { return t_ == Type::ICMP; } + + virtual const char* what() const noexcept + { return msg_; } + private: - Type t_ = Type::no_error; - const char* msg_ = "No error"; - }; + Type t_{Type::no_error}; + const char* msg_{"No error"}; + + }; // < class Error + + + /** + * An object of this error class is sent to UDP and TCP (via Inet) when an ICMP error message + * is received in ICMPv4::receive + */ + class ICMP_error : public Error { + + public: + using ICMP_type = icmp4::Type; + using ICMP_code = uint8_t; // Codes in icmp4_common.hpp in namespace icmp4::code + // icmp4::code::Dest_unreachable::PORT f.ex. + + ICMP_error() + : Error{} + {} + + ICMP_error(ICMP_type icmp_type, ICMP_code icmp_code) + : Error{Error::Type::ICMP, "ICMP error message received"}, + icmp_type_{icmp_type}, icmp_code_{icmp_code} + {} + + ICMP_type icmp_type() const noexcept + { return icmp_type_; } + + std::string icmp_type_str() const + { return icmp4::get_type_string(icmp_type_); } + + void set_icmp_type(ICMP_type icmp_type) + { icmp_type_ = icmp_type; } + + ICMP_code icmp_code() const noexcept + { return icmp_code_; } + + std::string icmp_code_str() const + { return icmp4::get_code_string(icmp_type_, icmp_code_); } + + void set_icmp_code(ICMP_code icmp_code) + { icmp_code_ = icmp_code; } + + private: + ICMP_type icmp_type_{ICMP_type::NO_ERROR}; + ICMP_code icmp_code_{0}; + + }; // < class ICMP_error /* RFC 6335 - IANA */ diff --git a/api/net/ip4/addr.hpp b/api/net/ip4/addr.hpp index 0363b3dc57..a6532f123b 100644 --- a/api/net/ip4/addr.hpp +++ b/api/net/ip4/addr.hpp @@ -323,15 +323,11 @@ struct Addr { { return part(3) == 127; } /** - * Determine if an address is a-priori illegal as source address in *all* cases. - * - * @note: The class E range 240/4 for "future use" is not illegal here - * @note: RFC-1122 prohibits 0.0.0.0 as source, but with exceptions + * @note: The class E range 240/4 for "future use" is not included here + * RFC-5771 defining multicast address range from 224.0.0.0 to 239.255.255.255 */ - bool is_illegal_src() const noexcept { - return (part(3) >= 224 and part(3) < 240) // Multicast - or (part(0) == 255); // Limited and directed broadcast - } + bool is_multicast() const noexcept + { return part(3) >= 224 and part(3) < 240; } /* Data member */ uint32_t whole; diff --git a/api/net/ip4/icmp4.hpp b/api/net/ip4/icmp4.hpp index 8a4bfb891c..d6771584c5 100644 --- a/api/net/ip4/icmp4.hpp +++ b/api/net/ip4/icmp4.hpp @@ -25,27 +25,25 @@ namespace net { /** - * User friendly ICMP packet used in callback (icmp_func) + * User friendly ICMP packet (view) used in ping callback (icmp_func) */ - struct ICMP_packet { - using Span = gsl::span; + class ICMP_view { - 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}; + using ICMP_type = ICMP_error::ICMP_type; + using ICMP_code = ICMP_error::ICMP_code; public: - 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) - : id_{id}, seq_{seq}, src_{src}, dst_{dst}, type_{type}, code_{code}, - checksum_{checksum}, payload_{payload} + ICMP_view() {} + + ICMP_view(icmp4::Packet& pckt) + : id_{pckt.id()}, + seq_{pckt.sequence()}, + src_{pckt.ip().ip_src()}, + dst_{pckt.ip().ip_dst()}, + type_{pckt.type()}, + code_{pckt.code()}, + checksum_{pckt.checksum()}, + payload_{(const char*) pckt.payload().data(), (size_t) pckt.payload().size()} {} uint16_t id() const noexcept @@ -60,30 +58,50 @@ namespace net { IP4::addr dst() const noexcept { return dst_; } - icmp4::Type type() const noexcept + ICMP_type type() const noexcept { return type_; } - uint8_t code() const noexcept + ICMP_code code() const noexcept { return code_; } uint16_t checksum() const noexcept { return checksum_; } - Span payload() const noexcept + std::string payload() const { return payload_; } operator bool() const noexcept - { return type_ != icmp4::Type::NO_REPLY; } + { return type_ != ICMP_type::NO_REPLY; } - std::string to_string(); - }; // < struct ICMP_packet + std::string to_string() const; + private: + uint16_t id_{0}; + uint16_t seq_{0}; + IP4::addr src_{0,0,0,0}; + IP4::addr dst_{0,0,0,0}; + ICMP_type type_{ICMP_type::NO_REPLY}; + uint8_t code_{0}; + uint16_t checksum_{0}; + std::string payload_{""}; + + }; // < class ICMP_view - struct ICMPv4 { + /** + * The main ICMPv4 class + */ + class ICMPv4 { + + using ICMP_type = ICMP_error::ICMP_type; + using ICMP_code = ICMP_error::ICMP_code; + + public: using Stack = IP4::Stack; using Tuple = std::pair; // identifier and sequence number - using icmp_func = delegate; + using icmp_func = delegate; + + static const int SEC_WAIT_FOR_REPLY = 40; // Initialize ICMPv4(Stack&); @@ -103,7 +121,7 @@ namespace net { /** * */ - void redirect(icmp4::Packet& req, icmp4::code::Redirect code); + void redirect(Packet_ptr pckt, icmp4::code::Redirect code); /** * Sending a Time Exceeded message from a host when fragment reassembly time exceeded (code 1) @@ -119,26 +137,32 @@ namespace net { * in the IP header * Code 1 means that a required option is missing */ - void parameter_problem(Packet_ptr pckt, uint8_t error); + void parameter_problem(Packet_ptr pckt, uint8_t error_pointer); // May void timestamp_request(IP4::addr ip); void timestamp_reply(icmp4::Packet& req); void ping(IP4::addr ip); - void ping(IP4::addr ip, icmp_func callback); + void ping(IP4::addr ip, icmp_func callback, int sec_wait = SEC_WAIT_FOR_REPLY); + + void ping(const std::string& hostname); + void ping(const std::string& hostname, icmp_func callback, int sec_wait = SEC_WAIT_FOR_REPLY); 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', - 'E','O','S',1,2,3,4,5, + '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}; + 'Y','Z','1','2','3','4','5','6', + '7','8'}; + + inline bool is_full_header(size_t pckt_size) + { return (pckt_size >= sizeof(IP4::header) + icmp4::Packet::header_size()); } struct ICMP_callback { using icmp_func = ICMPv4::icmp_func; @@ -148,10 +172,10 @@ namespace net { icmp_func callback; Timers::id_t timer_id; - ICMP_callback(ICMPv4& icmp, Tuple t, icmp_func cb) + ICMP_callback(ICMPv4& icmp, Tuple t, icmp_func cb, int sec_wait) : tuple{t}, callback{cb} { - timer_id = Timers::oneshot(std::chrono::seconds(40), [&icmp, t](Timers::id_t) { + timer_id = Timers::oneshot(std::chrono::seconds(sec_wait), [&icmp, t](Timers::id_t) { icmp.remove_ping_callback(t); }); } @@ -170,8 +194,7 @@ namespace net { 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().ip_src(), - ping_response.ip().ip_dst(), ping_response.type(), ping_response.code(), ping_response.checksum(), ping_response.payload()}); + it->second.callback(ICMP_view{ping_response}); Timers::stop(it->second.timer_id); ping_callbacks_.erase(it); } @@ -183,17 +206,18 @@ namespace net { if (it != ping_callbacks_.end()) { // Data back to user if no response found - it->second.callback(ICMP_packet{}); + it->second.callback(ICMP_view{}); Timers::stop(it->second.timer_id); ping_callbacks_.erase(it); } } - void send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, - icmp_func callback = nullptr, uint16_t sequence = 0); + void send_request(IP4::addr dest_ip, ICMP_type type, ICMP_code code, + icmp_func callback = nullptr, int sec_wait = SEC_WAIT_FOR_REPLY, 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()); + void send_response(icmp4::Packet& req, ICMP_type type, ICMP_code code, + uint8_t error_pointer = std::numeric_limits::max()); /** * Responding to a ping (echo) request diff --git a/api/net/ip4/icmp4_common.hpp b/api/net/ip4/icmp4_common.hpp index 187d19c02f..da46a4d6d2 100644 --- a/api/net/ip4/icmp4_common.hpp +++ b/api/net/ip4/icmp4_common.hpp @@ -34,7 +34,8 @@ namespace net { PARAMETER_PROBLEM = 12, TIMESTAMP = 13, TIMESTAMP_REPLY = 14, - NO_REPLY = 100 // Custom: Type in ICMP_packet if no ping reply received + NO_REPLY = 100, // Custom: Type in ICMP_packet if no ping reply received + NO_ERROR = 200 }; namespace code { @@ -75,107 +76,103 @@ namespace net { } // < 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"; - default: - return "UNKNOWN"; - }; - }(); + 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"; + default: + return "UNKNOWN"; + } } 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 "-"; - }; - }(); + 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 diff --git a/api/net/ip4/ip4.hpp b/api/net/ip4/ip4.hpp index 2549ad35e4..e6222b91f1 100644 --- a/api/net/ip4/ip4.hpp +++ b/api/net/ip4/ip4.hpp @@ -23,6 +23,8 @@ #include #include +#include + namespace net { class PacketIP4; @@ -38,7 +40,6 @@ namespace net { enum class Direction { Upstream, Downstream }; - using Stack = Inet; using addr = ip4::Addr; using header = ip4::Header; @@ -47,6 +48,7 @@ namespace net { using downstream_arp = delegate; using Packet_filter = delegate; using drop_handler = delegate; + using PMTU = uint16_t; /** Initialize. Sets a dummy linklayer out. */ explicit IP4(Stack&) noexcept; @@ -161,8 +163,10 @@ namespace net { /** Default downstream packet filter */ IP_packet_ptr filter_downstream(IP_packet_ptr packet); - - + /** + * Path MTU (and Packetization Layered Path MTU Discovery) related methods + */ + void update_path(IP4::addr dest, PMTU val); private: /** Stats */ @@ -172,6 +176,20 @@ namespace net { Stack& stack_; + /** + * Map of Path MTUs + * + * MTU (RFC4821, p. 7): Maximum Transmission Unit, the size in bytes of the largest IP packet, including the IP header and payload, + * that can be transmitted on a link or path + * + * Link MTU (RFC4821, p. 7): Maximum Transmission Unit, i.e., maximum IP packet size in bytes, that can be conveyed in one piece + * over a link + * + * Key: Destination address (chosen as the local representation of a path after reviewing RFC 1191, 1981 and 4821) + * Value: The Path MTU (the minimum link MTU of all the links in a path between a source node and a destination node) + */ + std::unordered_map paths_; + /** Downstream: Linklayer output delegate */ downstream_arp linklayer_out_ = nullptr; @@ -187,7 +205,6 @@ namespace net { Packet_filter upstream_filter_; Packet_filter downstream_filter_; - /** All dropped packets go here */ drop_handler drop_handler_; diff --git a/api/net/ip4/packet_icmp4.hpp b/api/net/ip4/packet_icmp4.hpp index 496b673026..719c83f899 100644 --- a/api/net/ip4/packet_icmp4.hpp +++ b/api/net/ip4/packet_icmp4.hpp @@ -61,8 +61,13 @@ namespace icmp4 { uint16_t sequence() const noexcept { return header().sequence; } + /** + * Where the payload of an ICMP packet starts, calculated from the start of the IP header + * The payload of an ICMP error message packet contains the original packet sent that caused an + * ICMP error to occur (the original IP header and 8 bytes of the original packet's data) + */ int payload_index() - { return pckt_->data_end() - &(header().payload[0]); } + { return pckt_->ip_header_length() + header_size(); } Span payload() { return {&(header().payload[0]), pckt_->data_end() - &(header().payload[0]) }; } diff --git a/api/net/ip4/udp.hpp b/api/net/ip4/udp.hpp index 2c2bdd8730..cf52f6fe5a 100644 --- a/api/net/ip4/udp.hpp +++ b/api/net/ip4/udp.hpp @@ -20,10 +20,15 @@ #include #include +#include +#include + #include "../inet.hpp" #include "ip4.hpp" -#include #include +#include +#include +#include namespace net { @@ -38,24 +43,22 @@ namespace net { 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; + typedef delegate error_handler; // write buffer for sendq struct WriteBuffer { WriteBuffer( - const uint8_t* data, size_t length, sendto_handler cb, + const uint8_t* data, size_t length, sendto_handler cb, error_handler ecb, UDP& udp, addr_t LA, port_t LP, addr_t DA, port_t DP); - int remaining() const { - return len - offset; - } - bool done() const { - return offset == len; - } + int remaining() const + { return len - offset; } + + bool done() const + { return offset == len; } size_t packets_needed() const; void write(); @@ -65,7 +68,9 @@ namespace net { size_t len; size_t offset; // the callback for when this buffer is written - sendto_handler callback; + sendto_handler send_callback; + // the callback for when this receives an error + error_handler error_callback; // the UDP stack UDP& udp; @@ -75,7 +80,7 @@ namespace net { // destination address and port port_t d_port; addr_t d_addr; - }; + }; // < struct WriteBuffer /** UDP header */ struct header { @@ -97,8 +102,11 @@ 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); + /** + * Is called when an Error has occurred in the OS + * F.ex.: An ICMP error message has been received in response to a sent UDP datagram + */ + void error_report(Error& err, Socket dest); /** Send UDP datagram from source ip/port to destination ip/port. @@ -123,13 +131,14 @@ namespace net { UDP(Stack& inet); Stack& stack() - { - return stack_; - } + { return stack_; } // send as much as possible from sendq void flush(); + /** Flush expired error entries (in error_callbacks_) when flush_timer_ has timed out */ + void flush_expired(); + // create and transmit @num packets from sendq void process_sendq(size_t num); @@ -141,27 +150,52 @@ namespace net { public: Port_in_use_exception(UDP::port_t p) : port_(p) {} - virtual const char* what() const noexcept { - return "UDP port already in use"; - } - UDP::port_t port(){ - return port_; - } + virtual const char* what() const noexcept + { return "UDP port already in use"; } + + UDP::port_t port() + { return port_; } private: UDP::port_t port_; }; private: + static constexpr uint16_t exp_t_ {60 * 5}; - downstream network_layer_out_; - Stack& stack_; + std::chrono::minutes flush_interval_{5}; + downstream network_layer_out_; + Stack& stack_; std::map ports_; - port_t current_port_; + port_t current_port_; // the async send queue std::deque sendq; + + /** Error entries are just error callbacks and timestamps */ + class Error_entry { + public: + Error_entry(UDP::error_handler cb) noexcept + : callback(std::move(cb)), timestamp(RTC::time_since_boot()) + {} + + bool expired() noexcept + { return timestamp + exp_t_ < RTC::time_since_boot(); } + + UDP::error_handler callback; + + private: + RTC::timestamp_t timestamp; + + }; //< class Error_entry + + /** The error callbacks that the user has sent in via the UDPSockets' sendto and bcast methods */ + std::unordered_map error_callbacks_; + + /** Timer that flushes expired error entries/callbacks (no errors have occurred) */ + Timer flush_timer_{{ *this, &UDP::flush_expired }}; + friend class net::UDPSocket; }; //< class UDP diff --git a/api/net/ip4/udp_socket.hpp b/api/net/ip4/udp_socket.hpp index b87e63062b..57febe3e19 100644 --- a/api/net/ip4/udp_socket.hpp +++ b/api/net/ip4/udp_socket.hpp @@ -27,16 +27,13 @@ namespace net { 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; + typedef UDP::error_handler error_handler; // constructors UDPSocket(UDP&, port_t port); @@ -52,14 +49,13 @@ namespace net void sendto(addr_t destIP, port_t port, const void* buffer, size_t length, - sendto_handler cb = [] {}); + sendto_handler cb = nullptr, + error_handler ecb = nullptr); 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; } + sendto_handler cb = nullptr, + error_handler ecb = nullptr); void close() { udp_.close(l_port); } @@ -80,22 +76,11 @@ namespace net 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 - 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); - }; bool reuse_addr; bool loopback; // true means multicast data is looped back to sender diff --git a/api/net/tcp/socket.hpp b/api/net/socket.hpp similarity index 78% rename from api/net/tcp/socket.hpp rename to api/net/socket.hpp index 89f7c3fbe5..4964e2a34c 100644 --- a/api/net/tcp/socket.hpp +++ b/api/net/socket.hpp @@ -16,27 +16,37 @@ // limitations under the License. #pragma once -#ifndef NET_TCP_SOCKET_HPP -#define NET_TCP_SOCKET_HPP +#ifndef NET_SOCKET_HPP +#define NET_SOCKET_HPP -#include "common.hpp" +#include namespace net { -namespace tcp { /** * An IP address and port */ class Socket { public: + + using Address = ip4::Addr; + using port_t = uint16_t; + + struct pair_hash { + std::size_t operator () (const Socket& s) const { + auto h1 = std::hash
{}(s.address()); + auto h2 = std::hash{}(s.port()); + return h1 ^ h2; + } + }; + /** * Constructor * * Intialize an empty socket <0.0.0.0:0> */ constexpr Socket() noexcept - : address_{} - , port_{} + : address_{}, port_{} {} /** @@ -51,8 +61,7 @@ class Socket { * The port associated with the process */ constexpr Socket(const Address address, const port_t port) noexcept - : address_{address} - , port_{port} + : address_{address}, port_{port} {} /** @@ -138,12 +147,37 @@ class Socket { */ constexpr bool operator>(const Socket& other) const noexcept { return not (*this < other); } + private: Address address_; port_t port_; + }; //< class Socket -} //< namespace tcp +class Quadruple { + +public: + Quadruple() noexcept + : source_{}, destination_{} + {} + + Quadruple(const Socket::Address src_address, const Socket::port_t src_port, + const Socket::Address dst_address, const Socket::port_t dst_port) noexcept + : source_{src_address, src_port}, destination_{dst_address, dst_port} + {} + + const Socket& source() const noexcept + { return source_; } + + const Socket& destination() const noexcept + { return destination_; } + +private: + Socket source_; + Socket destination_; + +}; //< class Quadruple + } //< namespace net -#endif //< NET_TCP_SOCKET_HPP +#endif //< NET_SOCKET_HPP diff --git a/api/net/stream.hpp b/api/net/stream.hpp index 4afa3b9b57..da3e6e1f92 100644 --- a/api/net/stream.hpp +++ b/api/net/stream.hpp @@ -23,7 +23,7 @@ #include #include #include -#include "tcp/socket.hpp" +#include namespace net { class Stream; @@ -123,14 +123,14 @@ namespace net { * * @return A TCP Socket */ - virtual tcp::Socket local() const = 0; + virtual Socket local() const = 0; /** * @brief Returns the streams remote socket. * * @return A TCP Socket */ - virtual tcp::Socket remote() const = 0; + virtual Socket remote() const = 0; /** * @brief Returns the local port. diff --git a/api/net/tcp/connection.hpp b/api/net/tcp/connection.hpp index ceea39fb61..a86c51f36f 100644 --- a/api/net/tcp/connection.hpp +++ b/api/net/tcp/connection.hpp @@ -23,10 +23,10 @@ #include "packet.hpp" #include "read_request.hpp" #include "rttm.hpp" -#include "socket.hpp" #include "tcp_errors.hpp" #include "write_queue.hpp" +#include #include #include #include @@ -321,7 +321,7 @@ class Connection : public std::enable_shared_from_this { * * @return A TCP Socket */ - tcp::Socket local() const override + Socket local() const override { return tcp->local(); } /** @@ -329,7 +329,7 @@ class Connection : public std::enable_shared_from_this { * * @return A TCP Socket */ - tcp::Socket remote() const override + Socket remote() const override { return tcp->remote(); } /** diff --git a/api/net/tcp/listener.hpp b/api/net/tcp/listener.hpp index b9fe3af4df..7cf4a6693e 100644 --- a/api/net/tcp/listener.hpp +++ b/api/net/tcp/listener.hpp @@ -24,7 +24,8 @@ #include "common.hpp" #include "connection.hpp" #include "packet.hpp" -#include "socket.hpp" + +#include namespace net { class TCP; diff --git a/api/net/tcp/packet.hpp b/api/net/tcp/packet.hpp index 05e81ff50d..22009e1522 100644 --- a/api/net/tcp/packet.hpp +++ b/api/net/tcp/packet.hpp @@ -21,10 +21,10 @@ #include // PacketIP4 #include // byte ordering helpers +#include #include "common.hpp" // constants, seq_t #include "headers.hpp" -#include "socket.hpp" #include // ostringstream diff --git a/api/net/tcp/tcp.hpp b/api/net/tcp/tcp.hpp index 64825db690..13e1cdbb6c 100644 --- a/api/net/tcp/tcp.hpp +++ b/api/net/tcp/tcp.hpp @@ -23,12 +23,12 @@ #include "connection.hpp" #include "headers.hpp" #include "listener.hpp" -#include "socket.hpp" #include "packet.hpp" #include // connections, listeners #include // writeq #include +#include #include namespace net { @@ -50,7 +50,7 @@ namespace net { friend class tcp::Listener; private: - using Listeners = std::map>; + using Listeners = std::map>; using Connections = std::map; /** @@ -137,9 +137,6 @@ namespace net { }; // < class Port_util using Port_lists = std::map; - using Error_type = Inet::Error_type; - using Error_code = Inet::Error_code; - public: /////// TCP Stuff - Relevant to the protocol ///// @@ -173,7 +170,7 @@ namespace net { * * @return A TCP Listener */ - tcp::Listener& listen(tcp::Socket socket, ConnectCallback cb = nullptr); + tcp::Listener& listen(Socket socket, ConnectCallback cb = nullptr); /** * @brief Close a Listener @@ -183,7 +180,7 @@ namespace net { * @param socket listening socket * @return whether the listener existed and was closed */ - bool close(tcp::Socket socket); + bool close(Socket socket); /** * @brief Make an outgoing connection to a TCP remote (IP:port). @@ -192,7 +189,7 @@ namespace net { * @param[in] remote The remote * @param[in] cb Connect callback to be invoked when the connection is established. */ - void connect(tcp::Socket remote, ConnectCallback cb); + void connect(Socket remote, ConnectCallback cb); /** * @brief Make an outgoing connection from a given source address. @@ -203,7 +200,7 @@ namespace net { * @param[in] remote The remote socket * @param[in] callback The connect callback */ - void connect(tcp::Address source, tcp::Socket remote, ConnectCallback callback); + void connect(tcp::Address source, Socket remote, ConnectCallback callback); /** * @brief Make an outgoing connection to from a given source socket. @@ -213,7 +210,7 @@ namespace net { * @param[in] remote The remote socket * @param[in] callback The connect callback */ - void connect(tcp::Socket local, tcp::Socket remote, ConnectCallback callback); + void connect(Socket local, Socket remote, ConnectCallback callback); /** * @brief Make an outgoing connecction to a TCP remote (IP:port). @@ -223,7 +220,7 @@ namespace net { * * @return A ptr to an unestablished TCP Connection */ - tcp::Connection_ptr connect(tcp::Socket remote); + tcp::Connection_ptr connect(Socket remote); /** * @brief Make an outgoing connection from a given source address. @@ -235,7 +232,7 @@ namespace net { * * @return A ptr to an unestablished TCP Connection */ - tcp::Connection_ptr connect(tcp::Address source, tcp::Socket remote); + tcp::Connection_ptr connect(tcp::Address source, Socket remote); /** * @brief Make an outgoing connection to from a given source socket. @@ -246,7 +243,7 @@ namespace net { * * @return A ptr to an unestablished TCP Connection */ - tcp::Connection_ptr connect(tcp::Socket local, tcp::Socket remote); + tcp::Connection_ptr connect(Socket local, Socket remote); /** * @brief Insert a connection ptr into the TCP (used for restoring) @@ -463,7 +460,7 @@ namespace net { * * @return True if bound, False otherwise. */ - bool is_bound(const tcp::Socket socket) const; + bool is_bound(const Socket socket) const; /** * @brief Number of connections queued for writing. @@ -489,8 +486,11 @@ 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); + /** + * Is called when an Error has occurred in the OS + * F.ex.: Is called when an ICMP error message has been received in response to a sent TCP packet + */ + void error_report(Error& err, Socket dest); private: IPStack& inet_; @@ -591,7 +591,7 @@ namespace net { * * @param[in] socket The socket */ - void bind(const tcp::Socket socket); + void bind(const Socket socket); /** * @brief Unbinds a socket, making it free for future use @@ -600,7 +600,7 @@ namespace net { * * @return Returns wether there was a socket that got unbound */ - bool unbind(const tcp::Socket socket); + bool unbind(const Socket socket); /** * @brief Bind to an socket where the address is given and the @@ -611,7 +611,7 @@ namespace net { * * @return The socket that got bound. */ - tcp::Socket bind(const tcp::Address addr); + Socket bind(const tcp::Address addr); /** * @brief Binds to an socket where the address is given by the @@ -619,7 +619,7 @@ namespace net { * * @return The socket that got bound. */ - tcp::Socket bind() + Socket bind() { return bind(address()); } /** @@ -640,7 +640,7 @@ namespace net { * * @return A listener iterator */ - Listeners::iterator find_listener(const tcp::Socket socket) + Listeners::iterator find_listener(const Socket socket) { Listeners::iterator it = listeners_.find(socket); if(it == listeners_.end() and socket.address() != 0) @@ -656,7 +656,7 @@ namespace net { * * @return A listener const iterator */ - Listeners::const_iterator cfind_listener(const tcp::Socket socket) const + Listeners::const_iterator cfind_listener(const Socket socket) const { Listeners::const_iterator it = listeners_.find(socket); if(it == listeners_.cend() and socket.address() != 0) @@ -680,8 +680,8 @@ namespace net { * * @return A ptr to the Connection whos created */ - tcp::Connection_ptr create_connection(tcp::Socket local, - tcp::Socket remote, + tcp::Connection_ptr create_connection(Socket local, + Socket remote, ConnectCallback cb = nullptr); /** diff --git a/api/util/statman.hpp b/api/util/statman.hpp index 99ad974ff7..f13f0cb9d8 100644 --- a/api/util/statman.hpp +++ b/api/util/statman.hpp @@ -197,21 +197,25 @@ class Statman { bool full() const noexcept { return next_available_ == stats_.size(); } - /** - * Returns an iterator to the last used (or filled in) element - * in the span stats_ - */ - Span_iterator last_used(); - /// /// /// auto begin() noexcept { return stats_.begin(); } - /// - /// - /// + /** + * Returns an iterator to the last element (Stat) + * in the span stats_ + */ + Span_iterator last_used() { + Expects(next_available_ <= stats_.size()); + return Span_iterator(&stats_, next_available_); + } + + /** + * Returns an iterator to the end of the span stats_ regardless + * of the span begin full or not + */ auto end() noexcept { return stats_.end(); } @@ -221,11 +225,21 @@ class Statman { auto cbegin() const noexcept { return stats_.cbegin(); } - /// - /// - /// + /** + * Returns a const iterator to the last element (Stat) + * in the span stats_ + */ + Span_citerator clast_used() { + Expects(next_available_ <= stats_.size()); + return Span_citerator(&stats_, next_available_); + } + + /** + * Returns a const iterator to the end of the span stats_ regardless + * of the span begin full or not + */ auto cend() const noexcept - { return Span_citerator(&stats_, next_available_); } + { return stats_.cend(); } /// /// diff --git a/examples/tcp/service.cpp b/examples/tcp/service.cpp index 591e5cf13a..47e9955370 100644 --- a/examples/tcp/service.cpp +++ b/examples/tcp/service.cpp @@ -36,7 +36,7 @@ using Disconnect = net::tcp::Connection::Disconnect; // Address to our python server: 10.0.2.2:1337 // @note: This may have to be modified depending on network and server settings. -net::tcp::Socket python_server{ {10,0,2,2} , 1337}; +net::Socket python_server{ {10,0,2,2} , 1337}; // Called when data is received on client (incoming connection) void handle_client_on_read(Connection_ptr python, const std::string& request) { diff --git a/src/net/dhcp/dhcpd.cpp b/src/net/dhcp/dhcpd.cpp index 496c29df78..5267996793 100644 --- a/src/net/dhcp/dhcpd.cpp +++ b/src/net/dhcp/dhcpd.cpp @@ -30,7 +30,7 @@ DHCPD::DHCPD(UDP& udp, IP4::addr pool_start, IP4::addr pool_end, server_id_{stack_.ip_addr()}, netmask_{stack_.netmask()}, router_{stack_.gateway()}, - dns_{stack_.dns()}, + dns_{stack_.dns_addr()}, lease_{lease}, max_lease_{max_lease}, pending_{pending} { if (not valid_pool(pool_start, pool_end)) diff --git a/src/net/dns/client.cpp b/src/net/dns/client.cpp index 7959d5a8bf..07b32687a1 100644 --- a/src/net/dns/client.cpp +++ b/src/net/dns/client.cpp @@ -28,13 +28,27 @@ namespace net std::array buf{}; size_t len = request.create(buf.data(), hostname); + auto key = request.get_id(); + // store the request for later match requests_.emplace(std::piecewise_construct, - std::forward_as_tuple(request.get_id()), + std::forward_as_tuple(key), std::forward_as_tuple(std::move(request), std::move(func))); // send request to DNS server - socket_.sendto(dns_server, DNS::DNS_SERVICE_PORT, buf.data(), len); + socket_.sendto(dns_server, DNS::DNS_SERVICE_PORT, buf.data(), len, nullptr, [this, dns_server, key] (Error& err) { + // If an error is not received, this will never execute (Error is just erased from the map + // without calling the callback) + + INFO("DNS", "Couldn't resolve DNS server at %s. Reason: %s", dns_server.to_string().c_str(), err.what()); + + // Find the request and remove it since an error occurred + auto it = requests_.find(key); + if (it != requests_.end()) { + it->second.callback(IP4::ADDR_ANY, err); + requests_.erase(it); + } + }); } void DNSClient::receive_response(IP4::addr, UDP::port_t, const char* data, size_t) diff --git a/src/net/http/client.cpp b/src/net/http/client.cpp index 21239af8bc..aeb5a64286 100644 --- a/src/net/http/client.cpp +++ b/src/net/http/client.cpp @@ -97,7 +97,7 @@ namespace http { cb{move(cb)}, opt{move(options)} ] - (net::ip4::Addr ip) + (net::ip4::Addr ip, net::Error&) { // Host resolved if (ip != 0) @@ -149,7 +149,7 @@ namespace http { data{move(data)}, cb{move(cb)}, opt{move(options)} - ] (auto ip) + ] (auto ip, net::Error&) { // Host resolved if(ip != 0) diff --git a/src/net/inet4.cpp b/src/net/inet4.cpp index cd3367e45e..52542363a4 100644 --- a/src/net/inet4.cpp +++ b/src/net/inet4.cpp @@ -18,6 +18,7 @@ #include #include #include +#include using namespace net; @@ -25,7 +26,7 @@ Inet4::Inet4(hw::Nic& nic) : ip4_addr_(IP4::ADDR_ANY), netmask_(IP4::ADDR_ANY), gateway_(IP4::ADDR_ANY), - dns_server(IP4::ADDR_ANY), + dns_server_(IP4::ADDR_ANY), nic_(nic), arp_(*this), ip4_(*this), icmp_(*this), udp_(*this), tcp_(*this), dns_(*this), MTU_(nic.MTU()) @@ -95,17 +96,15 @@ Inet4::Inet4(hw::Nic& nic) #endif } -void Inet4::error_report(Error_type type, Error_code code, Packet_ptr orig_pckt) { +void Inet4::error_report(Error& err, 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()); + udp_.error_report(err, Socket{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()); + tcp_.error_report(err, Socket{pckt_tcp->ip_dst(), pckt_tcp->dst_port()}); } } diff --git a/src/net/ip4/icmp4.cpp b/src/net/ip4/icmp4.cpp index 4973b379de..32ff878911 100644 --- a/src/net/ip4/icmp4.cpp +++ b/src/net/ip4/icmp4.cpp @@ -20,10 +20,10 @@ namespace net { - // ---------------------------- ICMP_packet ---------------------------- + // ---------------------------- ICMP_view ---------------------------- - std::string ICMP_packet::to_string() { - if (type_ == icmp4::Type::NO_REPLY) + std::string ICMP_view::to_string() const { + if (type_ == ICMP_type::NO_REPLY) return "No reply received"; return "Identifier: " + std::to_string(id_) + "\n" + @@ -33,7 +33,7 @@ namespace net { "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()}; + "Data: " + payload_; } // ------------------------------ ICMPv4 ------------------------------ @@ -45,97 +45,118 @@ namespace net { {} void ICMPv4::receive(Packet_ptr pckt) { - if ((size_t)pckt->size() < sizeof(IP4::header) + icmp4::Packet::header_size()) // Drop if not a full header + if (not is_full_header((size_t) pckt->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()); + case (ICMP_type::ECHO): + debug(" PING from %s\n", req.ip().src().ip_str().c_str()); ping_reply(req); break; - case (icmp4::Type::ECHO_REPLY): - debug(" PING Reply from %s\n", req.ip().src().str().c_str()); + case (ICMP_type::ECHO_REPLY): + debug(" PING Reply from %s\n", req.ip().ip_src().str().c_str()); execute_ping_callback(req); break; - case (icmp4::Type::DEST_UNREACHABLE): - case (icmp4::Type::REDIRECT): - case (icmp4::Type::TIME_EXCEEDED): - case (icmp4::Type::PARAMETER_PROBLEM): - debug(" ICMP error message from %s\n", req.ip().src().str().c_str()); + case (ICMP_type::DEST_UNREACHABLE): + case (ICMP_type::REDIRECT): + case (ICMP_type::TIME_EXCEEDED): + case (ICMP_type::PARAMETER_PROBLEM): + debug(" ICMP error message from %s\n", req.ip().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()); + case (ICMP_type::TIMESTAMP): + debug(" TIMESTAMP from %s\n", req.ip().ip_src().str().c_str()); // TODO May // timestamp_reply(req); break; - case (icmp4::Type::TIMESTAMP_REPLY): - debug(" TIMESTAMP REPLY from %s\n", req.ip().src().str().c_str()); + case (ICMP_type::TIMESTAMP_REPLY): + debug(" TIMESTAMP REPLY from %s\n", req.ip().ip_src().str().c_str()); // TODO May break; - default: // icmp4::NO_REPLY + default: // ICMP_type::NO_REPLY return; } } void ICMPv4::forward_to_transport_layer(icmp4::Packet& req) { + ICMP_error err{req.type(), req.code()}; + + // The icmp4::Packet's payload contains the original packet sent that resulted + // in an error 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(), std::move(packet_ptr)); + inet_.error_report(err, std::move(packet_ptr)); } 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 + if (not is_full_header((size_t) pckt->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::DEST_UNREACHABLE, (uint8_t) code); + send_response(pckt_icmp4, ICMP_type::DEST_UNREACHABLE, (ICMP_code) code); } - void ICMPv4::redirect(icmp4::Packet& /* req */, icmp4::code::Redirect /* code */) { - // send_response(req, icmp4::Type::REDIRECT, (uint8_t) code, ...); + void ICMPv4::redirect(Packet_ptr /* pckt */, icmp4::code::Redirect /* code */) { + // send_response(req, ICMP_type::REDIRECT, (ICMP_code) code, ...); } 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 + if (not is_full_header((size_t) pckt->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); + send_response(pckt_icmp4, ICMP_type::TIME_EXCEEDED, (ICMP_code) code); } - 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 + void ICMPv4::parameter_problem(Packet_ptr pckt, uint8_t error_pointer) { + if (not is_full_header((size_t) pckt->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, error); + send_response(pckt_icmp4, ICMP_type::PARAMETER_PROBLEM, 0, error_pointer); } void ICMPv4::timestamp_request(IP4::addr /* ip */) { // TODO - // send_request(ip, icmp4::Type::TIMESTAMP, 0, ...); + // send_request(ip, ICMP_type::TIMESTAMP, 0, ...); } void ICMPv4::timestamp_reply(icmp4::Packet& /* req */) { // TODO - // send_response(req, icmp4::Type::TIMESTAMP_REPLY, 0, ...); + // send_response(req, ICMP_type::TIMESTAMP_REPLY, 0, ...); } void ICMPv4::ping(IP4::addr ip) - { send_request(ip, icmp4::Type::ECHO, 0); } + { send_request(ip, ICMP_type::ECHO, 0); } - void ICMPv4::ping(IP4::addr ip, icmp_func callback) - { send_request(ip, icmp4::Type::ECHO, 0, callback); } + void ICMPv4::ping(IP4::addr ip, icmp_func callback, int sec_wait) + { send_request(ip, ICMP_type::ECHO, 0, callback, sec_wait); } + + void ICMPv4::ping(const std::string& hostname) { + inet_.resolve(hostname, [this] (IP4::addr a, Error err) { + if (!err and a != IP4::ADDR_ANY) + ping(a); + }); + } + + void ICMPv4::ping(const std::string& hostname, icmp_func callback, int sec_wait) { + inet_.resolve(hostname, Inet::resolve_func::make_packed([this, callback, sec_wait] (IP4::addr a, Error err) { + if (!err and a != IP4::ADDR_ANY) + ping(a, callback, sec_wait); + })); + } - void ICMPv4::send_request(IP4::addr dest_ip, icmp4::Type type, uint8_t code, - icmp_func callback, uint16_t sequence) { + void ICMPv4::send_request(IP4::addr dest_ip, ICMP_type type, ICMP_code code, + icmp_func callback, int sec_wait, uint16_t sequence) { // Provision new IP4-packet icmp4::Packet req(inet_.ip_packet_factory()); @@ -154,7 +175,7 @@ namespace net { if (callback) { 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})); + std::forward_as_tuple(ICMP_callback{*this, std::make_pair(temp_id, sequence), callback, sec_wait})); } debug(" Transmitting request to %s\n", dest_ip.to_string().c_str()); @@ -172,7 +193,7 @@ namespace net { network_layer_out_(req.release()); } - void ICMPv4::send_response(icmp4::Packet& req, icmp4::Type type, uint8_t code, uint8_t error) { + void ICMPv4::send_response(icmp4::Packet& req, ICMP_type type, ICMP_code code, uint8_t error_pointer) { // Provision new IP4-packet icmp4::Packet res(inet_.ip_packet_factory()); @@ -193,8 +214,8 @@ namespace net { // Add checksum res.set_checksum(); - if (error != std::numeric_limits::max()) - res.set_pointer(error); + if (error_pointer != std::numeric_limits::max()) + res.set_pointer(error_pointer); debug(" Response size: %i payload size: %i, checksum: 0x%x\n", res.ip().size(), res.payload().size(), res.compute_checksum()); @@ -211,7 +232,7 @@ namespace net { res.ip().set_ip_dst(req.ip().ip_src()); // Populate response ICMP header - res.set_type(icmp4::Type::ECHO_REPLY); + res.set_type(ICMP_type::ECHO_REPLY); res.set_code(0); // Incl. id and sequence number res.set_id(req.id()); diff --git a/src/net/ip4/ip4.cpp b/src/net/ip4/ip4.cpp index cbf998123a..a0ef85a9bd 100644 --- a/src/net/ip4/ip4.cpp +++ b/src/net/ip4/ip4.cpp @@ -36,22 +36,21 @@ namespace net { stack_ {inet}, upstream_filter_ {this, &IP4::filter_upstream}, downstream_filter_{this, &IP4::filter_downstream} - { } + {} IP4::IP_packet_ptr IP4::drop(IP_packet_ptr ptr, Direction direction, Drop_reason reason) { - packets_dropped_++; + packets_dropped_++; - if(drop_handler_) - drop_handler_(std::move(ptr), direction, reason); + if(drop_handler_) + drop_handler_(std::move(ptr), direction, reason); - return nullptr; - } + return nullptr; + } IP4::IP_packet_ptr IP4::filter_upstream(IP4::IP_packet_ptr packet) { - IP4::Direction up = IP4::Direction::Upstream; // RFC-1122 3.2.1.1, Silently discard Version != 4 @@ -63,18 +62,18 @@ namespace net { return drop(std::move(packet), up, Drop_reason::Wrong_checksum); // RFC-1122 3.2.1.3, Silently discard datagram with bad src addr - if (UNLIKELY(packet->ip_src().is_illegal_src())) + // Here dropping if the source ip address is a multicast address or is this interface's broadcast address + if (UNLIKELY(packet->ip_src().is_multicast() or packet->ip_src() == IP4::ADDR_BCAST or + packet->ip_src() == stack_.broadcast_addr())) { return drop(std::move(packet), up, Drop_reason::Bad_source); - + } return packet; - - }; + } IP4::IP_packet_ptr IP4::filter_downstream(IP4::IP_packet_ptr packet) { - // RFC-1122 3.2.1.7, MUST NOT send packet with TTL of 0 if (packet->ip_ttl() == 0) return drop(std::move(packet), Direction::Downstream, Drop_reason::TTL0); @@ -204,5 +203,4 @@ namespace net { linklayer_out_(std::move(ip4_pckt), next_hop); } - } //< namespace net diff --git a/src/net/ip4/udp.cpp b/src/net/ip4/udp.cpp index 4e9b326e4d..9557a0b9c7 100644 --- a/src/net/ip4/udp.cpp +++ b/src/net/ip4/udp.cpp @@ -38,6 +38,7 @@ namespace net { 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_packet->src_port(), udp_packet->dst_port(), udp_packet->length()); @@ -55,29 +56,24 @@ namespace net { // 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)); - if (ip4_packet->ip_dst() != IP4::ADDR_BCAST and (ip4_packet->ip_dst().part(3) <= 224 or - ip4_packet->ip_dst().part(3) >= 239)) + if (ip4_packet->ip_dst() != stack_.broadcast_addr() and ip4_packet->ip_dst() != IP4::ADDR_BCAST and + not ip4_packet->ip_dst().is_multicast()) { 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) { + void UDP::error_report(Error& err, Socket dest) { + // If err is an ICMP error message: // 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; - } + // Find callback with this destination address and port, and call it with the incoming err + auto it = error_callbacks_.find(Socket{dest.address(), dest.port()}); - debug("<%s> UDP Error report: Nobody listening on %u. Drop!\n", - stack_.ifname().c_str(), src_port); + if (it != error_callbacks_.end()) { + it->second.callback(err); + error_callbacks_.erase(it); + } } UDPSocket& UDP::bind(UDP::port_t port) @@ -94,7 +90,7 @@ namespace net { std::forward_as_tuple(port), std::forward_as_tuple(*this, port)); it = res.first; - }else { + } else { throw UDP::Port_in_use_exception(it->first); } return it->second; @@ -143,6 +139,18 @@ namespace net { if (packets) process_sendq(packets); } + void UDP::flush_expired() { + INFO("UDP", "Flushing expired error callbacks"); + + for (auto& err : error_callbacks_) { + if (err.second.expired()) + error_callbacks_.erase(err.first); + } + + if (not error_callbacks_.empty()) + flush_timer_.start(flush_interval_); + } + void UDP::process_sendq(size_t num) { while (!sendq.empty() && num != 0) @@ -154,11 +162,21 @@ namespace net { num--; if (buffer.done()) { - auto copy = buffer.callback; + if (buffer.send_callback != nullptr) + buffer.send_callback(); + + if (buffer.error_callback != nullptr) { + error_callbacks_.emplace(std::piecewise_construct, + std::forward_as_tuple(Socket{buffer.d_addr, buffer.d_port}), + std::forward_as_tuple(Error_entry{buffer.error_callback})); + + if (UNLIKELY(not flush_timer_.is_running())) + flush_timer_.start(flush_interval_); + } + // remove buffer from queue sendq.pop_front(); - // call on_written callback - copy(); + // reduce @num, just in case packets were sent in // another stack frame size_t avail = stack_.transmit_queue_available(); @@ -177,9 +195,9 @@ namespace net { return P; } - UDP::WriteBuffer::WriteBuffer(const uint8_t* data, size_t length, sendto_handler cb, + UDP::WriteBuffer::WriteBuffer(const uint8_t* data, size_t length, sendto_handler cb, error_handler ecb, UDP& stack, addr_t LA, port_t LP, addr_t DA, port_t DP) - : len(length), offset(0), callback(cb), udp(stack), + : len(length), offset(0), send_callback(cb), error_callback(ecb), udp(stack), l_addr(LA), l_port(LP), d_port(DP), d_addr(DA) { // create a copy of the data, diff --git a/src/net/ip4/udp_socket.cpp b/src/net/ip4/udp_socket.cpp index 82ed5d2a5a..79688daf02 100644 --- a/src/net/ip4/udp_socket.cpp +++ b/src/net/ip4/udp_socket.cpp @@ -41,44 +41,39 @@ namespace net } void UDPSocket::internal_read(UDP::Packet_ptr udp) - { - on_read_handler(udp->ip_src(), udp->src_port(), (const char*) udp->data(), udp->data_length()); - } - - 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); - } + { on_read_handler(udp->ip_src(), udp->src_port(), (const char*) udp->data(), udp->data_length()); } - void UDPSocket::sendto ( + void UDPSocket::sendto( addr_t destIP, port_t port, const void* buffer, - size_t len, - sendto_handler cb) + size_t length, + sendto_handler cb, + error_handler ecb) { - if (UNLIKELY(len == 0)) return; + if (UNLIKELY(length == 0)) return; udp_.sendq.emplace_back( - (const uint8_t*) buffer, len, cb, this->udp_, + (const uint8_t*) buffer, length, cb, ecb, this->udp_, local_addr(), this->l_port, destIP, port); // UDP packets are meant to be sent immediately, so try flushing udp_.flush(); } - void UDPSocket::bcast ( - addr_t srcIP, - port_t port, - const void* buffer, - size_t len, - sendto_handler cb) + + void UDPSocket::bcast( + addr_t srcIP, + port_t port, + const void* buffer, + size_t length, + sendto_handler cb, + error_handler ecb) { - if (UNLIKELY(len == 0)) return; + if (UNLIKELY(length == 0)) return; udp_.sendq.emplace_back( - (const uint8_t*) buffer, len, cb, this->udp_, + (const uint8_t*) buffer, length, cb, ecb, this->udp_, srcIP, this->l_port, IP4::ADDR_BCAST, port); // UDP packets are meant to be sent immediately, so try flushing udp_.flush(); } -} +} // < namespace net diff --git a/src/net/tcp/listener.cpp b/src/net/tcp/listener.cpp index 8535d68682..b2bd9e2d6e 100644 --- a/src/net/tcp/listener.cpp +++ b/src/net/tcp/listener.cpp @@ -21,7 +21,8 @@ #include #include -using namespace net::tcp; +using namespace net; +using namespace tcp; Listener::Listener(TCP& host, Socket local, ConnectCallback cb) : host_(host), local_(local), syn_queue_(), diff --git a/src/net/tcp/tcp.cpp b/src/net/tcp/tcp.cpp index 97cce0a9d3..a4b4a5c3fc 100644 --- a/src/net/tcp/tcp.cpp +++ b/src/net/tcp/tcp.cpp @@ -88,7 +88,7 @@ void TCP::Port_util::increment_ephemeral() Current solution: Simple. */ -Listener& TCP::listen(tcp::Socket socket, ConnectCallback cb) +Listener& TCP::listen(Socket socket, ConnectCallback cb) { bind(socket); @@ -99,7 +99,7 @@ Listener& TCP::listen(tcp::Socket socket, ConnectCallback cb) return *listener; } -bool TCP::close(tcp::Socket socket) { +bool TCP::close(Socket socket) { auto it = listeners_.find(socket); if(it != listeners_.end()) { @@ -247,11 +247,8 @@ 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::error_report(Error& /* err */, Socket /* dest */) { + // TODO } void TCP::transmit(tcp::Packet_ptr packet) { diff --git a/src/plugins/unik.cpp b/src/plugins/unik.cpp index 4031695402..e32cefaa88 100644 --- a/src/plugins/unik.cpp +++ b/src/plugins/unik.cpp @@ -61,7 +61,7 @@ void unik::Client::register_instance(net::Inet& inet, const net::UDP:: INFO("Unik client","Prefix: %s , IP: '%s' \n", prefix.c_str(), ip_str.c_str()); net::IP4::addr ip{ip_str}; - net::tcp::Socket unik_instance_listener { ip , 3000}; + net::Socket unik_instance_listener { ip , 3000}; attempts_left --; INFO("Unik client", "Connecting to UniK instance listener %s:%i (attempt %i / %i) ", diff --git a/src/util/statman.cpp b/src/util/statman.cpp index 0c756eb361..dbbe8f9fb9 100644 --- a/src/util/statman.cpp +++ b/src/util/statman.cpp @@ -81,16 +81,6 @@ Statman::Statman(const uintptr_t start, const Size_type num_bytes) { stats_ = Span{reinterpret_cast(start), num_stats_in_span}; } -/////////////////////////////////////////////////////////////////////////////// -Statman::Span_iterator Statman::last_used() { - Expects(next_available_ <= stats_.size()); - - auto it = stats_.begin(); - std::advance(it, next_available_); - - return it; -} - /////////////////////////////////////////////////////////////////////////////// Stat& Statman::create(const Stat::Stat_type type, const std::string& name) { if (name.empty()) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bc92d866ed..bf790a3de6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -80,8 +80,8 @@ set(TEST_SOURCES ${TEST}/net/unit/ip4_addr.cpp ${TEST}/net/unit/ip4.cpp ${TEST}/net/unit/packets.cpp + ${TEST}/net/unit/socket.cpp ${TEST}/net/unit/tcp_packet_test.cpp - ${TEST}/net/unit/tcp_socket.cpp ${TEST}/net/unit/tcp_write_queue.cpp ${TEST}/net/unit/router.cpp ${TEST}/posix/unit/fd_map_test.cpp @@ -197,7 +197,7 @@ if(EXTRA_TESTS) message(STATUS "Adding some extra tests") list(APPEND TEST_SOURCES ${TEST}/kernel/unit/rdrand_test.cpp) list(APPEND TEST_SOURCES ${TEST}/util/unit/tar_test.cpp) - + endif() if(COVERAGE) diff --git a/test/net/integration/dns/CMakeLists.txt b/test/net/integration/dns/CMakeLists.txt new file mode 100644 index 0000000000..ffd35d5fa0 --- /dev/null +++ b/test/net/integration/dns/CMakeLists.txt @@ -0,0 +1,34 @@ +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_dns) + +# Human-readable name of your service +set(SERVICE_NAME "DNS Test Service") + +# Name of your service binary +set(BINARY "test_dns") + +# 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 # ...add more here + ) + +# DRIVERS / PLUGINS: + +set(DRIVERS + virtionet + ) + +# include service build script +include($ENV{INCLUDEOS_PREFIX}/includeos/service.cmake) diff --git a/test/net/integration/dns/README.md b/test/net/integration/dns/README.md new file mode 100644 index 0000000000..66994cbb11 --- /dev/null +++ b/test/net/integration/dns/README.md @@ -0,0 +1 @@ +# DNS Test diff --git a/test/net/integration/dns/service.cpp b/test/net/integration/dns/service.cpp new file mode 100644 index 0000000000..ec11b210a7 --- /dev/null +++ b/test/net/integration/dns/service.cpp @@ -0,0 +1,108 @@ +// 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. + +#include +#include + +using namespace net; + +void print_error(const std::string& hostname, IP4::addr server, Error& err) { + printf("Error occurred when resolving IP address of %s with DNS server %s: %s\n", hostname.c_str(), + server.to_string().c_str(), err.what()); + + if (err.is_icmp()) { + auto* pd = dynamic_cast(&err); + printf("ICMP error type received when resolving %s: %s\n", hostname.c_str(), pd->icmp_type_str().c_str()); + printf("ICMP error code received when resolving %s: %s\n", hostname.c_str(), pd->icmp_code_str().c_str()); + } +} + +void print_not_resolved(const std::string& hostname) { + printf("%s couldn't be resolved\n", hostname.c_str()); +} + +void print_success(const std::string& hostname, IP4::addr server, IP4::addr res) { + printf("Resolved IP address of %s with DNS server %s: %s\n", hostname.c_str(), + server.to_string().c_str(), res.to_string().c_str()); +} + +void Service::start(const std::string&) +{ + auto& inet = net::Inet4::stack<0>(); + inet.network_config( + { 10, 0, 0, 48 }, // IP + { 255, 255, 255, 0 }, // Netmask + { 10, 0, 0, 1 }, // Gateway + { 8, 8, 8, 8 } // DNS + ); + + const std::string google = "google.com"; + const std::string github = "github.com"; + const std::string guardian = "theguardian.com"; + const std::string some_address = "some_address_that_doesnt_exist.com"; + + const IP4::addr stack_dns = inet.dns_addr(); + const IP4::addr level3 = IP4::addr{4, 2, 2, 1}; + + inet.resolve(google, [google, stack_dns] (IP4::addr res, Error& err) { + if (err) { + print_error(google, stack_dns, err); + } + else { + if (res != IP4::ADDR_ANY) + print_success(google, stack_dns, res); + else + print_not_resolved(google); + } + }); + + inet.resolve(github, [github, stack_dns] (IP4::addr res, Error& err) { + if (err) { + print_error(github, stack_dns, err); + } + else { + if (res != IP4::ADDR_ANY) + print_success(github, stack_dns, res); + else + print_not_resolved(github); + } + }); + + inet.resolve(guardian, level3, [guardian, level3] (IP4::addr res, Error& err) { + if (err) { + print_error(guardian, level3, err); + } + else { + if (res != IP4::ADDR_ANY) + print_success(guardian, level3, res); + else + print_not_resolved(guardian); + } + }); + + inet.resolve(some_address, [some_address, stack_dns] (IP4::addr res, Error& err) { + if (err) { + print_error(some_address, stack_dns, err); + } + else { + if (res != IP4::ADDR_ANY) + print_success(some_address, stack_dns, res); + else + print_not_resolved(some_address); + } + }); +} diff --git a/test/net/integration/dns/setup.sh b/test/net/integration/dns/setup.sh new file mode 100755 index 0000000000..ea66ad5d5a --- /dev/null +++ b/test/net/integration/dns/setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Sets up the environment needed for running the DNS test +OUTWARD_FACING_INTERFACE=ens3 +BRIDGE_INTERFACE=bridge43 + +# Enable ip forwarding +sudo sysctl -w net.ipv4.ip_forward=1 + +# Create iptables rules for NATing all DNS requests + +# Masks the source address +sudo iptables -t nat -A POSTROUTING -o $OUTWARD_FACING_INTERFACE -j MASQUERADE + +# Udp packets coming from bridge43 should be natted +sudo iptables -t nat -A PREROUTING -i $BRIDGE_INTERFACE -p udp -m udp diff --git a/test/net/integration/dns/test.py b/test/net/integration/dns/test.py new file mode 100755 index 0000000000..c584afdb0c --- /dev/null +++ b/test/net/integration/dns/test.py @@ -0,0 +1,39 @@ +#! /usr/bin/env python + +import sys +import os +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 +from vmrunner.prettify import color + +# Install build dependencies, ip forwarding +subprocess.call(["bash", "setup.sh"]) + +# Get an auto-created VM from the vmrunner +vm = vmrunner.vms[0] + +counter = 0 + +def count(trigger_line): + global counter + counter += 1 + + if counter == 4: + finish() + +def finish(): + vm.exit(0, "DNS resolution test succeeded") + +# Add custom event-handler +vm.on_output("Resolved IP address of google.com with DNS server 8.8.8.8", count) +vm.on_output("Resolved IP address of theguardian.com with DNS server 4.2.2.1", count) +vm.on_output("Resolved IP address of github.com with DNS server 8.8.8.8", count) +vm.on_output("some_address_that_doesnt_exist.com couldn't be resolved", count) + +# Boot the VM, taking a timeout as parameter +vm.cmake().boot(20).clean() diff --git a/test/net/integration/dns/vm.json b/test/net/integration/dns/vm.json new file mode 100644 index 0000000000..93dab698fb --- /dev/null +++ b/test/net/integration/dns/vm.json @@ -0,0 +1,6 @@ +{ + "image" : "test_dns.img", + "net" : [{"device" : "virtio", "mac" : "c0:01:0a:00:00:2a"}], + "mem" : 128, + "intrusive" : "True" +} diff --git a/test/net/integration/icmp/service.cpp b/test/net/integration/icmp/service.cpp index 8701a1fc2e..be8e26644f 100644 --- a/test/net/integration/icmp/service.cpp +++ b/test/net/integration/icmp/service.cpp @@ -25,11 +25,13 @@ 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 + { 10, 0, 0, 1 }, // Gateway + { 8, 8, 8, 8 } // DNS + ); printf("Service IP address is %s\n", inet.ip_addr().str().c_str()); // ping gateway - inet.icmp().ping(inet.gateway(), [](ICMP_packet pckt) { + inet.icmp().ping(inet.gateway(), [](ICMP_view pckt) { if (pckt) printf("Received packet from gateway\n%s\n", pckt.to_string().c_str()); else @@ -37,32 +39,42 @@ void Service::start(const std::string&) }); /* If IP forwarding on: + inet.icmp().ping("google.com", [](ICMP_view pckt) { + if (pckt) + printf("Received packet\n%s\n", pckt.to_string().c_str()); + else + printf("No reply received from google.com. Identifier: %u. Sequence number: %u\n", pckt.id(), pckt.seq()); + }); + // ping google.com with callback - inet.icmp().ping(IP4::addr{193,90,147,109}, [](ICMP_packet pckt) { - if (pckt.is_reply()) // or pckt.type() != icmp4::Type::NO_REPLY + inet.icmp().ping(IP4::addr{193,90,147,109}, [](ICMP_view pckt) { + if (pckt) 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()); }); */ + const int wait = 10; + // No reply-pings // Waiting 30 seconds for reply - inet.icmp().ping(IP4::addr{10,0,0,42}, [](ICMP_packet pckt) { + inet.icmp().ping(IP4::addr{10,0,0,42}, [](ICMP_view pckt) { 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\n"); - }); + }, wait); // Waiting 30 seconds for reply - inet.icmp().ping(IP4::addr{10,0,0,43}, [](ICMP_packet pckt) { + inet.icmp().ping(IP4::addr{10,0,0,43}, [](ICMP_view pckt) { 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\n"); - }); + }, wait); // Also possible without callback: + // inet.icmp().ping("google.com"); // inet.icmp().ping(IP4::addr{193,90,147,109}); // google.com } diff --git a/test/net/integration/icmp/test.py b/test/net/integration/icmp/test.py index 52055f09d2..b705386809 100755 --- a/test/net/integration/icmp/test.py +++ b/test/net/integration/icmp/test.py @@ -53,6 +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 \ + "Checksum: " in output_data and \ + "Data: INCLUDEOS12345ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678" in output_data and \ "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 diff --git a/test/net/integration/tcp/service.cpp b/test/net/integration/tcp/service.cpp index 59c060d873..d6b449a9b4 100644 --- a/test/net/integration/tcp/service.cpp +++ b/test/net/integration/tcp/service.cpp @@ -77,7 +77,7 @@ void OUTGOING_TEST_INTERNET(const HostAddress& address) { // This needs correct setup to work INFO("TEST", "Outgoing Internet Connection (%s:%u)", address.first.c_str(), address.second); Inet4::stack<0>().resolve(address.first, - [port](auto ip_address) { + [port](auto ip_address, Error&) { CHECK(ip_address != 0, "Resolved host"); if(ip_address != 0) @@ -97,7 +97,7 @@ void OUTGOING_TEST_INTERNET(const HostAddress& address) { /* TEST: Outgoing Connection to Host */ -void OUTGOING_TEST(tcp::Socket outgoing) { +void OUTGOING_TEST(Socket outgoing) { INFO("TEST", "Outgoing Connection (%s)", outgoing.to_string().c_str()); Inet4::stack<0>().tcp().connect(outgoing, [](tcp::Connection_ptr conn) { diff --git a/test/net/unit/ip4.cpp b/test/net/unit/ip4.cpp index bcf142badf..779d624234 100644 --- a/test/net/unit/ip4.cpp +++ b/test/net/unit/ip4.cpp @@ -45,16 +45,19 @@ std::map pass_count { void ip_rcv_udp(net::Packet_ptr) { MYINFO("UDP got packet from IP"); + pass_count[net::Protocol::UDP]++; } void ip_rcv_tcp(net::Packet_ptr) { MYINFO("TCP got packet from IP"); + pass_count[net::Protocol::TCP]++; } void ip_rcv_icmp(net::Packet_ptr) { MYINFO("ICMP got packet from IP"); + pass_count[net::Protocol::ICMPv4]++; } @@ -166,7 +169,7 @@ CASE("IP4 is still a thing") EXPECT_DROP(std::move(ip_pckt), Direction::Upstream, Drop_reason::Bad_source); ip_pckt = inet.create_ip_packet(Protocol::UDP); - ip_pckt->set_ip_src({0,0,0,255}); + ip_pckt->set_ip_src(inet.broadcast_addr()); ip_pckt->make_flight_ready(); EXPECT_DROP(std::move(ip_pckt), Direction::Upstream, Drop_reason::Bad_source); @@ -183,11 +186,30 @@ CASE("IP4 is still a thing") EXPECT_DROP(std::move(ip_pckt), Direction::Upstream, Drop_reason::Bad_source); + // Corner case source from multicast address gets dropped + ip_pckt = inet.create_ip_packet(Protocol::UDP); + ip_pckt->set_ip_src({239,255,255,255}); + ip_pckt->make_flight_ready(); + + EXPECT_DROP(std::move(ip_pckt), Direction::Upstream, Drop_reason::Bad_source); MYINFO("Section %i done", ++sections); } + // + // Packet with unknown protocol gets dropped + // + + + SECTION("IP packet with invalid protocol gets dropped"){ + auto ip_pckt = inet.create_ip_packet((Protocol) 16); // CHAOS + ip_pckt->set_ip_src({10,0,0,45}); + ip_pckt->make_flight_ready(); + EXPECT_DROP(std::move(ip_pckt), Direction::Upstream, Drop_reason::Unknown_proto); + } + + // // Valid packets are forwarded to proto handlers // @@ -199,7 +221,6 @@ CASE("IP4 is still a thing") auto ip_pckt = inet.create_ip_packet(Protocol::UDP); ip_pckt->make_flight_ready(); EXPECT_PASS(std::move(ip_pckt), Protocol::UDP); - } SECTION("Valid TCP packets gets passed on"){ diff --git a/test/net/unit/tcp_socket.cpp b/test/net/unit/socket.cpp similarity index 95% rename from test/net/unit/tcp_socket.cpp rename to test/net/unit/socket.cpp index 3993226c54..9ce0e59ea5 100644 --- a/test/net/unit/tcp_socket.cpp +++ b/test/net/unit/socket.cpp @@ -16,9 +16,11 @@ // limitations under the License. #include -#include +#include +#include -using namespace net::tcp; +using namespace net; +using namespace tcp; CASE("Creating a Socket without arguments is empty") {