/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #include "stack.hh" #include "net.hh" #include "ip.hh" #include "tcp.hh" #include "udp.hh" #include "virtio.hh" #include "proxy.hh" #include #include namespace net { template class native_server_socket_impl; template class native_connected_socket_impl; class native_network_stack; // native_server_socket_impl template class native_server_socket_impl : public server_socket_impl { typename Protocol::listener _listener; public: native_server_socket_impl(Protocol& proto, uint16_t port, listen_options opt); virtual future accept() override; }; template native_server_socket_impl::native_server_socket_impl(Protocol& proto, uint16_t port, listen_options opt) : _listener(proto.listen(port)) { } template future native_server_socket_impl::accept() { return _listener.accept().then([this] (typename Protocol::connection conn) { return make_ready_future( connected_socket(std::make_unique>(std::move(conn))), socket_address()); // FIXME: don't fake it }); } // native_connected_socket_impl template class native_connected_socket_impl : public connected_socket_impl { typename Protocol::connection _conn; class native_data_source_impl; class native_data_sink_impl; public: explicit native_connected_socket_impl(typename Protocol::connection conn) : _conn(std::move(conn)) {} virtual input_stream input() override; virtual output_stream output() override; }; template class native_connected_socket_impl::native_data_source_impl final : public data_source_impl { typename Protocol::connection& _conn; size_t _cur_frag = 0; bool _eof = false; packet _buf; public: explicit native_data_source_impl(typename Protocol::connection& conn) : _conn(conn) {} virtual future> get() override { if (_eof) { return make_ready_future>(temporary_buffer(0)); } if (_cur_frag != _buf.nr_frags()) { auto& f = _buf.fragments()[_cur_frag++]; return make_ready_future>( temporary_buffer(f.base, f.size, make_deleter(deleter(), [p = _buf.share()] () mutable {}))); } return _conn.wait_for_data().then([this] { _buf = _conn.read(); _cur_frag = 0; _eof = !_buf.len(); return get(); }); } }; template class native_connected_socket_impl::native_data_sink_impl final : public data_sink_impl { typename Protocol::connection& _conn; public: explicit native_data_sink_impl(typename Protocol::connection& conn) : _conn(conn) {} virtual future<> put(std::vector> data) override { std::vector frags; frags.reserve(data.size()); for (auto& e : data) { frags.push_back(fragment{e.get_write(), e.size()}); } return _conn.send(packet(std::move(frags), [tmp = std::move(data)] () mutable {})); } virtual future<> put(temporary_buffer data) override { return _conn.send(packet({data.get_write(), data.size()}, data.release())); } }; template input_stream native_connected_socket_impl::input() { data_source ds(std::make_unique(_conn)); return input_stream(std::move(ds)); } template output_stream native_connected_socket_impl::output() { data_sink ds(std::make_unique(_conn)); return output_stream(std::move(ds), 8192); } // native_network_stack class native_network_stack : public network_stack { static std::unique_ptr _s; interface _netif; ipv4 _inet; udp_v4 _udp; using tcp4 = tcp; public: explicit native_network_stack(boost::program_options::variables_map opts); virtual server_socket listen(socket_address sa, listen_options opt) override; virtual udp_channel make_udp_channel(ipv4_addr addr) override; static std::unique_ptr create(boost::program_options::variables_map opts) { return std::make_unique(opts); } friend class native_server_socket_impl; }; udp_channel native_network_stack::make_udp_channel(ipv4_addr addr) { return _udp.make_channel(addr); } native_network_stack::native_network_stack(boost::program_options::variables_map opts) : _netif(smp::main_thread() ? create_virtio_net_device(opts["tap-device"].as(), opts) : create_proxy_net_device(opts)) , _inet(&_netif) , _udp(_inet) { _inet.set_host_address(ipv4_address(opts["host-ipv4-addr"].as())); _inet.set_gw_address(ipv4_address(opts["gw-ipv4-addr"].as())); _inet.set_netmask_address(ipv4_address(opts["netmask-ipv4-addr"].as())); _udp.set_queue_size(opts["udpv4-queue-size"].as()); } server_socket native_network_stack::listen(socket_address sa, listen_options opts) { assert(sa.as_posix_sockaddr().sa_family == AF_INET); return server_socket(std::make_unique>( _inet.get_tcp(), ntohs(sa.as_posix_sockaddr_in().sin_port), opts)); } std::unique_ptr native_network_stack::_s; boost::program_options::options_description nns_options() { boost::program_options::options_description opts( "Native networking stack options"); opts.add_options() ("tap-device", boost::program_options::value()->default_value("tap0"), "tap device to connect to") ("host-ipv4-addr", boost::program_options::value()->default_value("192.168.122.2"), "static IPv4 address to use") ("gw-ipv4-addr", boost::program_options::value()->default_value("192.168.122.1"), "static IPv4 gateway to use") ("netmask-ipv4-addr", boost::program_options::value()->default_value("255.255.255.0"), "static IPv4 netmask to use") ("udpv4-queue-size", boost::program_options::value()->default_value(udp_v4::default_queue_size), "Default size of the UDPv4 per-channel packet queue") ; opts.add(get_virtio_net_options_description()); return opts; } network_stack_registrator nns_registrator{ "native", nns_options(), native_network_stack::create }; }