diff options
Diffstat (limited to 'lib/ssl/test/inet_epmd_cryptcookie_inet_ktls.erl')
-rw-r--r-- | lib/ssl/test/inet_epmd_cryptcookie_inet_ktls.erl | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/lib/ssl/test/inet_epmd_cryptcookie_inet_ktls.erl b/lib/ssl/test/inet_epmd_cryptcookie_inet_ktls.erl new file mode 100644 index 0000000000..d350cc8139 --- /dev/null +++ b/lib/ssl/test/inet_epmd_cryptcookie_inet_ktls.erl @@ -0,0 +1,230 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2023. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% ------------------------------------------------------------------------- +%% +%% Plug-in module for inet_epmd distribution +%% with cryptcookie over inet_tcp with kTLS offloading +%% +-module(inet_epmd_cryptcookie_inet_ktls). +-feature(maybe_expr, enable). + +%% DistMod API +-export([net_address/0, listen_open/2, listen_port/3, listen_close/1, + accept_open/2, accept_controller/3, accepted/3, + connect/3]). + +-export([supported/0]). + +%% Socket I/O Stream internal exports (export entry fun()s) +-export([stream_recv/2, stream_send/2, + stream_controlling_process/2]). + +-include_lib("kernel/include/net_address.hrl"). +-include_lib("kernel/include/dist.hrl"). +-include_lib("kernel/include/dist_util.hrl"). + +-define(FAMILY, inet). +-define(DRIVER, inet_tcp). + +%% ------------------------------------------------------------ +net_address() -> + Family = ?DRIVER:family(), + Protocol = cryptcookie:start_keypair_server(), + #net_address{ + protocol = Protocol, + family = Family }. + +%% ------------------------------------------------------------ +listen_open(_NetAddress, Options) -> + {ok, + inet_epmd_dist:merge_options( + Options, + [{active, false}, {mode, binary}, {packet, 0}, + inet_epmd_dist:nodelay()], + [])}. + +%% ------------------------------------------------------------ +listen_port(_NetAddress, Port, ListenOptions) -> + maybe + {ok, ListenSocket} ?= + ?DRIVER:listen(Port, ListenOptions), + {ok, Address} ?= + inet:sockname(ListenSocket), + {ok, {ListenSocket, Address}} + end. + +%% ------------------------------------------------------------ +listen_close(ListenSocket) -> + ?DRIVER:close(ListenSocket). + +%% ------------------------------------------------------------ +accept_open(_NetAddress, ListenSocket) -> + maybe + {ok, Socket} ?= + ?DRIVER:accept(ListenSocket), + {ok, {Ip, _}} ?= + inet:sockname(Socket), + {ok, {PeerIp, _} = PeerAddress} ?= + inet:peername(Socket), + inet_epmd_dist:check_ip(Ip, PeerIp), + Stream = stream(Socket), + {_Stream_1, _, CipherState} = cryptcookie:init(Stream), + KtlsInfo = + inet_ktls_info(Socket, cryptcookie:ktls_info(CipherState)), + ok ?= inet_tls_dist:set_ktls(KtlsInfo), + ok ?= inet:setopts(Socket, [{packet, 2}, {mode, list}]), + {Socket, PeerAddress} + else + {error, Reason} -> + exit({accept, Reason}) + end. + +%% ------------------------------------------------------------ +accept_controller(_NetAddress, Controller, Socket) -> + ok = ?DRIVER:controlling_process(Socket, Controller), + Socket. + +%% ------------------------------------------------------------ +accepted(NetAddress, _Timer, Socket) -> + inet_epmd_dist:hs_data(NetAddress, Socket). + +%% ------------------------------------------------------------ +connect(NetAddress, _Timer, Options) -> + ConnectOptions = + inet_epmd_dist:merge_options( + Options, + [{active, false}, {mode, binary}, {packet, 0}, + inet_epmd_dist:nodelay()], + []), + #net_address{ address = {Ip, Port} } = NetAddress, + maybe + {ok, Socket} ?= + ?DRIVER:connect(Ip, Port, ConnectOptions), + Stream = stream(Socket), + {_Stream_1, _, CipherState} = cryptcookie:init(Stream), + KtlsInfo = + inet_ktls_info(Socket, cryptcookie:ktls_info(CipherState)), + ok ?= inet_tls_dist:set_ktls(KtlsInfo), + ok ?= inet:setopts(Socket, [{packet, 2}, {mode, list}]), + inet_epmd_dist:hs_data(NetAddress, Socket) + else + {error, _} = Error -> + Error + end. + +%% ------------------------------------------------------------ +%% A socket as an I/O Stream +%% +%% Stream :: {InStream, OutStream, ControllingProcessFun}. +%% +%% InStream :: [InFun | InState]. +%% InFun :: fun (InStream, Size) -> +%% [Data | NewInStream] | +%% [closed | DebugTerm] +%% NewInStream :: InStream +%% %% If Size == 0 and there is no pending input data; +%% %% return immediately with empty Data, +%% %% otherwise wait for Size bytes of data +%% %% or any amount of data > 0 +%% +%% OutStream :: [OutFun | OutState] +%% OutFun :: fun (OutStream, Data) -> +%% NewOutStream | +%% [closed | DebugTerm] +%% NewOutStream :: OutStream +%% +%% Data :: binary() or list(binary()) +%% +%% ControllingProcessFun :: fun (Stream, pid()) -> NewStream +%% +%% NewSTream :: Stream + +stream(Socket) -> + {stream_in(Socket), stream_out(Socket), + fun ?MODULE:stream_controlling_process/2}. + +stream_in(Socket) -> + [fun ?MODULE:stream_recv/2 | Socket]. + +stream_recv(InStream = [_ | Socket], Size) -> + case + if + Size =:= 0 -> + ?DRIVER:recv(Socket, 0, 0); + true -> + ?DRIVER:recv(Socket, Size, infinity) + end + of + {ok, Data} -> + [Data | InStream]; + {error, timeout} -> + [<<>> | InStream]; + {error, closed} -> + [closed | InStream]; + {error, Reason} -> + erlang:error({?MODULE, ?FUNCTION_NAME, Reason}) + end. + +stream_out(Socket) -> + [fun ?MODULE:stream_send/2 | Socket]. + +stream_send(OutStream = [_ | Socket], Data) -> + case ?DRIVER:send(Socket, Data) of + ok -> + OutStream; + {error, closed} -> + [closed | OutStream]; + {error, Reason} -> + erlang:error({?MODULE, ?FUNCTION_NAME, Reason, [OutStream, Data]}) + end. + +stream_controlling_process(Stream = {_, [_ | Socket], _}, Pid) -> + %% + case ?DRIVER:controlling_process(Socket, Pid) of + ok -> + Stream; + {error, Reason} -> + erlang:error({?MODULE, ?FUNCTION_NAME, Reason}) + end. + +%% ------------------------------------------------------------ +supported() -> + maybe + ok ?= cryptcookie:supported(), + %% + {ok, Listen} = ?DRIVER:listen(0, [{active, false}]), + {ok, Port} = inet:port(Listen), + {ok, Client} = + ?DRIVER:connect({127,0,0,1}, Port, [{active, false}]), + try + inet_tls_dist:set_ktls( + inet_ktls_info(Client, cryptcookie:ktls_info())) + after + _ = ?DRIVER:close(Client), + _ = ?DRIVER:close(Listen) + end + end. + + +inet_ktls_info(Socket, KtlsInfo) -> + KtlsInfo + #{ socket => Socket, + setopt_fun => fun inet_tls_dist:inet_ktls_setopt/3, + getopt_fun => fun inet_tls_dist:inet_ktls_getopt/3 }. |