/* * \brief State tracking for UDP/TCP connections * \author Martin Stein * \date 2016-08-19 */ /* * Copyright (C) 2016-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ /* Genode includes */ #include /* local includes */ #include "link.h" #include "configuration.h" using namespace Net; using namespace Genode; /****************** ** Link_side_id ** ******************/ constexpr size_t Link_side_id::data_size() { return sizeof(src_ip) + sizeof(src_port) + sizeof(dst_ip) + sizeof(dst_port); } bool Link_side_id::operator == (Link_side_id const &id) const { return memcmp(id.data_base(), data_base(), data_size()) == 0; } bool Link_side_id::operator > (Link_side_id const &id) const { return memcmp(id.data_base(), data_base(), data_size()) > 0; } /*************** ** Link_side ** ***************/ Link_side::Link_side(Domain &domain, Link_side_id const &id, Link &link) : _domain(domain), _id(id), _link(link) { if (link.config().verbose()) { log("[", domain, "] new ", l3_protocol_name(link.protocol()), " link ", is_client() ? "client" : "server", ": ", *this); } } Link_side const &Link_side::find_by_id(Link_side_id const &id) const { if (id == _id) { return *this; } bool const side = id > _id; Link_side *const link_side = Avl_node::child(side); if (!link_side) { throw Link_side_tree::No_match(); } return link_side->find_by_id(id); } void Link_side::print(Output &output) const { Genode::print(output, "src ", src_ip(), ":", src_port(), " dst ", dst_ip(), ":", dst_port()); } bool Link_side::is_client() const { return this == &_link.client(); } /******************** ** Link_side_tree ** ********************/ Link_side const &Link_side_tree::find_by_id(Link_side_id const &id) const { Link_side *const link_side = first(); if (!link_side) { throw No_match(); } return link_side->find_by_id(id); } /********** ** Link ** **********/ void Link::print(Output &output) const { Genode::print(output, "CLN ", _client, " SRV ", _server); } Link::Link(Interface &cln_interface, Link_side_id const &cln_id, Pointer srv_port_alloc, Domain &srv_domain, Link_side_id const &srv_id, Timer::Connection &timer, Configuration &config, L3_protocol const protocol, Microseconds const dissolve_timeout, Interface_link_stats &stats) : _config(config), _client_interface(cln_interface), _server_port_alloc(srv_port_alloc), _dissolve_timeout(timer, *this, &Link::_handle_dissolve_timeout), _dissolve_timeout_us(dissolve_timeout), _protocol(protocol), _client(cln_interface.domain(), cln_id, *this), _server(srv_domain, srv_id, *this), _stats(stats), _stats_curr(stats.opening) { _stats_curr()++; _client_interface.links(_protocol).insert(this); _client.domain().links(_protocol).insert(&_client); _server.domain().links(_protocol).insert(&_server); _dissolve_timeout.schedule(_dissolve_timeout_us); } Link::~Link() { _stats.destroyed++; } void Link::_handle_dissolve_timeout(Duration) { dissolve(true); _client_interface.links(_protocol).remove(this); _client_interface.dissolved_links(_protocol).insert(this); } void Link::dissolve(bool timeout) { _stats_curr()--; if (timeout) { if (&_stats_curr() == &_stats.opening) { _stats_curr = _stats.dissolved_timeout_opening; } if (&_stats_curr() == &_stats.open) { _stats_curr = _stats.dissolved_timeout_open; } if (&_stats_curr() == &_stats.closing) { _stats_curr = _stats.dissolved_timeout_closing; } if (&_stats_curr() == &_stats.closed) { _stats_curr = _stats.dissolved_timeout_closed; } } else { _stats_curr = _stats.dissolved_no_timeout; } _stats_curr()++; _client.domain().links(_protocol).remove(&_client); _server.domain().links(_protocol).remove(&_server); if (_config().verbose()) { log("Dissolve ", l3_protocol_name(_protocol), " link: ", *this); } try { if (_config().verbose()) { log("Free ", l3_protocol_name(_protocol), " port ", _server.dst_port(), " at ", _server.domain(), " that was used by ", _client.domain()); } _server_port_alloc().free(_server.dst_port()); } catch (Pointer::Invalid) { } } void Link::handle_config(Domain &cln_domain, Domain &srv_domain, Pointer srv_port_alloc, Configuration &config) { Microseconds dissolve_timeout_us(0); switch (_protocol) { case L3_protocol::TCP: dissolve_timeout_us = config.tcp_idle_timeout(); break; case L3_protocol::UDP: dissolve_timeout_us = config.udp_idle_timeout(); break; case L3_protocol::ICMP: dissolve_timeout_us = config.icmp_idle_timeout(); break; default: throw Interface::Bad_transport_protocol(); } _dissolve_timeout_us = dissolve_timeout_us; _dissolve_timeout.schedule(_dissolve_timeout_us); _client.domain().links(_protocol).remove(&_client); _server.domain().links(_protocol).remove(&_server); _config = config; _client._domain = cln_domain; _server._domain = srv_domain; _server_port_alloc = srv_port_alloc; cln_domain.links(_protocol).insert(&_client); srv_domain.links(_protocol).insert(&_server); if (config.verbose()) { log("[", cln_domain, "] update link client: ", _client); log("[", srv_domain, "] update link server: ", _server); } } /************** ** Tcp_link ** **************/ Tcp_link::Tcp_link(Interface &cln_interface, Link_side_id const &cln_id, Pointer srv_port_alloc, Domain &srv_domain, Link_side_id const &srv_id, Timer::Connection &timer, Configuration &config, L3_protocol const protocol, Interface_link_stats &stats) : Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, config, protocol, config.tcp_idle_timeout(), stats) { } void Tcp_link::_closing() { _state = State::CLOSING; _stats_curr()--; _stats_curr = _stats.closing; _stats_curr()++; } void Tcp_link::_closed() { _state = State::CLOSED; _stats_curr()--; _stats_curr = _stats.closed; _stats_curr()++; } void Tcp_link::_tcp_packet(Tcp_packet &tcp, Peer &sender, Peer &receiver) { if (_state == State::CLOSED) { return; } if (tcp.rst()) { _closed(); } else { if (tcp.fin()) { sender.fin = true; _closing(); } if (receiver.fin && tcp.ack()) { receiver.fin_acked = true; if (sender.fin_acked) { _closed(); } else { _closing(); } } } if (_state == State::OPEN) { _packet(); } else { _dissolve_timeout.schedule( Microseconds(_config().tcp_max_segm_lifetime().value << 1)); } } void Tcp_link::server_packet(Tcp_packet &tcp) { if (_opening) { _opening = false; _stats_curr()--; if (&_stats_curr() == &_stats.opening) { _stats_curr = _stats.open; } _stats_curr()++; } _tcp_packet(tcp, _server, _client); } /************** ** Udp_link ** **************/ Udp_link::Udp_link(Interface &cln_interface, Link_side_id const &cln_id, Pointer srv_port_alloc, Domain &srv_domain, Link_side_id const &srv_id, Timer::Connection &timer, Configuration &config, L3_protocol const protocol, Interface_link_stats &stats) : Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, config, protocol, config.udp_idle_timeout(), stats) { } void Udp_link::server_packet() { if (_opening) { _opening = false; _stats_curr()--; if (&_stats_curr() == &_stats.opening) { _stats_curr = _stats.open; } _stats_curr()++; } _packet(); } /*************** ** Icmp_link ** ***************/ Icmp_link::Icmp_link(Interface &cln_interface, Link_side_id const &cln_id, Pointer srv_port_alloc, Domain &srv_domain, Link_side_id const &srv_id, Timer::Connection &timer, Configuration &config, L3_protocol const protocol, Interface_link_stats &stats) : Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, config, protocol, config.icmp_idle_timeout(), stats) { } void Icmp_link::server_packet() { if (_opening) { _opening = false; _stats_curr()--; if (&_stats_curr() == &_stats.opening) { _stats_curr = _stats.open; } _stats_curr()++; } _packet(); }