Add support for IPv6 clients. gh-11

parent 42eb63f2
sudo: required
language: erlang
otp_release:
- 22.0
......@@ -6,6 +7,8 @@ otp_release:
- 20.3
#- 19.3 not supported (string:lexemes/2)
#- 18.3 not supported (string:lexemes/2, tricky binary comprehension, map typespec with `:=`, ?assertEqual/3)
install:
- sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
script:
- ./rebar3 compile
- ./rebar3 xref
......
......@@ -246,6 +246,38 @@ You should disable all protocols other than `mtp_secure` by providing `allowed_p
<..>
```
### IPv6
Currently proxy only supports client connections via IPv6, but can only connect to Telegram servers
using IPv4.
To enable IPv6, you should put IPv6 address in `listen_ip` config key.
If you want proxy to accept clients on the same port with both IPv4 and IPv6, you should
have 2 `ports` sections with the same `port`, `secret` and `tag`, but with different names and
different `listen_ip` (one v4 and one v6):
```erlang
{mtproto_proxy,
%% see src/mtproto_proxy.app.src for examples.
[
{ports,
[#{name => mtp_handler_all_ipv4,
listen_ip => "0.0.0.0", % IPv4 address, eg 203.0.113.1
port => 1443,
secret => <<"d0d6e111bada5511fcce9584deadbeef">>,
tag => <<"dcbe8f1493fa4cd9ab300891c0b5b326">>},
#{name => mtp_handler_all_ipv6,
listen_ip => "::", % IPv6 address, eg "2001:db8:85a3::8a2e:370:7334"
port => 1443,
secret => <<"d0d6e111bada5511fcce9584deadbeef">>,
tag => <<"dcbe8f1493fa4cd9ab300891c0b5b326">>}
]}
]},
{lager,
<...>
```
### Tune resource consumption
If your server have low amount of RAM, try to set
......
......@@ -26,9 +26,11 @@
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export_type([netloc_v4v6/0]).
-type dc_id() :: integer().
-type netloc() :: {inet:ip4_address(), inet:port_number()}.
-type netloc_v4v6() :: {inet:ip_address(), inet:port_number()}.
-include_lib("hut/include/hut.hrl").
......
......@@ -42,7 +42,7 @@
-endif.
-type handle() :: pid().
-type upstream_opts() :: #{addr := mtp_config:netloc(), % IP/Port of TG client
-type upstream_opts() :: #{addr := mtp_config:netloc_v4v6(), % IP/Port of TG client
ad_tag => binary()}.
-type upstream() :: {
_UpsStatic ::{_ConnId :: mtp_rpc:conn_id(),
......
......@@ -49,7 +49,7 @@
dc_id :: {DcId :: integer(), Pool :: pid()} | undefined,
ad_tag :: binary(),
addr :: mtp_config:netloc(), % IP/Port of remote side
addr :: mtp_config:netloc_v4v6(), % IP/Port of remote side
started_at :: pos_integer(),
timer_state = init :: init | hibernate | stop,
timer :: gen_timeout:tout(),
......
......@@ -19,7 +19,7 @@
port := inet:port_number(),
secret := binary(),
tag := binary(),
listen_ip => inet:ip4_addr()}.
listen_ip => string()}.
%%====================================================================
%% API
......@@ -69,11 +69,16 @@ running_ports() ->
ip := Ip,
port := Port} = maps:from_list(Opts),
[Name, Secret, AdTag] = ProtoOpts,
#{name => Name,
listen_ip => inet:ntoa(Ip),
port => Port,
secret => Secret,
tag => AdTag}
case inet:ntoa(Ip) of
{error, einval} ->
error({invalid_ip, Ip});
IpAddr ->
#{name => Name,
listen_ip => IpAddr,
port => Port,
secret => Secret,
tag => AdTag}
end
end, mtp_listeners()).
%%====================================================================
......@@ -84,14 +89,19 @@ start_proxy(#{name := Name, port := Port, secret := Secret, tag := Tag} = P) ->
ListenIpStr = maps:get(
listen_ip, P,
application:get_env(?APP, listen_ip, "0.0.0.0")),
{ok, ListenIp} = inet:parse_ipv4_address(ListenIpStr),
{ok, ListenIp} = inet:parse_address(ListenIpStr),
Family = case tuple_size(ListenIp) of
4 -> inet;
8 -> inet6
end,
NumAcceptors = application:get_env(?APP, num_acceptors, 60),
MaxConnections = application:get_env(?APP, max_connections, 10240),
Res =
ranch:start_listener(
Name, ranch_tcp,
#{socket_opts => [{ip, ListenIp},
{port, Port}],
{port, Port},
Family],
num_acceptors => NumAcceptors,
max_connections => MaxConnections},
mtp_handler, [Name, Secret, Tag]),
......
......@@ -14,7 +14,8 @@
downstream_qlen_backpressure_case/1,
config_change_case/1,
replay_attack_case/1,
replay_attack_server_error_case/1
replay_attack_server_error_case/1,
ipv6_connect_case/1
]).
-export([set_env/2,
......@@ -375,26 +376,55 @@ replay_attack_server_error_case(Cfg) when is_list(Cfg) ->
%% TODO: send a lot, not read, and then close - assert connection IDs are cleaned up
%% @doc Test that it's possible to connect and communicate via IPv6
ipv6_connect_case({pre, Cfg}) ->
setup_single(?FUNCTION_NAME, "::1", 10000 + ?LINE, #{}, Cfg);
ipv6_connect_case({post, Cfg}) ->
stop_single(Cfg);
ipv6_connect_case(Cfg) when is_list(Cfg) ->
DcId = ?config(dc_id, Cfg),
Host = ?config(mtp_host, Cfg),
Port = ?config(mtp_port, Cfg),
Secret = ?config(mtp_secret, Cfg),
ConnCount = fun() ->
mtp_test_metric:get_tags(
count, [?APP, in_connection, total], [?FUNCTION_NAME])
end,
?assertEqual(not_found, ConnCount()),
?assertEqual(8, tuple_size(Host)),
Cli0 = mtp_test_client:connect(Host, Port, Secret, DcId, mtp_secure),
Data = crypto:strong_rand_bytes(64),
Cli1 = mtp_test_client:send(Data, Cli0),
{ok, Packet, Cli2} = mtp_test_client:recv_packet(Cli1, 1000),
ok = mtp_test_client:close(Cli2),
?assertEqual(Data, Packet),
?assertEqual(1, ConnCount()),
ok = mtp_test_metric:wait_for_value(
count, [?APP, in_connection_closed, total], [?FUNCTION_NAME], 1, 5000).
%% Helpers
setup_single(Name, MtpPort, DcCfg0, Cfg) ->
setup_single(Name, "127.0.0.1", MtpPort, DcCfg0, Cfg).
setup_single(Name, MtpIpStr, MtpPort, DcCfg0, Cfg) ->
{ok, Pid} = mtp_test_metric:start_link(),
PubKey = crypto:strong_rand_bytes(128),
DcId = 1,
Ip = {127, 0, 0, 1},
DcConf = [{DcId, Ip, MtpPort + 10}],
DcConf = [{DcId, {127, 0, 0, 1}, MtpPort + 10}],
Secret = mtp_handler:hex(crypto:strong_rand_bytes(16)),
Listeners = [#{name => Name,
port => MtpPort,
listen_ip => "127.0.0.1",
listen_ip => MtpIpStr,
secret => Secret,
tag => <<"dcbe8f1493fa4cd9ab300891c0b5b326">>}],
application:load(mtproto_proxy),
Cfg1 = set_env([{ports, Listeners}], Cfg),
{ok, DcCfg} = mtp_test_datacenter:start_dc(PubKey, DcConf, DcCfg0),
{ok, _} = application:ensure_all_started(mtproto_proxy),
{ok, MtpIp} = inet:parse_address(MtpIpStr),
[{dc_id, DcId},
{mtp_host, Ip},
{mtp_host, MtpIp},
{mtp_port, MtpPort},
{mtp_secret, Secret},
{dc_conf, DcCfg},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment