/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #include "ip.hh" using namespace net; namespace net { namespace ipv4_udp_impl { static inline ipv4_addr to_ipv4_addr(ipv4_address a, uint16_t port) { return {a.ip, port}; } class native_datagram : public udp_datagram_impl { private: ipv4_addr _src; ipv4_addr _dst; packet _p; public: native_datagram(ipv4_address src, ipv4_address dst, packet p) : _p(std::move(p)) { udp_hdr* hdr = _p.get_header(); auto h = ntoh(*hdr); _p.trim_front(sizeof(*hdr)); _src = to_ipv4_addr(src, h.src_port); _dst = to_ipv4_addr(dst, h.dst_port); } virtual ipv4_addr get_src() override { return _src; }; virtual ipv4_addr get_dst() override { return _dst; }; virtual uint16_t get_dst_port() override { return _dst.port; } virtual packet& get_data() override { return _p; } }; class native_channel : public udp_channel_impl { private: ipv4_udp& _proto; ipv4_udp::registration _reg; bool _closed; lw_shared_ptr _state; // Limit number of data queued into send queue lw_shared_ptr _user_queue_space; public: native_channel(ipv4_udp &proto, ipv4_udp::registration reg, lw_shared_ptr state) : _proto(proto) , _reg(reg) , _closed(false) , _state(state) { _user_queue_space = make_lw_shared(212992); } virtual future receive() override { return _state->_queue.pop_eventually(); } virtual future<> send(ipv4_addr dst, const char* msg) override { return send(dst, packet::from_static_data(msg, strlen(msg))); } virtual future<> send(ipv4_addr dst, packet p) override { auto len = p.len(); return _user_queue_space->wait(len).then([this, dst, p = std::move(p), len] () mutable { _proto.send(_reg.port(), dst, std::move(p), l4send_completion(_user_queue_space, len)); }); } virtual bool is_closed() const { return _closed; } virtual void close() override { _reg.unregister(); _closed = true; } }; } /* namespace ipv4_udp_impl */ using namespace net::ipv4_udp_impl; const int ipv4_udp::default_queue_size = 1024; ipv4_udp::ipv4_udp(ipv4& inet) : _inet(inet) { } bool ipv4_udp::forward(forward_hash& out_hash_data, packet& p, size_t off) { auto uh = p.get_header(off); if (!uh) { out_hash_data.push_back(uh->src_port); out_hash_data.push_back(uh->dst_port); } return true; } void ipv4_udp::received(packet p, ipv4_address from, ipv4_address to) { udp_datagram dgram(std::make_unique(from, to, std::move(p))); auto chan_it = _channels.find(dgram.get_dst_port()); if (chan_it != _channels.end()) { auto chan = chan_it->second; chan->_queue.push(std::move(dgram)); } } void ipv4_udp::send(uint16_t src_port, ipv4_addr dst, packet &&p, l4send_completion completion) { auto src = _inet.host_address(); auto hdr = p.prepend_header(); hdr->src_port = src_port; hdr->dst_port = dst.port; hdr->len = p.len(); *hdr = hton(*hdr); offload_info oi; checksummer csum; ipv4_traits::udp_pseudo_header_checksum(csum, src, dst, p.len()); bool needs_frag = ipv4::needs_frag(p, ip_protocol_num::udp, _inet.hw_features()); if (_inet.hw_features().tx_csum_l4_offload && !needs_frag) { hdr->cksum = ~csum.get(); oi.needs_csum = true; } else { csum.sum(p); hdr->cksum = csum.get(); oi.needs_csum = false; } oi.protocol = ip_protocol_num::udp; p.set_offload_info(oi); _inet.send(dst, ip_protocol_num::udp, std::move(p), std::move(completion)); } uint16_t ipv4_udp::next_port(uint16_t port) { return (port + 1) == 0 ? min_anonymous_port : port + 1; } udp_channel ipv4_udp::make_channel(ipv4_addr addr) { if (!is_ip_unspecified(addr)) { throw std::runtime_error("Binding to specific IP not supported yet"); } uint16_t bind_port; if (!is_port_unspecified(addr)) { if (_channels.count(addr.port)) { throw std::runtime_error("Address already in use"); } bind_port = addr.port; } else { auto starting_port = _next_anonymous_port; while (_channels.count(_next_anonymous_port)) { _next_anonymous_port = next_port(_next_anonymous_port); if (starting_port == _next_anonymous_port) { throw std::runtime_error("No free port"); } } bind_port = _next_anonymous_port; _next_anonymous_port = next_port(_next_anonymous_port); } auto chan_state = make_lw_shared(_queue_size); _channels[bind_port] = chan_state; return udp_channel(std::make_unique(*this, registration(*this, bind_port), chan_state)); } } /* namespace net */