File 4599-ssl-send-fragmented-dtls-packets-separately.patch of Package erlang
From d448bcb6c284f136835cdef783a25375ddaa4ebd Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Wed, 13 Oct 2021 15:58:16 +0200
Subject: [PATCH 09/10] ssl: send fragmented dtls packets separately
Do not undo the fragmentation already done.
---
lib/ssl/src/dtls_connection.erl | 10 ++++-
lib/ssl/src/dtls_gen_connection.erl | 64 +++++++++++++++++++++--------
lib/ssl/src/dtls_record.erl | 10 +++--
3 files changed, 62 insertions(+), 22 deletions(-)
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 1e2f0eb36a..7c7a1be9d0 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -805,7 +805,7 @@ send_application_data(Data, From, _StateName,
{Msgs, ConnectionStates} =
dtls_record:encode_data(Data, Version, ConnectionStates0),
State = State0#state{connection_states = ConnectionStates},
- case dtls_gen_connection:send(Transport, Socket, Msgs) of
+ case send_msgs(Transport, Socket, Msgs) of
ok ->
ssl_logger:debug(LogLevel, outbound, 'record', Msgs),
ssl_gen_statem:hibernate_after(connection, State, [{reply, From, ok}]);
@@ -814,6 +814,14 @@ send_application_data(Data, From, _StateName,
end
end.
+send_msgs(Transport, Socket, [Msg|Msgs]) ->
+ case dtls_gen_connection:send(Transport, Socket, Msg) of
+ ok -> send_msgs(Transport, Socket, Msgs);
+ Error -> Error
+ end;
+send_msgs(_, _, []) ->
+ ok.
+
time_to_renegotiate(_Data,
#{current_write := #{sequence_number := Num}},
RenegotiateAt) ->
diff --git a/lib/ssl/src/dtls_gen_connection.erl b/lib/ssl/src/dtls_gen_connection.erl
index 12aa094191..dab05335c0 100644
--- a/lib/ssl/src/dtls_gen_connection.erl
+++ b/lib/ssl/src/dtls_gen_connection.erl
@@ -68,6 +68,14 @@
send_alert_in_connection/2,
close/5,
protocol_name/0]).
+
+
+%% See thread @ http://lists.cluenet.de/pipermail/ipv6-ops/2011-June/005755.html
+%% 1280 - headers
+-define(PMTUEstimate, 1200).
+
+
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -244,12 +252,11 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
connection_states = ConnectionStates0,
ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
- PMTUEstimate = 1400, %% TODO make configurable
#{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0,
- MaxSize = min(MaxFragmentLength, PMTUEstimate),
+ MaxSize = min(MaxFragmentLength, ?PMTUEstimate),
{Encoded, ConnectionStates} =
encode_handshake_flight(lists:reverse(Flight), Version, MaxSize, Epoch, ConnectionStates0),
- send(Transport, Socket, Encoded),
+ send_packets(Transport, Socket, Encoded),
ssl_logger:debug(LogLevel, outbound, 'record', Encoded),
{State0#state{connection_states = ConnectionStates}, []};
@@ -262,14 +269,12 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
connection_states = ConnectionStates0,
ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
- PMTUEstimate = 1400, %% TODO make configurable
#{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0,
- MaxSize = min(MaxFragmentLength, PMTUEstimate),
+ MaxSize = min(MaxFragmentLength, ?PMTUEstimate),
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, MaxSize, Epoch, ConnectionStates0),
{EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1),
-
- send(Transport, Socket, [HsBefore, EncChangeCipher]),
+ send_packets(Transport, Socket, HsBefore ++ EncChangeCipher),
ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]),
ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]),
{State0#state{connection_states = ConnectionStates}, []};
@@ -283,16 +288,15 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
connection_states = ConnectionStates0,
ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
- PMTUEstimate = 1400, %% TODO make configurable
#{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0,
- MaxSize = min(MaxFragmentLength, PMTUEstimate),
+ MaxSize = min(MaxFragmentLength, ?PMTUEstimate),
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, MaxSize, Epoch-1, ConnectionStates0),
{EncChangeCipher, ConnectionStates2} =
encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates1),
{HsAfter, ConnectionStates} =
encode_handshake_flight(lists:reverse(Flight1), Version, MaxSize, Epoch, ConnectionStates2),
- send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]),
+ send_packets(Transport, Socket, HsBefore ++ EncChangeCipher ++ HsAfter),
ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]),
ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]),
ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]),
@@ -307,14 +311,13 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
connection_states = ConnectionStates0,
ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
- PMTUEstimate = 1400, %% TODO make configurable
#{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0,
- MaxSize = min(MaxFragmentLength, PMTUEstimate),
+ MaxSize = min(MaxFragmentLength, ?PMTUEstimate),
{EncChangeCipher, ConnectionStates1} =
encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0),
{HsAfter, ConnectionStates} =
encode_handshake_flight(lists:reverse(Flight1), Version, MaxSize, Epoch, ConnectionStates1),
- send(Transport, Socket, [EncChangeCipher, HsAfter]),
+ send_packets(Transport, Socket, EncChangeCipher ++ HsAfter),
ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]),
ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]),
{State0#state{connection_states = ConnectionStates}, []}.
@@ -479,6 +482,28 @@ send(Transport, {Listener, Socket}, Data) when is_pid(Listener) ->
send(Transport, Socket, Data) -> % Client socket
dtls_socket:send(Transport, Socket, Data).
+send_packets(_Transport, _Socket, []) ->
+ ok;
+send_packets(Transport, Socket, Packets) ->
+ {Packet, Rest} = pack_packets(Packets, 0, ?PMTUEstimate+80, []),
+ case send(Transport, Socket, Packet) of
+ ok -> send_packets(Transport, Socket, Rest);
+ Err -> Err
+ end.
+
+pack_packets([P|Rest]=Packets, SoFar, Max, Acc) ->
+ Size = erlang:iolist_size(P),
+ Next = SoFar + Size,
+ if Size > Max, Acc =:= [] ->
+ {P, Rest};
+ Next < Max ->
+ pack_packets(Rest, Next, Max, [P|Acc]);
+ true ->
+ {lists:reverse(Acc), Packets}
+ end;
+pack_packets([], _, _, Acc) ->
+ {lists:reverse(Acc), []}.
+
socket(Pid, Transport, Socket, _Tracker) ->
dtls_socket:socket(Pid, Transport, Socket, ?MODULE).
@@ -577,10 +602,15 @@ unprocessed_events(Events) ->
erlang:length(Events)-1.
encode_handshake_flight(Flight, Version, MaxFragmentSize, Epoch, ConnectionStates) ->
- Fragments = lists:map(fun(Handshake) ->
- dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize)
- end, Flight),
- dtls_record:encode_handshake(Fragments, Version, Epoch, ConnectionStates).
+ Encode = fun(Fragment, {Acc, Cs0}) ->
+ {Enc, Cs} = dtls_record:encode_handshake(Fragment, Version, Epoch, Cs0),
+ {[Enc|Acc], Cs}
+ end,
+ {Rev, Cs} = lists:foldl(fun(Handshake, {Acc,Cs0}) ->
+ Frags = dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize),
+ lists:foldl(Encode, {Acc,Cs0}, Frags)
+ end, {[], ConnectionStates}, Flight),
+ {lists:reverse(Rev), Cs}.
encode_change_cipher(#change_cipher_spec{}, Version, Epoch, ConnectionStates) ->
dtls_record:encode_change_cipher_spec(Version, Epoch, ConnectionStates).
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index da8d19d6ed..f0d2a91a67 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -204,16 +204,17 @@ encode_alert_record(#alert{level = Level, description = Description},
%%--------------------------------------------------------------------
-spec encode_change_cipher_spec(ssl_record:ssl_version(), integer(), ssl_record:connection_states()) ->
- {iolist(), ssl_record:connection_states()}.
+ {[iolist()], ssl_record:connection_states()}.
%%
%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
%%--------------------------------------------------------------------
encode_change_cipher_spec(Version, Epoch, ConnectionStates) ->
- encode_plain_text(?CHANGE_CIPHER_SPEC, Version, Epoch, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates).
+ {Enc, Cs} = encode_plain_text(?CHANGE_CIPHER_SPEC, Version, Epoch, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates),
+ {[Enc], Cs}.
%%--------------------------------------------------------------------
-spec encode_data(binary(), ssl_record:ssl_version(), ssl_record:connection_states()) ->
- {iolist(),ssl_record:connection_states()}.
+ {[iolist()],ssl_record:connection_states()}.
%%
%% Description: Encodes data to send on the ssl-socket.
%%--------------------------------------------------------------------
@@ -236,7 +237,8 @@ encode_data(Data, Version, ConnectionStates) ->
end, {[], ConnectionStates}, Frags),
{lists:reverse(RevCipherText), ConnectionStates1};
_ ->
- encode_plain_text(?APPLICATION_DATA, Version, Epoch, Data, ConnectionStates)
+ {Enc, Cs} = encode_plain_text(?APPLICATION_DATA, Version, Epoch, Data, ConnectionStates),
+ {[Enc], Cs}
end.
encode_plain_text(Type, Version, Epoch, Data, ConnectionStates) ->
--
2.31.1