From: Libor Peltan Date: Wed, 19 Oct 2022 16:02:03 +0000 (+0200) Subject: xdp-quic/refactoring: separate XDP from QUIC routines X-Git-Tag: v3.4.dev~204^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa0a63c3deafdfc39d3c8f6df080c4ac28f6a109;p=thirdparty%2Fknot-dns.git xdp-quic/refactoring: separate XDP from QUIC routines --- diff --git a/src/knot/server/xdp-handler.c b/src/knot/server/xdp-handler.c index dcd88b0493..4e4682feff 100644 --- a/src/knot/server/xdp-handler.c +++ b/src/knot/server/xdp-handler.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 CZ.NIC, z.s.p.o. +/* Copyright (C) 2023 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -51,7 +51,7 @@ typedef struct xdp_handle_ctx { #ifdef ENABLE_QUIC knot_xquic_conn_t *quic_relays[XDP_BATCHLEN]; - int quic_rets[XDP_BATCHLEN]; + knot_quic_reply_t quic_replies[XDP_BATCHLEN]; knot_xquic_table_t *quic_table; knot_sweep_stats_t quic_closed; #endif // ENABLE_QUIC @@ -150,6 +150,22 @@ static void quic_log_cb(const char *line) { log_debug("QUIC: %s", line); } + +static int quic_alloc_cb(knot_quic_reply_t *rpl) +{ + return knot_xdp_reply_alloc(rpl->sock, rpl->in_ctx, rpl->out_ctx); +} + +static int quic_send_cb(knot_quic_reply_t *rpl) +{ + uint32_t sent = 0; + return knot_xdp_send(rpl->sock, rpl->out_ctx, 1, &sent); +} + +static void quic_free_cb(knot_quic_reply_t *rpl) +{ + knot_xdp_send_free(rpl->sock, rpl->out_ctx, 1); +} #endif // ENABLE_QUIC xdp_handle_ctx_t *xdp_handle_init(struct server *server, knot_xdp_socket_t *xdp_sock) @@ -189,6 +205,13 @@ xdp_handle_ctx_t *xdp_handle_init(struct server *server, knot_xdp_socket_t *xdp_ if (conf_get_bool(pconf, C_XDP, C_QUIC_LOG)) { ctx->quic_table->log_cb = quic_log_cb; } + for (int i = 0; i < XDP_BATCHLEN; i++) { + knot_quic_reply_t *reply = &ctx->quic_replies[i]; + reply->sock = xdp_sock; + reply->alloc_reply = quic_alloc_cb; + reply->send_reply = quic_send_cb; + reply->free_reply = quic_free_cb; + } #else assert(0); // verified in configuration checks #endif // ENABLE_QUIC @@ -368,9 +391,19 @@ static void handle_quic(xdp_handle_ctx_t *ctx, knot_layer_t *layer, continue; } - ctx->quic_rets[i] = knot_xquic_handle(ctx->quic_table, msg_recv, - ctx->quic_idle_close, - &ctx->quic_relays[i]); + knot_quic_reply_t *reply = &ctx->quic_replies[i]; + knot_xdp_msg_t *msg_out = &ctx->msg_send_udp[i]; + + reply->ip_rem = (struct sockaddr_storage *)&msg_recv->ip_from; + reply->ip_loc = (struct sockaddr_storage *)&msg_recv->ip_to; + reply->in_payload = &msg_recv->payload; + reply->out_payload = &msg_out->payload; + reply->in_ctx = msg_recv; + reply->out_ctx = msg_out; + + (void)knot_quic_handle(ctx->quic_table, reply, ctx->quic_idle_close, + &ctx->quic_relays[i]); + knot_xquic_conn_t *rl = ctx->quic_relays[i]; int64_t stream_id; @@ -434,9 +467,8 @@ void xdp_handle_send(xdp_handle_ctx_t *ctx) continue; } - ret = knot_xquic_send(ctx->quic_table, ctx->quic_relays[i], ctx->sock, - &ctx->msg_recv[i], ctx->quic_rets[i], - QUIC_MAX_SEND_PER_RECV, false); + ret = knot_quic_send(ctx->quic_table, ctx->quic_relays[i], + &ctx->quic_replies[i], QUIC_MAX_SEND_PER_RECV, false); if (ret != KNOT_EOK) { log_notice("QUIC, failed to send some packets"); } diff --git a/src/libknot/Makefile.inc b/src/libknot/Makefile.inc index 32ea26bce8..cd500d30e8 100755 --- a/src/libknot/Makefile.inc +++ b/src/libknot/Makefile.inc @@ -114,20 +114,20 @@ libknot_la_SOURCES += \ libknot/xdp/tcp.c \ libknot/xdp/xdp.c +endif ENABLE_XDP if ENABLE_QUIC libknot_la_CPPFLAGS += $(libngtcp2_CFLAGS) $(gnutls_CFLAGS) libknot_la_LIBADD += $(libngtcp2_LIBS) $(gnutls_LIBS) nobase_include_libknot_HEADERS += \ - libknot/xdp/quic.h \ - libknot/xdp/quic_conn.h + libknot/xdp/quic.h \ + libknot/xdp/quic_conn.h libknot_la_SOURCES += \ libknot/xdp/quic.c \ - libknot/xdp/quic_conn.c + libknot/xdp/quic_conn.c endif ENABLE_QUIC -endif ENABLE_XDP DIST_SUBDIRS = libknot/xdp diff --git a/src/libknot/xdp/quic.c b/src/libknot/xdp/quic.c index 1a0df337d3..d508724fa6 100644 --- a/src/libknot/xdp/quic.c +++ b/src/libknot/xdp/quic.c @@ -106,7 +106,8 @@ int knot_xquic_session_load(knot_xquic_conn_t *conn, struct knot_quic_session *s goto session_free; } - ret = gnutls_session_set_data(conn->tls_session, session->tls_session.data, session->tls_session.size); + ret = gnutls_session_set_data(conn->tls_session, session->tls_session.data, + session->tls_session.size); if (ret != KNOT_EOK) { goto session_free; } @@ -158,7 +159,8 @@ static int self_signed_cert(gnutls_certificate_credentials_t tls_cert) #define now_years(years) (time(NULL) + 365 * 24 * 3600 * (years)) - CHK(gnutls_x509_privkey_generate(privkey, GNUTLS_PK_ECDSA, GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1), 0)); + CHK(gnutls_x509_privkey_generate(privkey, GNUTLS_PK_ECDSA, + GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1), 0)); CHK(gnutls_x509_crt_init(&cert)); //CHK(gnutls_x509_crt_set_ca_status(cert, 0)); // TODO needed ? @@ -168,7 +170,8 @@ static int self_signed_cert(gnutls_certificate_credentials_t tls_cert) CHK(gnutls_x509_crt_set_key(cert, privkey)); CHK(gnutls_x509_crt_set_serial(cert, serial, sizeof(serial))); - CHK(gnutls_x509_crt_set_subject_alt_name(cert, GNUTLS_SAN_DNSNAME, hostname, strlen(hostname), GNUTLS_FSAN_SET)); + CHK(gnutls_x509_crt_set_subject_alt_name(cert, GNUTLS_SAN_DNSNAME, hostname, + strlen(hostname), GNUTLS_FSAN_SET)); CHK(gnutls_x509_crt_set_version(cert, 3)); CHK(gnutls_x509_crt_sign2(cert, cert, privkey, GNUTLS_DIG_SHA256, 0)); @@ -214,7 +217,8 @@ struct knot_quic_creds *knot_xquic_init_creds(bool server, const char *tls_cert, goto fail2; } if (tls_cert != NULL) { - ret = gnutls_certificate_set_x509_key_file(creds->tls_cert, tls_cert, tls_key, GNUTLS_X509_FMT_PEM); + ret = gnutls_certificate_set_x509_key_file(creds->tls_cert, tls_cert, + tls_key, GNUTLS_X509_FMT_PEM); } else if (server) { ret = self_signed_cert(creds->tls_cert); } @@ -523,7 +527,8 @@ static int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data; assert(ctx->conn == conn); - int ret = knot_xquic_stream_recv_data(ctx, stream_id, data, datalen, (flags & NGTCP2_STREAM_DATA_FLAG_FIN)); + int ret = knot_xquic_stream_recv_data(ctx, stream_id, data, datalen, + (flags & NGTCP2_STREAM_DATA_FLAG_FIN)); return ret == KNOT_EOK ? 0 : NGTCP2_ERR_CALLBACK_FAILURE; } @@ -556,7 +561,8 @@ static int stream_closed(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id, return 0; } -static int recv_stateless_rst(ngtcp2_conn *conn, const ngtcp2_pkt_stateless_reset *sr, void *user_data) +static int recv_stateless_rst(ngtcp2_conn *conn, const ngtcp2_pkt_stateless_reset *sr, + void *user_data) { // NOTE server can't receive stateless resets, only client @@ -682,9 +688,11 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_path *path, const ngtcp2_c } if (server) { - return ngtcp2_conn_server_new(pconn, dcid, scid, path, version, &callbacks, &settings, ¶ms, NULL, user_data); + return ngtcp2_conn_server_new(pconn, dcid, scid, path, version, &callbacks, + &settings, ¶ms, NULL, user_data); } else { - return ngtcp2_conn_client_new(pconn, dcid, scid, path, version, &callbacks, &settings, ¶ms, NULL, user_data); + return ngtcp2_conn_client_new(pconn, dcid, scid, path, version, &callbacks, + &settings, ¶ms, NULL, user_data); } } @@ -735,7 +743,8 @@ int knot_xquic_client(knot_xquic_table_t *table, struct sockaddr_in6 *dest, } _public_ -int knot_xquic_handle(knot_xquic_table_t *table, knot_xdp_msg_t *msg, uint64_t idle_timeout, knot_xquic_conn_t **out_conn) +int knot_quic_handle(knot_xquic_table_t *table, knot_quic_reply_t *reply, + uint64_t idle_timeout, knot_xquic_conn_t **out_conn) { *out_conn = NULL; @@ -743,13 +752,14 @@ int knot_xquic_handle(knot_xquic_table_t *table, knot_xdp_msg_t *msg, uint64_t i ngtcp2_cid scid = { 0 }, dcid = { 0 }, odcid = { 0 }; uint64_t now = get_timestamp(); int ret = ngtcp2_pkt_decode_version_cid(&decoded_cids, - msg->payload.iov_base, - msg->payload.iov_len, + reply->in_payload->iov_base, + reply->in_payload->iov_len, SERVER_DEFAULT_SCIDLEN); if (ret == NGTCP2_ERR_VERSION_NEGOTIATION) { - return -XQUIC_SEND_VERSION_NEGOTIATION; + ret = -XQUIC_SEND_VERSION_NEGOTIATION; + goto finish; } else if (ret != NGTCP2_NO_ERROR) { - return ret; + goto finish; } ngtcp2_cid_init(&dcid, decoded_cids.dcid, decoded_cids.dcidlen); ngtcp2_cid_init(&scid, decoded_cids.scid, decoded_cids.scidlen); @@ -757,40 +767,48 @@ int knot_xquic_handle(knot_xquic_table_t *table, knot_xdp_msg_t *msg, uint64_t i knot_xquic_conn_t *xconn = xquic_table_lookup(&dcid, table); if (decoded_cids.version == 0 /* short header */ && xconn == NULL) { - return KNOT_EOK; // NOOP + ret = KNOT_EOK; // NOOP + goto finish; } ngtcp2_path path; - path.remote.addr = (struct sockaddr *)&msg->ip_from; - path.remote.addrlen = addr_len(&msg->ip_from); - path.local.addr = (struct sockaddr *)&msg->ip_to; - path.local.addrlen = addr_len(&msg->ip_to); + path.remote.addr = (struct sockaddr *)reply->ip_rem; + path.remote.addrlen = addr_len((struct sockaddr_in6 *)reply->ip_rem); + path.local.addr = (struct sockaddr *)reply->ip_loc; + path.local.addrlen = addr_len((struct sockaddr_in6 *)reply->ip_loc); if (xconn == NULL) { // new conn ngtcp2_pkt_hd header = { 0 }; - ret = ngtcp2_accept(&header, msg->payload.iov_base, msg->payload.iov_len); + ret = ngtcp2_accept(&header, reply->in_payload->iov_base, + reply->in_payload->iov_len); if (ret == NGTCP2_ERR_RETRY) { - return -XQUIC_SEND_RETRY; + ret = -XQUIC_SEND_RETRY; + goto finish; } else if (ret != NGTCP2_NO_ERROR) { // discard packet - return KNOT_EOK; + ret = KNOT_EOK; + goto finish; } assert(header.type == NGTCP2_PKT_INITIAL); if (header.tokenlen == 0 && xquic_require_retry(table)) { - return -XQUIC_SEND_RETRY; + ret = -XQUIC_SEND_RETRY; + goto finish; } if (header.tokenlen > 0) { ret = ngtcp2_crypto_verify_retry_token( &odcid, header.token, header.tokenlen, - (const uint8_t *)table->hash_secret, sizeof(table->hash_secret), header.version, - (const struct sockaddr *)&msg->ip_from, addr_len(&msg->ip_from), + (const uint8_t *)table->hash_secret, + sizeof(table->hash_secret), header.version, + (const struct sockaddr *)reply->ip_rem, + addr_len((struct sockaddr_in6 *)reply->ip_rem), &dcid, idle_timeout, now // NOTE setting retry token validity to idle_timeout for simplicity ); if (ret != 0) { - return KNOT_EOK; + ret = KNOT_EOK; + goto finish; } } else { memcpy(&odcid, &dcid, sizeof(odcid)); @@ -798,42 +816,51 @@ int knot_xquic_handle(knot_xquic_table_t *table, knot_xdp_msg_t *msg, uint64_t i // server chooses his CID to his liking if (!init_unique_cid(&dcid, 0, table)) { - return KNOT_ERROR; + ret = KNOT_ERROR; + goto finish; } xconn = xquic_table_add(NULL, &dcid, table); if (xconn == NULL) { - return ENOMEM; + ret = KNOT_ENOMEM; + goto finish; } xquic_conn_mark_used(xconn, table, now); - ret = conn_new(&xconn->conn, &path, &dcid, &scid, &odcid, decoded_cids.version, now, - table->udp_payload_limit, idle_timeout, xconn, true, header.tokenlen > 0); + ret = conn_new(&xconn->conn, &path, &dcid, &scid, &odcid, decoded_cids.version, + now, table->udp_payload_limit, idle_timeout, xconn, true, + header.tokenlen > 0); if (ret >= 0) { ret = tls_init_conn_session(xconn, true); } if (ret < 0) { knot_xquic_table_rem(xconn, table); - return ret; + goto finish; } } ngtcp2_pkt_info pi = { .ecn = NGTCP2_ECN_NOT_ECT, }; - ret = ngtcp2_conn_read_pkt(xconn->conn, &path, &pi, msg->payload.iov_base, msg->payload.iov_len, now); + ret = ngtcp2_conn_read_pkt(xconn->conn, &path, &pi, reply->in_payload->iov_base, + reply->in_payload->iov_len, now); *out_conn = xconn; if (ret == NGTCP2_ERR_DRAINING // received CONNECTION_CLOSE from the counterpart || ngtcp2_err_is_fatal(ret)) { // connection doomed knot_xquic_table_rem(xconn, table); - return KNOT_ECONN; + ret = KNOT_ECONN; + goto finish; } else if (ret != NGTCP2_NO_ERROR) { // non-fatal error, discard packet - return KNOT_EOK; + ret = KNOT_EOK; + goto finish; } xquic_conn_mark_used(xconn, table, now); - return KNOT_EOK; + ret = KNOT_EOK; +finish: + reply->handle_ret = ret; + return ret; } static bool stream_exists(knot_xquic_conn_t *xconn, int64_t stream_id) @@ -842,9 +869,9 @@ static bool stream_exists(knot_xquic_conn_t *xconn, int64_t stream_id) return (ngtcp2_conn_set_stream_user_data(xconn->conn, stream_id, NULL) == NGTCP2_NO_ERROR); } -static int send_stream(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock, - knot_xdp_msg_t *in_msg, knot_xquic_conn_t *relay, int64_t stream_id, - uint8_t *data, size_t len, bool fin, ngtcp2_ssize *sent) +static int send_stream(knot_xquic_table_t *quic_table, knot_quic_reply_t *rpl, + knot_xquic_conn_t *relay, int64_t stream_id, + uint8_t *data, size_t len, bool fin, ngtcp2_ssize *sent) { (void)quic_table; assert(stream_id >= 0 || (data == NULL && len == 0)); @@ -858,40 +885,37 @@ static int send_stream(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock, assert((bool)(opened == stream_id) == stream_exists(relay, stream_id)); } - uint32_t xdp_sent = 0; - knot_xdp_msg_t out_msg = { 0 }; - int ret = knot_xdp_reply_alloc(sock, in_msg, &out_msg); + int ret = rpl->alloc_reply(rpl); if (ret != KNOT_EOK) { return ret; } - uint32_t fl = ((stream_id >= 0 && fin) ? NGTCP2_WRITE_STREAM_FLAG_FIN : NGTCP2_WRITE_STREAM_FLAG_NONE); + uint32_t fl = ((stream_id >= 0 && fin) ? NGTCP2_WRITE_STREAM_FLAG_FIN : + NGTCP2_WRITE_STREAM_FLAG_NONE); ngtcp2_vec vec = { .base = data, .len = len }; - ret = ngtcp2_conn_writev_stream(relay->conn, NULL, NULL, out_msg.payload.iov_base, out_msg.payload.iov_len, - sent, fl, stream_id, &vec, (stream_id >= 0 ? 1 : 0), get_timestamp()); + ret = ngtcp2_conn_writev_stream(relay->conn, NULL, NULL, rpl->out_payload->iov_base, + rpl->out_payload->iov_len, sent, fl, stream_id, + &vec, (stream_id >= 0 ? 1 : 0), get_timestamp()); if (ret <= 0) { - knot_xdp_send_free(sock, &out_msg, 1); + rpl->free_reply(rpl); return ret; } if (*sent < 0) { *sent = 0; } - out_msg.payload.iov_len = ret; - ret = knot_xdp_send(sock, &out_msg, 1, &xdp_sent); + rpl->out_payload->iov_len = ret; + ret = rpl->send_reply(rpl); if (ret == KNOT_EOK) { - assert(xdp_sent == 1); return 1; } return ret; } -static int send_special(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock, - knot_xdp_msg_t *in_msg, int handle_ret) +static int send_special(knot_xquic_table_t *quic_table, knot_quic_reply_t *rpl) { - knot_xdp_msg_t out_msg; - int ret = knot_xdp_reply_alloc(sock, in_msg, &out_msg); + int ret = rpl->alloc_reply(rpl); if (ret != KNOT_EOK) { return ret; } @@ -901,8 +925,8 @@ static int send_special(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock, ngtcp2_cid scid = { 0 }, dcid = { 0 }; int dvc_ret = ngtcp2_pkt_decode_version_cid(&decoded_cids, - in_msg->payload.iov_base, - in_msg->payload.iov_len, + rpl->in_payload->iov_base, + rpl->in_payload->iov_len, SERVER_DEFAULT_SCIDLEN); uint8_t rnd = 0; @@ -914,13 +938,14 @@ static int send_special(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock, uint8_t sreset_rand[NGTCP2_MIN_STATELESS_RESET_RANDLEN]; dnssec_random_buffer(sreset_rand, sizeof(sreset_rand)); - switch (handle_ret) { + switch (rpl->handle_ret) { case -XQUIC_SEND_VERSION_NEGOTIATION: if (dvc_ret != NGTCP2_ERR_VERSION_NEGOTIATION) { + rpl->free_reply(rpl); return KNOT_ERROR; } ret = ngtcp2_pkt_write_version_negotiation( - out_msg.payload.iov_base, out_msg.payload.iov_len, + rpl->out_payload->iov_base, rpl->out_payload->iov_len, rnd, decoded_cids.scid, decoded_cids.scidlen, decoded_cids.dcid, decoded_cids.dcidlen, supported_quic, sizeof(supported_quic) / sizeof(*supported_quic) @@ -933,21 +958,23 @@ static int send_special(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock, init_random_cid(&new_dcid, 0); ret = ngtcp2_crypto_generate_retry_token( - retry_token, (const uint8_t *)quic_table->hash_secret, sizeof(quic_table->hash_secret), decoded_cids.version, - (const struct sockaddr *)&in_msg->ip_from, sockaddr_len((const struct sockaddr_storage *)&in_msg->ip_from), + retry_token, (const uint8_t *)quic_table->hash_secret, + sizeof(quic_table->hash_secret), decoded_cids.version, + (const struct sockaddr *)rpl->ip_rem, sockaddr_len(rpl->ip_rem), &new_dcid, &dcid, now ); if (ret >= 0) { ret = ngtcp2_crypto_write_retry( - out_msg.payload.iov_base, out_msg.payload.iov_len, - decoded_cids.version, &scid, &new_dcid, &dcid, retry_token, ret + rpl->out_payload->iov_base, rpl->out_payload->iov_len, + decoded_cids.version, &scid, &new_dcid, &dcid, + retry_token, ret ); } break; case -XQUIC_SEND_STATELESS_RESET: ret = ngtcp2_pkt_write_stateless_reset( - out_msg.payload.iov_base, out_msg.payload.iov_len, + rpl->out_payload->iov_base, rpl->out_payload->iov_len, stateless_reset_token, sreset_rand, sizeof(sreset_rand) ); break; @@ -957,45 +984,43 @@ static int send_special(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock, } if (ret < 0) { - knot_xdp_send_free(sock, &out_msg, 1); + rpl->free_reply(rpl); } else { - uint32_t sent; - out_msg.payload.iov_len = ret; - ret = knot_xdp_send(sock, &out_msg, 1, &sent); + rpl->out_payload->iov_len = ret; + ret = rpl->send_reply(rpl); } return ret; } _public_ -int knot_xquic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *relay, - knot_xdp_socket_t *sock, knot_xdp_msg_t *in_msg, - int handle_ret, unsigned max_msgs, bool ignore_lastbyte) +int knot_quic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *conn, + knot_quic_reply_t *reply, unsigned max_msgs, bool ignore_lastbyte) { - if (handle_ret < 0) { - return handle_ret; - } else if (handle_ret > 0) { - return send_special(quic_table, sock, in_msg, handle_ret); - } else if (relay == NULL) { + if (reply->handle_ret < 0) { + return reply->handle_ret; + } else if (reply->handle_ret > 0) { + return send_special(quic_table, reply); + } else if (conn == NULL) { return KNOT_EINVAL; - } else if (relay->conn == NULL) { + } else if (conn->conn == NULL) { return KNOT_EOK; } unsigned sent_msgs = 0, stream_msgs = 0; int ret = 1; - for (int64_t si = 0; si < relay->streams_count && sent_msgs < max_msgs; /* NO INCREMENT */) { - int64_t stream_id = 4 * (relay->streams_first + si); + for (int64_t si = 0; si < conn->streams_count && sent_msgs < max_msgs; /* NO INCREMENT */) { + int64_t stream_id = 4 * (conn->streams_first + si); ngtcp2_ssize sent = 0; - size_t uf = relay->streams[si].unsent_offset; - knot_xquic_obuf_t *uo = relay->streams[si].unsent_obuf; + size_t uf = conn->streams[si].unsent_offset; + knot_xquic_obuf_t *uo = conn->streams[si].unsent_obuf; if (uo == NULL) { si++; continue; } bool fin = (((node_t *)uo->node.next)->next == NULL) && !ignore_lastbyte; - ret = send_stream(quic_table, sock, in_msg, relay, stream_id, + ret = send_stream(quic_table, reply, conn, stream_id, uo->buf + uf, uo->len - uf - (ignore_lastbyte ? 1 : 0), fin, &sent); if (ret < 0) { @@ -1008,10 +1033,10 @@ int knot_xquic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *relay, sent++; } if (sent > 0) { - knot_xquic_stream_mark_sent(relay, stream_id, sent); + knot_xquic_stream_mark_sent(conn, stream_id, sent); } - if (stream_msgs >= max_msgs / relay->streams_count) { + if (stream_msgs >= max_msgs / conn->streams_count) { stream_msgs = 0; si++; // if this stream is sending too much, give chance to other streams } @@ -1019,7 +1044,7 @@ int knot_xquic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *relay, while (ret == 1) { ngtcp2_ssize unused = 0; - ret = send_stream(quic_table, sock, in_msg, relay, -1, NULL, 0, false, &unused); + ret = send_stream(quic_table, reply, conn, -1, NULL, 0, false, &unused); } return ret; diff --git a/src/libknot/xdp/quic.h b/src/libknot/xdp/quic.h index 943a2f371c..c4af674b55 100644 --- a/src/libknot/xdp/quic.h +++ b/src/libknot/xdp/quic.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 CZ.NIC, z.s.p.o. +/* Copyright (C) 2023 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,11 +26,26 @@ #pragma once #include "libknot/xdp/quic_conn.h" -#include "libknot/xdp/xdp.h" struct knot_quic_creds; struct knot_quic_session; +typedef struct knot_quic_reply { + const struct sockaddr_storage *ip_rem; + const struct sockaddr_storage *ip_loc; + struct iovec *in_payload; + struct iovec *out_payload; + void *in_ctx; + void *out_ctx; + + void *sock; + int handle_ret; + + int (*alloc_reply)(struct knot_quic_reply *); + int (*send_reply)(struct knot_quic_reply *); + void (*free_reply)(struct knot_quic_reply *); +} knot_quic_reply_t; + /*! * \brief Gets data needed for session resumption. * @@ -104,31 +119,28 @@ int knot_xquic_client(knot_xquic_table_t *table, struct sockaddr_in6 *dest, /*! * \brief Handle incoming QUIC packet. * - * \param table QUIC connectoins table- - * \param msg Incoming XDP packet. + * \param table QUIC connectoins table. + * \param reply Incoming packet info. * \param idle_timeout Configured idle timeout for connections (in nanoseconds). * \param out_conn Out: QUIC connection that this packet belongs to. * - * \return KNOT_E* + * \return KNOT_E* or -XQUIC_SEND_* */ -int knot_xquic_handle(knot_xquic_table_t *table, knot_xdp_msg_t *msg, - uint64_t idle_timeout, knot_xquic_conn_t **out_conn); +int knot_quic_handle(knot_xquic_table_t *table, knot_quic_reply_t *reply, + uint64_t idle_timeout, knot_xquic_conn_t **out_conn); /*! * \brief Send outgoing QUIC packet(s) for a connection. * * \param quic_table QUIC connection table. - * \param relay QUIC connection. - * \param sock XDP socket. - * \param in_msg Previous incomming packet for this connection. - * \param handle_ret Error returned from knot_xquic_handle() for incoming packet. + * \param conn QUIC connection. + * \param reply Incoming/outgoing packet info. * \param max_msgs Maxmimum packets to be sent. * \param ignore_lastbyte Cut off last byte of QUIC paylod. * * \return KNOT_E* */ -int knot_xquic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *relay, - knot_xdp_socket_t *sock, knot_xdp_msg_t *in_msg, - int handle_ret, unsigned max_msgs, bool ignore_lastbyte); +int knot_quic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *conn, + knot_quic_reply_t *reply, unsigned max_msgs, bool ignore_lastbyte); /*! @} */ diff --git a/src/libknot/xdp/quic_conn.h b/src/libknot/xdp/quic_conn.h index 20ee17653e..ab44e581b9 100644 --- a/src/libknot/xdp/quic_conn.h +++ b/src/libknot/xdp/quic_conn.h @@ -25,7 +25,6 @@ #pragma once -#include #include #include #include diff --git a/src/utils/kxdpgun/main.c b/src/utils/kxdpgun/main.c index 2ec0c0c064..4e3a2677fc 100644 --- a/src/utils/kxdpgun/main.c +++ b/src/utils/kxdpgun/main.c @@ -424,6 +424,49 @@ inline static bool check_dns_payload(struct iovec *payl, xdp_gun_ctx_t *ctx, return true; } +#ifdef ENABLE_QUIC +static int quic_alloc_cb(knot_quic_reply_t *rpl) +{ + xdp_gun_ctx_t *ctx = rpl->in_ctx; + knot_xdp_msg_t *msg = rpl->out_ctx; + + unsigned flags = ctx->ipv6 ? KNOT_XDP_MSG_IPV6 : 0; + if (ctx->vlan_tci != 0) { + flags |= KNOT_XDP_MSG_VLAN; + } + + int ret = knot_xdp_send_alloc(rpl->sock, flags, msg); + if (ret != KNOT_EOK) { + return ret; + } + + memcpy(msg->eth_from, ctx->local_mac, sizeof(ctx->local_mac)); + memcpy(msg->eth_to, ctx->target_mac, sizeof(ctx->target_mac)); + memcpy(&msg->ip_from, &ctx->local_ip, sizeof(msg->ip_from)); + memcpy(&msg->ip_to, &ctx->target_ip, sizeof(msg->ip_to)); + + msg->vlan_tci = ctx->vlan_tci; + + return KNOT_EOK; +} + +static int quic_reply_alloc_cb(knot_quic_reply_t *rpl) +{ + return knot_xdp_reply_alloc(rpl->sock, rpl->in_ctx, rpl->out_ctx); +} + +static int quic_send_cb(knot_quic_reply_t *rpl) +{ + uint32_t sent = 0; + return knot_xdp_send(rpl->sock, rpl->out_ctx, 1, &sent); +} + +static void quic_free_cb(knot_quic_reply_t *rpl) +{ + knot_xdp_send_free(rpl->sock, rpl->out_ctx, 1); +} +#endif // ENABLE_QUIC + void *xdp_gun_thread(void *_ctx) { xdp_gun_ctx_t *ctx = _ctx; @@ -437,9 +480,9 @@ void *xdp_gun_thread(void *_ctx) #ifdef ENABLE_QUIC knot_xquic_table_t *quic_table = NULL; struct knot_quic_creds *quic_creds = NULL; - knot_xdp_msg_t quic_fake_req = { 0 }; list_t quic_sessions; init_list(&quic_sessions); + knot_quic_reply_t replies[ctx->at_once]; #endif // ENABLE_QUIC const uint64_t extra_wait = ctx->quic ? 4000000 : 1000000; @@ -462,13 +505,6 @@ void *xdp_gun_thread(void *_ctx) ERR2("failed to allocate QUIC connection table"); return NULL; } - ctx->target_ip.sin6_port = htobe16(ctx->target_port); - - memcpy(quic_fake_req.eth_from, ctx->target_mac, sizeof(ctx->target_mac)); - memcpy(quic_fake_req.eth_to, ctx->local_mac, sizeof(ctx->local_mac)); - memcpy(&quic_fake_req.ip_from, &ctx->target_ip, sizeof(quic_fake_req.ip_from)); - memcpy(&quic_fake_req.ip_to, &ctx->local_ip, sizeof(quic_fake_req.ip_to)); - quic_fake_req.flags = ctx->ipv6 ? KNOT_XDP_MSG_IPV6 : 0; #else assert(0); #endif // ENABLE_QUIC @@ -512,7 +548,30 @@ void *xdp_gun_thread(void *_ctx) next_payload(&payload_ptr, ctx->thread_id); #ifdef ENABLE_QUIC + knot_quic_reply_t send_reply = { + .out_payload = &pkts->payload, + .in_ctx = ctx, + .out_ctx = pkts, + .sock = xsk, + .alloc_reply = quic_alloc_cb, + .send_reply = quic_send_cb, + .free_reply = quic_free_cb, + }; + knot_xdp_msg_t msg_out; + for (int i = 0; i < ctx->at_once; i++) { + knot_quic_reply_t *reply = &replies[i]; + memset(reply, 0, sizeof(*reply)); + reply->out_payload = &msg_out.payload; + reply->out_ctx = &msg_out; + reply->sock = xsk; + reply->alloc_reply = quic_reply_alloc_cb; + reply->send_reply = quic_send_cb; + reply->free_reply = quic_free_cb; + } + + ctx->target_ip.sin6_port = htobe16(ctx->target_port); knot_sweep_stats_t sweep_stats = { 0 }; + uint16_t local_ports[QUIC_THREAD_PORTS]; uint16_t port = LOCAL_PORT_MIN; for (int i = 0; i < QUIC_THREAD_PORTS; ++i) { @@ -546,12 +605,16 @@ void *xdp_gun_thread(void *_ctx) } else if (ctx->quic) { #ifdef ENABLE_QUIC uint16_t local_port = local_ports[local_ports_it++ % QUIC_THREAD_PORTS]; + ctx->local_ip.sin6_port = htobe16(local_port); + for (unsigned i = 0; i < ctx->at_once; i++) { knot_xquic_conn_t *newconn = NULL; - ctx->local_ip.sin6_port = htobe16(local_port); ret = knot_xquic_client(quic_table, &ctx->target_ip, &ctx->local_ip, &newconn); if (ret == KNOT_EOK) { - struct iovec tmp = { knot_xquic_stream_add_data(newconn, 0, NULL, payload_ptr->len), 0 }; + struct iovec tmp = { + knot_xquic_stream_add_data(newconn, 0, NULL, payload_ptr->len), + 0 + }; put_dns_payload(&tmp, false, ctx, &payload_ptr); if (EMPTY_LIST(quic_sessions)) { newconn->streams_count = -1; @@ -560,8 +623,8 @@ void *xdp_gun_thread(void *_ctx) rem_node(session); (void)knot_xquic_session_load(newconn, session); } - quic_fake_req.ip_to.sin6_port = htobe16(local_port); - ret = knot_xquic_send(quic_table, newconn, xsk, &quic_fake_req, KNOT_EOK, 1, (ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE)); + ret = knot_quic_send(quic_table, newconn, &send_reply, 1, + (ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE)); } if (ret == KNOT_EOK) { local_stats.qry_sent++; @@ -659,8 +722,17 @@ void *xdp_gun_thread(void *_ctx) } else if (ctx->quic) { #ifdef ENABLE_QUIC knot_xquic_conn_t *relays[recvd]; + for (size_t i = 0; i < recvd; i++) { - ret = knot_xquic_handle(quic_table, &pkts[i], 5000000000L, &relays[i]); + knot_xdp_msg_t *msg_in = &pkts[i]; + knot_quic_reply_t *reply = &replies[i]; + + reply->ip_rem = (struct sockaddr_storage *)&msg_in->ip_from; + reply->ip_loc = (struct sockaddr_storage *)&msg_in->ip_to; + reply->in_payload = &msg_in->payload; + reply->in_ctx = msg_in; + + ret = knot_quic_handle(quic_table, &replies[i], 5000000000L, &relays[i]); if (ret == KNOT_ECONN) { local_stats.rst_recv++; continue; @@ -723,7 +795,8 @@ void *xdp_gun_thread(void *_ctx) continue; } } - ret = knot_xquic_send(quic_table, rl, xsk, &pkts[i], KNOT_EOK, 4, (ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE)); + ret = knot_quic_send(quic_table, rl, &replies[i], 4, + (ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE)); if (ret != KNOT_EOK) { errors++; }