Mercurial > hg > nginx
view src/event/quic/ngx_event_quic_connection.h @ 8913:40445fc7c403 quic
QUIC: fixed migration during NAT rebinding.
The RFC 9000 allows a packet from known CID arrive from unknown path:
These requirements regarding connection ID reuse apply only to the
sending of packets, as unintentional changes in path without a change
in connection ID are possible. For example, after a period of
network inactivity, NAT rebinding might cause packets to be sent on a
new path when the client resumes sending.
Before the patch, such packets were rejected with an error in the
ngx_quic_check_migration() function. Removing the check makes the
separate function excessive - remaining checks are early migration
check and "disable_active_migration" check. The latter is a transport
parameter sent to client and it should not be used by server.
The server should send "disable_active_migration" "if the endpoint does
not support active connection migration" (18.2). The support status depends
on nginx configuration: to have migration working with multiple workers,
you need bpf helper, available on recent Linux systems. The patch does
not set "disable_active_migration" automatically and leaves it for the
administrator. By default, active migration is enabled.
RFC 900 says that it is ok to migrate if the peer violates
"disable_active_migration" flag requirements:
If the peer violates this requirement,
the endpoint MUST either drop the incoming packets on that path without
generating a Stateless Reset
OR
proceed with path validation and allow the peer to migrate. Generating a
Stateless Reset or closing the connection would allow third parties in the
network to cause connections to close by spoofing or otherwise manipulating
observed traffic.
So, nginx adheres to the second option and proceeds to path validation.
Note:
The ngtcp2 may be used for testing both active migration and NAT rebinding:
ngtcp2/client --change-local-addr=200ms --delay-stream=500ms <ip> <port> <url>
ngtcp2/client --change-local-addr=200ms --delay-stream=500ms --nat-rebinding \
<ip> <port> <url>
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Mon, 29 Nov 2021 11:51:14 +0300 |
parents | 1d7bf9778328 |
children | ddd5e5c0f87d |
line wrap: on
line source
/* * Copyright (C) Nginx, Inc. */ #ifndef _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_ #define _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> /* #define NGX_QUIC_DEBUG_PACKETS */ /* dump packet contents */ /* #define NGX_QUIC_DEBUG_FRAMES */ /* dump frames contents */ /* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */ /* #define NGX_QUIC_DEBUG_CRYPTO */ typedef struct ngx_quic_connection_s ngx_quic_connection_t; typedef struct ngx_quic_server_id_s ngx_quic_server_id_t; typedef struct ngx_quic_client_id_s ngx_quic_client_id_t; typedef struct ngx_quic_send_ctx_s ngx_quic_send_ctx_t; typedef struct ngx_quic_socket_s ngx_quic_socket_t; typedef struct ngx_quic_path_s ngx_quic_path_t; typedef struct ngx_quic_keys_s ngx_quic_keys_t; #include <ngx_event_quic_transport.h> #include <ngx_event_quic_protection.h> #include <ngx_event_quic_frames.h> #include <ngx_event_quic_migration.h> #include <ngx_event_quic_connid.h> #include <ngx_event_quic_streams.h> #include <ngx_event_quic_ssl.h> #include <ngx_event_quic_tokens.h> #include <ngx_event_quic_ack.h> #include <ngx_event_quic_output.h> #include <ngx_event_quic_socket.h> /* RFC 9002, 6.2.2. Handshakes and New Paths: kInitialRtt */ #define NGX_QUIC_INITIAL_RTT 333 /* ms */ #define NGX_QUIC_UNSET_PN (uint64_t) -1 #define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1) /* 0-RTT and 1-RTT data exist in the same packet number space, * so we have 3 packet number spaces: * * 0 - Initial * 1 - Handshake * 2 - 0-RTT and 1-RTT */ #define ngx_quic_get_send_ctx(qc, level) \ ((level) == ssl_encryption_initial) ? &((qc)->send_ctx[0]) \ : (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1]) \ : &((qc)->send_ctx[2])) #define ngx_quic_get_connection(c) \ (((c)->udp) ? (((ngx_quic_socket_t *)((c)->udp))->quic) : NULL) #define ngx_quic_get_socket(c) ((ngx_quic_socket_t *)((c)->udp)) struct ngx_quic_client_id_s { ngx_queue_t queue; uint64_t seqnum; size_t len; u_char id[NGX_QUIC_CID_LEN_MAX]; u_char sr_token[NGX_QUIC_SR_TOKEN_LEN]; ngx_uint_t refcnt; }; struct ngx_quic_server_id_s { uint64_t seqnum; size_t len; u_char id[NGX_QUIC_CID_LEN_MAX]; }; struct ngx_quic_path_s { ngx_queue_t queue; struct sockaddr *sockaddr; socklen_t socklen; ngx_uint_t state; ngx_msec_t expires; ngx_uint_t tries; off_t sent; off_t received; u_char challenge1[8]; u_char challenge2[8]; ngx_uint_t refcnt; uint64_t seqnum; time_t validated_at; ngx_str_t addr_text; u_char text[NGX_SOCKADDR_STRLEN]; }; struct ngx_quic_socket_s { ngx_udp_connection_t udp; ngx_quic_connection_t *quic; ngx_queue_t queue; ngx_quic_server_id_t sid; ngx_quic_path_t *path; ngx_quic_client_id_t *cid; }; typedef struct { ngx_rbtree_t tree; ngx_rbtree_node_t sentinel; ngx_queue_t uninitialized; uint64_t sent; uint64_t recv_offset; uint64_t recv_window; uint64_t recv_last; uint64_t recv_max_data; uint64_t send_max_data; uint64_t server_max_streams_uni; uint64_t server_max_streams_bidi; uint64_t server_streams_uni; uint64_t server_streams_bidi; uint64_t client_max_streams_uni; uint64_t client_max_streams_bidi; uint64_t client_streams_uni; uint64_t client_streams_bidi; ngx_uint_t initialized; /* unsigned initialized:1; */ } ngx_quic_streams_t; typedef struct { size_t in_flight; size_t window; size_t ssthresh; ngx_msec_t recovery_start; } ngx_quic_congestion_t; /* * RFC 9000, 12.3. Packet Numbers * * Conceptually, a packet number space is the context in which a packet * can be processed and acknowledged. Initial packets can only be sent * with Initial packet protection keys and acknowledged in packets that * are also Initial packets. */ struct ngx_quic_send_ctx_s { enum ssl_encryption_level_t level; ngx_chain_t *crypto; uint64_t crypto_received; uint64_t crypto_sent; uint64_t pnum; /* to be sent */ uint64_t largest_ack; /* received from peer */ uint64_t largest_pn; /* received from peer */ ngx_queue_t frames; /* generated frames */ ngx_queue_t sending; /* frames assigned to pkt */ ngx_queue_t sent; /* frames waiting ACK */ uint64_t pending_ack; /* non sent ack-eliciting */ uint64_t largest_range; uint64_t first_range; ngx_msec_t largest_received; ngx_msec_t ack_delay_start; ngx_uint_t nranges; ngx_quic_ack_range_t ranges[NGX_QUIC_MAX_RANGES]; ngx_uint_t send_ack; }; struct ngx_quic_connection_s { uint32_t version; ngx_quic_socket_t *socket; ngx_quic_socket_t *backup; ngx_queue_t sockets; ngx_queue_t paths; ngx_queue_t client_ids; ngx_queue_t free_sockets; ngx_queue_t free_paths; ngx_queue_t free_client_ids; ngx_uint_t nsockets; ngx_uint_t nclient_ids; uint64_t max_retired_seqnum; uint64_t client_seqnum; uint64_t server_seqnum; uint64_t path_seqnum; ngx_quic_tp_t tp; ngx_quic_tp_t ctp; ngx_quic_send_ctx_t send_ctx[NGX_QUIC_SEND_CTX_LAST]; ngx_quic_keys_t *keys; ngx_quic_conf_t *conf; ngx_event_t push; ngx_event_t pto; ngx_event_t close; ngx_event_t path_validation; ngx_msec_t last_cc; ngx_msec_t first_rtt; ngx_msec_t latest_rtt; ngx_msec_t avg_rtt; ngx_msec_t min_rtt; ngx_msec_t rttvar; ngx_uint_t pto_count; ngx_queue_t free_frames; ngx_chain_t *free_bufs; ngx_buf_t *free_shadow_bufs; ngx_uint_t nframes; #ifdef NGX_QUIC_DEBUG_ALLOC ngx_uint_t nbufs; #endif ngx_quic_streams_t streams; ngx_quic_congestion_t congestion; off_t received; ngx_uint_t error; enum ssl_encryption_level_t error_level; ngx_uint_t error_ftype; const char *error_reason; ngx_uint_t shutdown_code; const char *shutdown_reason; unsigned error_app:1; unsigned send_timer_set:1; unsigned closing:1; unsigned shutdown:1; unsigned draining:1; unsigned key_phase:1; unsigned validated:1; unsigned client_tp_done:1; }; ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp); void ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level); void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc); void ngx_quic_shutdown_quic(ngx_connection_t *c); #if (NGX_DEBUG) void ngx_quic_connstate_dbg(ngx_connection_t *c); #else #define ngx_quic_connstate_dbg(c) #endif #endif /* _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_ */