lokinet/llarp/quic/address.hpp
Jason Rhinelander 752879d712
QUIC lokinet integration refactor
Refactors how quic packets get handled: the actual tunnels now live in
tunnel.hpp's TunnelManager which holds and manages all the quic<->tcp
tunnelling.  service::Endpoint now holds a TunnelManager rather than a
quic::Server.  We only need one quic server, but we need a separate quic
client instance per outgoing quic tunnel, and TunnelManager handles all
that glue now.

Adds QUIC packet handling to get to the right tunnel code.  This
required multiplexing incoming quic packets, as follows:

Adds a very small quic tunnel packet header of 4 bytes:

    [1, SPORT, ECN] for client->server packets, where SPORT is our
    source "port" (really: just a uint16_t unique quic instance
    identifier)

or

    [2, DPORT, ECN] for server->client packets where the DPORT is the SPORT
    from above.

(This also reworks ECN bits to get properly carried over lokinet.)

We don't need a destination/source port for the server-side because
there is only ever one quic server (and we know we're going to it when
the first byte of the header is 1).

Removes the config option for quic exposing ports; a full lokinet will
simply accept anything incoming on quic and tunnel it to the requested
port on the the local endpoint IP (this handler will come in a following
commit).

Replace ConvoTags with full addresses: we need to carry the port, as
well, which the ConvoTag can't give us, so change those to more general
SockAddrs from which we can extract both the ConvoTag *and* the port.

Add a pending connection queue along with new quic-side handlers to call
when a stream becomes available (TunnelManager uses this to wire up
pending incoming conns with quic streams as streams open up).

Completely get rid of tunnel_server/tunnel_client.cpp code; it is now
moved to tunnel.hpp.

Add listen()/forget() methods in TunnelManager for setting up quic
listening sockets (for liblokinet usage).

Add open()/close() methods in TunnelManager for spinning up new quic
clients for outgoing quic connections.
2021-04-19 06:58:36 -04:00

148 lines
3.3 KiB
C++

#pragma once
#include <llarp/net/sock_addr.hpp>
#include <array>
#include <cassert>
#include <cstring>
#include <memory>
#include <string>
#include <iosfwd>
#include <ngtcp2/ngtcp2.h>
#include <llarp/net/sock_addr.hpp>
#include <llarp/service/convotag.hpp>
namespace llarp::quic
{
// Wrapper around a sockaddr; ngtcp2 requires more intrusive access that llarp::SockAddr is meant
// to deal with, hence this wrapper (rather than trying to abuse llarp::SockAddr).
class Address
{
sockaddr_in6 saddr{};
ngtcp2_addr a{0, reinterpret_cast<sockaddr*>(&saddr)};
public:
Address() = default;
Address(const SockAddr& addr);
Address(const Address& other)
{
*this = other;
}
Address&
operator=(const Address&);
// Implicit conversion to sockaddr* and ngtcp2_addr& so that an Address can be passed wherever
// one of those is expected.
operator sockaddr*()
{
return reinterpret_cast<sockaddr*>(&saddr);
}
operator const sockaddr*() const
{
return reinterpret_cast<const sockaddr*>(&saddr);
}
operator ngtcp2_addr&()
{
return a;
}
operator const ngtcp2_addr&() const
{
return a;
}
size_t
sockaddr_size() const
{
return sizeof(sockaddr_in6);
}
// Implicit conversion to a convo tag so you can pass an Address to things taking a ConvoTag
operator service::ConvoTag() const;
// Returns the lokinet pseudo-port for the quic connection (which routes this quic packet to the
// correct waiting quic instance on the remote).
nuint16_t
port() const
{
return nuint16_t{saddr.sin6_port};
}
// Sets the address port
void
port(nuint16_t port)
{
saddr.sin6_port = port.n;
}
// Implicit conversion to SockAddr for going back to general llarp code
// FIXME: see if this is still needed, I think it may have been refactored away with the
// ConvoTag operator
operator SockAddr() const
{
return SockAddr(saddr);
}
std::string
to_string() const;
};
// Wraps an ngtcp2_path (which is basically just and address pair) with remote/local components.
// Implicitly convertable to a ngtcp2_path* so that this can be passed wherever a ngtcp2_path* is
// taken in the ngtcp2 API.
struct Path
{
private:
Address local_, remote_;
public:
ngtcp2_path path{{local_.sockaddr_size(), local_}, {remote_.sockaddr_size(), remote_}, nullptr};
// Public accessors are const:
const Address& local = local_;
const Address& remote = remote_;
Path() = default;
Path(const Address& laddr, const Address& raddr) : local_{laddr}, remote_{raddr}
{}
Path(const Path& p) : Path{p.local, p.remote}
{}
Path&
operator=(const Path& p)
{
local_ = p.local_;
remote_ = p.remote_;
return *this;
}
// Equivalent to `&obj.path`, but slightly more convenient for passing into ngtcp2 functions
// taking a ngtcp2_path pointer.
operator ngtcp2_path*()
{
return &path;
}
operator const ngtcp2_path*() const
{
return &path;
}
std::string
to_string() const;
};
std::ostream&
operator<<(std::ostream& o, const Address& a);
std::ostream&
operator<<(std::ostream& o, const Path& p);
} // namespace llarp::quic