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

openSUSE Build Service is sponsored by