From: Remi Gacogne Date: Tue, 26 Dec 2023 09:25:12 +0000 (+0100) Subject: dnsdist: Avoid a few more allocations in the DoQ code X-Git-Tag: auth-4.9.0-alpha1~18^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F13666%2Fhead;p=thirdparty%2Fpdns.git dnsdist: Avoid a few more allocations in the DoQ code --- diff --git a/pdns/dnsdistdist/doh3.cc b/pdns/dnsdistdist/doh3.cc index b8ed4bd219..a890e9375c 100644 --- a/pdns/dnsdistdist/doh3.cc +++ b/pdns/dnsdistdist/doh3.cc @@ -658,7 +658,7 @@ static void flushStalledResponses(H3Connection& conn) } } -static void processH3HeaderEvent(ClientState& clientState, DOH3Frontend& frontend, H3Connection& conn, const ComboAddress& client, PacketBuffer& serverConnID, std::map& headers, int64_t streamID, quiche_h3_event* event) +static void processH3HeaderEvent(ClientState& clientState, DOH3Frontend& frontend, H3Connection& conn, const ComboAddress& client, const PacketBuffer& serverConnID, std::map& headers, int64_t streamID, quiche_h3_event* event) { auto handleImmediateError = [&clientState, &frontend, &conn, streamID](const char* msg) { DEBUGLOG(msg); @@ -719,7 +719,7 @@ static void processH3HeaderEvent(ClientState& clientState, DOH3Frontend& fronten handleImmediateError("Unsupported HTTP method"); } -static void processH3DataEvent(ClientState& clientState, DOH3Frontend& frontend, H3Connection& conn, const ComboAddress& client, PacketBuffer& serverConnID, std::map& headers, int64_t streamID, quiche_h3_event* event) +static void processH3DataEvent(ClientState& clientState, DOH3Frontend& frontend, H3Connection& conn, const ComboAddress& client, const PacketBuffer& serverConnID, std::map& headers, int64_t streamID, quiche_h3_event* event, PacketBuffer& buffer) { auto handleImmediateError = [&clientState, &frontend, &conn, streamID](const char* msg) { DEBUGLOG(msg); @@ -739,14 +739,14 @@ static void processH3DataEvent(ClientState& clientState, DOH3Frontend& frontend, return; } - PacketBuffer buffer(std::numeric_limits::max()); + buffer.resize(std::numeric_limits::max()); auto& streamBuffer = conn.d_streamBuffers[streamID]; while (true) { buffer.resize(std::numeric_limits::max()); ssize_t len = quiche_h3_recv_body(conn.d_http3.get(), conn.d_conn.get(), streamID, - buffer.data(), buffer.capacity()); + buffer.data(), buffer.size()); if (len <= 0) { break; @@ -771,7 +771,7 @@ static void processH3DataEvent(ClientState& clientState, DOH3Frontend& frontend, conn.d_streamBuffers.erase(streamID); } -static void processH3Events(ClientState& clientState, DOH3Frontend& frontend, H3Connection& conn, const ComboAddress& client, PacketBuffer& serverConnID) +static void processH3Events(ClientState& clientState, DOH3Frontend& frontend, H3Connection& conn, const ComboAddress& client, const PacketBuffer& serverConnID, PacketBuffer& buffer) { std::map headers; while (true) { @@ -791,7 +791,7 @@ static void processH3Events(ClientState& clientState, DOH3Frontend& frontend, H3 break; } case QUICHE_H3_EVENT_DATA: { - processH3DataEvent(clientState, frontend, conn, client, serverConnID, headers, streamID, event); + processH3DataEvent(clientState, frontend, conn, client, serverConnID, headers, streamID, event, buffer); break; } case QUICHE_H3_EVENT_FINISHED: @@ -807,6 +807,11 @@ static void processH3Events(ClientState& clientState, DOH3Frontend& frontend, H3 static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientState, Socket& sock, PacketBuffer& buffer) { + // destination connection ID, will have to be sent as original destination connection ID + PacketBuffer serverConnID; + // source connection ID, will have to be sent as destination connection ID + PacketBuffer clientConnID; + PacketBuffer tokenBuf; while (true) { ComboAddress client; buffer.resize(4096); @@ -834,10 +839,9 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat continue; } - // destination connection ID, will have to be sent as original destination connection ID - PacketBuffer serverConnID(dcid.begin(), dcid.begin() + dcid_len); + serverConnID.assign(dcid.begin(), dcid.begin() + dcid_len); // source connection ID, will have to be sent as destination connection ID - PacketBuffer clientConnID(scid.begin(), scid.begin() + scid_len); + clientConnID.assign(scid.begin(), scid.begin() + scid_len); auto conn = getConnection(frontend.d_server_config->d_connections, serverConnID); if (!conn) { @@ -845,18 +849,18 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat if (!quiche_version_is_supported(version)) { DEBUGLOG("Unsupported version"); ++frontend.d_doh3UnsupportedVersionErrors; - handleVersionNegociation(sock, clientConnID, serverConnID, client); + handleVersionNegociation(sock, clientConnID, serverConnID, client, buffer); continue; } if (token_len == 0) { /* stateless retry */ DEBUGLOG("No token received"); - handleStatelessRetry(sock, clientConnID, serverConnID, client, version); + handleStatelessRetry(sock, clientConnID, serverConnID, client, version, buffer); continue; } - PacketBuffer tokenBuf(token.begin(), token.begin() + token_len); + tokenBuf.assign(token.begin(), token.begin() + token_len); auto originalDestinationID = validateToken(tokenBuf, client); if (!originalDestinationID) { ++frontend.d_doh3InvalidTokensReceived; @@ -897,9 +901,9 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat DEBUGLOG("Successfully created HTTP/3 connection"); } - processH3Events(clientState, frontend, conn->get(), client, serverConnID); + processH3Events(clientState, frontend, conn->get(), client, serverConnID, buffer); - flushEgress(sock, conn->get().d_conn, client); + flushEgress(sock, conn->get().d_conn, client, buffer); } else { DEBUGLOG("Connection not established"); @@ -944,7 +948,7 @@ void doh3Thread(ClientState* clientState) for (auto conn = frontend->d_server_config->d_connections.begin(); conn != frontend->d_server_config->d_connections.end();) { quiche_conn_on_timeout(conn->second.d_conn.get()); - flushEgress(sock, conn->second.d_conn, conn->second.d_peer); + flushEgress(sock, conn->second.d_conn, conn->second.d_peer, buffer); if (quiche_conn_is_closed(conn->second.d_conn.get())) { #ifdef DEBUGLOG_ENABLED diff --git a/pdns/dnsdistdist/doq-common.cc b/pdns/dnsdistdist/doq-common.cc index 4b0b2868f9..46dfa0c7c0 100644 --- a/pdns/dnsdistdist/doq-common.cc +++ b/pdns/dnsdistdist/doq-common.cc @@ -56,7 +56,9 @@ PacketBuffer mintToken(const PacketBuffer& dcid, const ComboAddress& peer) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) const auto encryptedToken = sodEncryptSym(std::string_view(reinterpret_cast(plainTextToken.data()), plainTextToken.size()), s_quicRetryTokenKey, nonce, false); // a bit sad, let's see if we can do better later - auto encryptedTokenPacket = PacketBuffer(encryptedToken.begin(), encryptedToken.end()); + PacketBuffer encryptedTokenPacket; + encryptedTokenPacket.reserve(encryptedToken.size() + nonce.value.size()); + encryptedTokenPacket.insert(encryptedTokenPacket.begin(), encryptedToken.begin(), encryptedToken.end()); encryptedTokenPacket.insert(encryptedTokenPacket.begin(), nonce.value.begin(), nonce.value.end()); return encryptedTokenPacket; } @@ -98,7 +100,7 @@ std::optional validateToken(const PacketBuffer& token, const Combo memcpy(nonce.value.data(), token.data(), nonce.value.size()); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) auto cipher = std::string_view(reinterpret_cast(&token.at(nonce.value.size())), token.size() - nonce.value.size()); auto plainText = sodDecryptSym(cipher, s_quicRetryTokenKey, nonce, false); @@ -124,7 +126,7 @@ std::optional validateToken(const PacketBuffer& token, const Combo } } -void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, uint32_t version) +void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, uint32_t version, PacketBuffer& buffer) { auto newServerConnID = getCID(); if (!newServerConnID) { @@ -133,46 +135,46 @@ void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const auto token = mintToken(serverConnID, peer); - PacketBuffer out(MAX_DATAGRAM_SIZE); + buffer.resize(MAX_DATAGRAM_SIZE); auto written = quiche_retry(clientConnID.data(), clientConnID.size(), serverConnID.data(), serverConnID.size(), newServerConnID->data(), newServerConnID->size(), token.data(), token.size(), version, - out.data(), out.size()); + buffer.data(), buffer.size()); if (written < 0) { DEBUGLOG("failed to create retry packet " << written); return; } - out.resize(written); - sock.sendTo(std::string(out.begin(), out.end()), peer); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + sock.sendTo(reinterpret_cast(buffer.data()), static_cast(written), peer); } -void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer) +void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, PacketBuffer& buffer) { - PacketBuffer out(MAX_DATAGRAM_SIZE); + buffer.resize(MAX_DATAGRAM_SIZE); auto written = quiche_negotiate_version(clientConnID.data(), clientConnID.size(), serverConnID.data(), serverConnID.size(), - out.data(), out.size()); + buffer.data(), buffer.size()); if (written < 0) { DEBUGLOG("failed to create vneg packet " << written); return; } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - sock.sendTo(reinterpret_cast(out.data()), written, peer); + sock.sendTo(reinterpret_cast(buffer.data()), static_cast(written), peer); } -void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer) +void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, PacketBuffer& buffer) { - std::array out{}; + buffer.resize(MAX_DATAGRAM_SIZE); quiche_send_info send_info; while (true) { - auto written = quiche_conn_send(conn.get(), out.data(), out.size(), &send_info); + auto written = quiche_conn_send(conn.get(), buffer.data(), buffer.size(), &send_info); if (written == QUICHE_ERR_DONE) { return; } @@ -182,7 +184,7 @@ void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer) } // FIXME pacing (as send_info.at should tell us when to send the packet) ? // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - sock.sendTo(reinterpret_cast(out.data()), written, peer); + sock.sendTo(reinterpret_cast(buffer.data()), static_cast(written), peer); } } @@ -203,6 +205,7 @@ void configureQuiche(QuicheConfig& config, const QuicheParams& params) { auto res = quiche_config_set_application_protos(config.get(), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast(params.d_alpn.data()), params.d_alpn.size()); if (res != 0) { diff --git a/pdns/dnsdistdist/doq-common.hh b/pdns/dnsdistdist/doq-common.hh index 7af19ecb79..efcb877829 100644 --- a/pdns/dnsdistdist/doq-common.hh +++ b/pdns/dnsdistdist/doq-common.hh @@ -81,9 +81,9 @@ void fillRandom(PacketBuffer& buffer, size_t size); std::optional getCID(); PacketBuffer mintToken(const PacketBuffer& dcid, const ComboAddress& peer); std::optional validateToken(const PacketBuffer& token, const ComboAddress& peer); -void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, uint32_t version); -void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer); -void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer); +void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, uint32_t version, PacketBuffer& buffer); +void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, PacketBuffer& buffer); +void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, PacketBuffer& buffer); void configureQuiche(QuicheConfig& config, const QuicheParams& params); }; diff --git a/pdns/dnsdistdist/doq.cc b/pdns/dnsdistdist/doq.cc index 5f9fff95eb..6181ca769f 100644 --- a/pdns/dnsdistdist/doq.cc +++ b/pdns/dnsdistdist/doq.cc @@ -628,6 +628,11 @@ static void handleReadableStream(DOQFrontend& frontend, ClientState& clientState static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState, Socket& sock, PacketBuffer& buffer) { + // destination connection ID, will have to be sent as original destination connection ID + PacketBuffer serverConnID; + // source connection ID, will have to be sent as destination connection ID + PacketBuffer clientConnID; + PacketBuffer tokenBuf; while (true) { ComboAddress client; buffer.resize(4096); @@ -655,10 +660,8 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState continue; } - // destination connection ID, will have to be sent as original destination connection ID - PacketBuffer serverConnID(dcid.begin(), dcid.begin() + dcid_len); - // source connection ID, will have to be sent as destination connection ID - PacketBuffer clientConnID(scid.begin(), scid.begin() + scid_len); + serverConnID.assign(dcid.begin(), dcid.begin() + dcid_len); + clientConnID.assign(scid.begin(), scid.begin() + scid_len); auto conn = getConnection(frontend.d_server_config->d_connections, serverConnID); if (!conn) { @@ -666,18 +669,18 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState if (!quiche_version_is_supported(version)) { DEBUGLOG("Unsupported version"); ++frontend.d_doqUnsupportedVersionErrors; - handleVersionNegociation(sock, clientConnID, serverConnID, client); + handleVersionNegociation(sock, clientConnID, serverConnID, client, buffer); continue; } if (token_len == 0) { /* stateless retry */ DEBUGLOG("No token received"); - handleStatelessRetry(sock, clientConnID, serverConnID, client, version); + handleStatelessRetry(sock, clientConnID, serverConnID, client, version, buffer); continue; } - PacketBuffer tokenBuf(token.begin(), token.begin() + token_len); + tokenBuf.assign(token.begin(), token.begin() + token_len); auto originalDestinationID = validateToken(tokenBuf, client); if (!originalDestinationID) { ++frontend.d_doqInvalidTokensReceived; @@ -714,7 +717,7 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState handleReadableStream(frontend, clientState, *conn, streamID, client, serverConnID); } - flushEgress(sock, conn->get().d_conn, client); + flushEgress(sock, conn->get().d_conn, client, buffer); } else { DEBUGLOG("Connection not established"); @@ -759,7 +762,7 @@ void doqThread(ClientState* clientState) for (auto conn = frontend->d_server_config->d_connections.begin(); conn != frontend->d_server_config->d_connections.end();) { quiche_conn_on_timeout(conn->second.d_conn.get()); - flushEgress(sock, conn->second.d_conn, conn->second.d_peer); + flushEgress(sock, conn->second.d_conn, conn->second.d_peer, buffer); if (quiche_conn_is_closed(conn->second.d_conn.get())) { #ifdef DEBUGLOG_ENABLED