Compare commits

..

No commits in common. "8d94244689ce8760e73dd743a8c5c294c2aac666" and "c89091c205bfdecdc202b487766e681f1a90cbf0" have entirely different histories.

3 changed files with 161 additions and 99 deletions

View File

@ -15,7 +15,7 @@
-export([get_all_networks/0, get_network/1]). -export([get_all_networks/0, get_network/1]).
-export([auth_token/3, node_online/3, node_offline/2, flow_report/5, network_forward_report/2, auth_network_code/3]). -export([auth_token/3, node_online/3, node_offline/2, flow_report/5, network_forward_report/2, auth_network_code/3]).
-export([assign_ip_address/3]). -export([assign_ip_address/5]).
-spec get_all_networks() -> {ok, [NetworkId :: integer()]} | {error, Reason :: any()}. -spec get_all_networks() -> {ok, [NetworkId :: integer()]} | {error, Reason :: any()}.
get_all_networks() -> get_all_networks() ->
@ -78,25 +78,19 @@ auth_network_code(ClientId, NetworkCode, Version) when is_binary(ClientId), is_b
end. end.
%% ip地址的分配 %% ip地址的分配
-spec assign_ip_address(NetworkId :: integer(), ClientId :: binary(), Mac :: binary()) -> {ok, map()} | {error, Reason :: any()}. assign_ip_address(NetworkId, ClientId, Mac, RetainIp, HostName) when is_integer(NetworkId), is_binary(ClientId), is_binary(Mac), is_integer(RetainIp), is_binary(HostName) ->
assign_ip_address(NetworkId, ClientId, Mac) when is_integer(NetworkId), is_binary(ClientId), is_binary(Mac) ->
Params = #{ Params = #{
<<"network_id">> => NetworkId, <<"network_id">> => NetworkId,
<<"client_id">> => ClientId, <<"client_id">> => ClientId,
<<"mac">> => Mac <<"mac">> => Mac,
<<"retain_ip">> => RetainIp,
<<"host_name">> => HostName
}, },
case catch do_post("assign_ip_address", Params) of case catch do_post("assign_ip_address", Params) of
{ok, Resp} -> {ok, Resp} ->
case catch jiffy:decode(Resp, [return_maps]) of case catch jiffy:decode(Resp, [return_maps]) of
Json when is_map(Json) -> Result when is_map(Result) ->
case Json of {ok, Result};
#{<<"result">> := Result} ->
{ok, Result};
#{<<"error">> := #{<<"message">> := Message}} ->
{error, Message};
_ ->
{error, <<"invalid response">>}
end;
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end; end;

View File

@ -117,7 +117,7 @@ handle_cast(_Msg, State) ->
%% token或者网络id来注册 %% token或者网络id来注册
handle_info({tcp, Sock, <<PacketId:32, ?PACKET_REGISTER_SUPER, Body/binary>>}, State=#state{transport = Transport, socket = Sock}) -> handle_info({tcp, Sock, <<PacketId:32, ?PACKET_REGISTER_SUPER, Body/binary>>}, State=#state{transport = Transport, socket = Sock}) ->
#sdl_register_super{version = Version, client_id = ClientId, dev_addr = #sdl_dev_addr{mac = Mac}, network_code = NetworkCode, token = Token, pub_key = PubKey} = sdlan_pb:decode_msg(Body, sdl_register_super), #sdl_register_super{version = Version, client_id = ClientId, hostname = HostName, dev_addr = #sdl_dev_addr{net_addr = NetAddr0, mac = Mac}, network_code = NetworkCode, token = Token, pub_key = PubKey} = sdlan_pb:decode_msg(Body, sdl_register_super),
%% %%
lager:debug("[sdlan_channel] client_id: ~p, public_key: ~p, token: ~p, network_code: ~p", [ClientId, PubKey, Token, NetworkCode]), lager:debug("[sdlan_channel] client_id: ~p, public_key: ~p, token: ~p, network_code: ~p", [ClientId, PubKey, Token, NetworkCode]),
@ -140,7 +140,7 @@ handle_info({tcp, Sock, <<PacketId:32, ?PACKET_REGISTER_SUPER, Body/binary>>}, S
%% network的对应关系 %% network的对应关系
case sdlan_network:get_pid(NetworkId) of case sdlan_network:get_pid(NetworkId) of
NetworkPid when is_pid(NetworkPid) -> NetworkPid when is_pid(NetworkPid) ->
try sdlan_network:assign_ip_addr(NetworkPid, self(), ClientId, Mac) of try sdlan_network:assign_ip_addr(NetworkPid, self(), ClientId, Mac, NetAddr0, HostName) of
{ok, Domain, NetAddr, NetBitLen, AesKey} -> {ok, Domain, NetAddr, NetBitLen, AesKey} ->
RsaPubKey = sdlan_cipher:rsa_pem_decode(PubKey), RsaPubKey = sdlan_cipher:rsa_pem_decode(PubKey),
EncodedAesKey = rsa_encode(AesKey, RsaPubKey), EncodedAesKey = rsa_encode(AesKey, RsaPubKey),

View File

@ -21,8 +21,8 @@
%% API %% API
-export([start_link/2]). -export([start_link/2]).
-export([get_name/1, get_pid/1, assign_ip_addr/4, peer_info/3, unregister/3, debug_info/1, get_network_id/1, get_used_map/1]). -export([get_name/1, get_pid/1, assign_ip_addr/6, peer_info/3, unregister/3, debug_info/1, get_network_id/1, get_used_map/1]).
-export([forward/5, update_hole/6, disable_client/2, get_channel/2, dropout_client/2]). -export([forward/5, update_hole/6, disable_client/2, get_channel/2, dropout_client/2, reload/1]).
-export([test_event/1]). -export([test_event/1]).
%% gen_server callbacks %% gen_server callbacks
@ -33,10 +33,9 @@
nat_type :: integer() nat_type :: integer()
}). }).
%% ip的使用信息, Node的运行时状态信 %% ip的使用信息,
-record(endpoint, { -record(host, {
client_id :: binary(), client_id :: binary(),
ip :: integer(),
channel_pid :: undefined | pid(), channel_pid :: undefined | pid(),
monitor_ref :: undefined | reference(), monitor_ref :: undefined | reference(),
hole :: undefined | #hole{}, hole :: undefined | #hole{},
@ -61,7 +60,11 @@
%% , AES-256 %% , AES-256
aes_key :: binary(), aes_key :: binary(),
%% 使ip, #{mac :: integer() => #endpoint{}} %% ip分配器
%% ip地址
ips = [] :: [Ip :: integer()],
%% 使ip, #{mac :: integer() => Host :: #host{}}
used_map = #{} used_map = #{}
}). }).
@ -81,10 +84,14 @@ get_pid(Id) when is_integer(Id) ->
get_name(Id) when is_integer(Id) -> get_name(Id) when is_integer(Id) ->
list_to_atom("sdlan_network:" ++ integer_to_list(Id)). list_to_atom("sdlan_network:" ++ integer_to_list(Id)).
-spec assign_ip_addr(Pid :: pid(), ChannelPid :: pid(), ClientId :: binary(), Mac :: binary()) -> -spec reload(Pid :: pid()) -> ok | {error, Reason :: any()}.
reload(Pid) when is_pid(Pid) ->
gen_server:call(Pid, reload).
-spec assign_ip_addr(Pid :: pid(), ChannelPid :: pid(), ClientId :: binary(), Mac :: binary(), NetAddr :: integer(), HostName :: binary()) ->
{ok, Domain :: binary(), NetAddr :: integer(), MaskLen :: integer(), AesKey :: binary()} | {error, Reason :: any()}. {ok, Domain :: binary(), NetAddr :: integer(), MaskLen :: integer(), AesKey :: binary()} | {error, Reason :: any()}.
assign_ip_addr(Pid, ChannelPid, ClientId, Mac) when is_pid(Pid), is_pid(ChannelPid), is_binary(ClientId), is_binary(Mac) -> assign_ip_addr(Pid, ChannelPid, ClientId, Mac, NetAddr, HostName) when is_pid(Pid), is_pid(ChannelPid), is_binary(ClientId), is_binary(Mac), is_integer(NetAddr) ->
gen_server:call(Pid, {assign_ip_addr, ChannelPid, ClientId, Mac}). gen_server:call(Pid, {assign_ip_addr, ChannelPid, ClientId, Mac, NetAddr, HostName}).
-spec get_network_id(Pid :: pid()) -> {ok, NetworkId :: integer()}. -spec get_network_id(Pid :: pid()) -> {ok, NetworkId :: integer()}.
get_network_id(Pid) when is_pid(Pid) -> get_network_id(Pid) when is_pid(Pid) ->
@ -153,8 +160,8 @@ init([Id]) when is_integer(Id) ->
ignore; ignore;
{ok, #{<<"id">> := Id, <<"name">> := Name, <<"domain">> := Domain, <<"ipaddr">> := IpAddr0, <<"owner_id">> := OwnerId}} -> {ok, #{<<"id">> := Id, <<"name">> := Name, <<"domain">> := Domain, <<"ipaddr">> := IpAddr0, <<"owner_id">> := OwnerId}} ->
{IpAddr, MaskLen} = parse_ipaddr(IpAddr0), {IpAddr, MaskLen} = parse_ipaddr(IpAddr0),
Ips = sdlan_ipaddr:ips(IpAddr, MaskLen),
AesKey = sdlan_util:rand_byte(32), AesKey = sdlan_util:rand_byte(32),
%% key %% key
ThrottleKey = list_to_atom("network_throttle:" ++ integer_to_list(Id)), ThrottleKey = list_to_atom("network_throttle:" ++ integer_to_list(Id)),
%% %%
@ -162,9 +169,15 @@ init([Id]) when is_integer(Id) ->
%% %%
erlang:start_timer(?FLOW_REPORT_INTERVAL, self(), flow_report_ticker), erlang:start_timer(?FLOW_REPORT_INTERVAL, self(), flow_report_ticker),
%%
create_mnesia_table(Id),
lager:debug("[sdlan_network] network: ~p, ips: ~p", [Id, lists:map(fun sdlan_ipaddr:int_to_ipv4/1, Ips)]),
sdlan_domain_regedit:insert(Domain), sdlan_domain_regedit:insert(Domain),
{ok, #state{network_id = Id, name = Name, domain = Domain, ipaddr = IpAddr, owner_id = OwnerId, mask_len = MaskLen, aes_key = AesKey, throttle_key = ThrottleKey}}; {ok, #state{network_id = Id, name = Name, domain = Domain, ipaddr = IpAddr, owner_id = OwnerId, mask_len = MaskLen, ips = Ips, aes_key = AesKey, throttle_key = ThrottleKey}};
{error, Reason} -> {error, Reason} ->
lager:warning("[sdlan_network] load network: ~p, get error: ~p", [Id, Reason]), lager:warning("[sdlan_network] load network: ~p, get error: ~p", [Id, Reason]),
ignore ignore
@ -180,23 +193,58 @@ init([Id]) when is_integer(Id) ->
{noreply, NewState :: #state{}, timeout() | hibernate} | {noreply, NewState :: #state{}, timeout() | hibernate} |
{stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
{stop, Reason :: term(), NewState :: #state{}}). {stop, Reason :: term(), NewState :: #state{}}).
%% ip地址 %%
handle_call({assign_ip_addr, ChannelPid, ClientId, Mac}, _From, handle_call(reload, _From, State = #state{network_id = Id, ipaddr = OldIpAddr, mask_len = OldMarkLen, used_map = UsedMap}) ->
State = #state{network_id = NetworkId, domain = Domain, used_map = UsedMap, mask_len = MaskLen, aes_key = AesKey}) -> case sdlan_api:get_network(Id) of
{ok, #{<<"name">> := Name, <<"ipaddr">> := IpAddr0, <<"owner_id">> := OwnerId}} ->
{IpAddr, MaskLen} = parse_ipaddr(IpAddr0),
case OldIpAddr =:= IpAddr andalso OldMarkLen =:= MaskLen of
true ->
{reply, ok, State#state{name = Name, owner_id = OwnerId}};
false ->
lager:debug("[sdlan_networkd] network_id: ~p, reload will close all channels", [Id]),
Ips = sdlan_ipaddr:ips(IpAddr, MaskLen),
%%
maps:foreach(fun(_, #host{channel_pid = ChannelPid, monitor_ref = MRef}) ->
is_reference(MRef) andalso demonitor(MRef),
is_process_alive(ChannelPid) andalso sdlan_channel:stop(ChannelPid, normal)
end, UsedMap),
%%
ok = client_model:delete_clients(Id),
case sdlan_api:assign_ip_address(NetworkId, ClientId, Mac) of {reply, ok, State#state{name = Name, ipaddr = IpAddr,
{ok, #{<<"ip">> := Ip, <<"host_name">> := HostName}} -> owner_id = OwnerId, mask_len = MaskLen, ips = Ips, used_map = maps:new()}}
%% ip地址的时候mac地址为唯一基准 end;
lager:debug("[sdlan_network] alloc_ip, network_id: ~p, client_id: ~p, mac: ~p, net_addr: ~p", {error, Reason} ->
[NetworkId, ClientId, sdlan_util:format_mac(Mac), sdlan_ipaddr:int_to_ipv4(Ip)]), lager:warning("[sdlan_network] reload network: ~p, get error: ~p", [Id, Reason]),
{reply, {error, Reason}, State}
end;
%% ip地址
handle_call({assign_ip_addr, ChannelPid, ClientId, Mac, NetAddr0, HostName}, _From,
State = #state{network_id = NetworkId, domain = Domain, ips = Ips, used_map = UsedMap, mask_len = MaskLen, aes_key = AesKey}) ->
%% ip地址的时候mac地址为唯一基准
lager:debug("[sdlan_network] alloc_ip, network_id: ~p, ips: ~p, client_id: ~p, mac: ~p, net_addr: ~p",
[NetworkId, Ips, ClientId, sdlan_util:format_mac(Mac), sdlan_ipaddr:int_to_ipv4(NetAddr0)]),
case client_model:alloc_ip(NetworkId, Ips, ClientId, Mac, NetAddr0, HostName) of
{ok, Ip} ->
%% channel %% channel
maybe_close_channel(maps:get(Mac, UsedMap, undefined)), maybe_close_channel(maps:get(Mac, UsedMap, undefined)),
%% ->ip的映射关系 %% ->ip的映射关系
sdlan_hostname_regedit:insert(HostName, Domain, Ip), case HostName =/= <<>> of
true ->
FullHostname = <<HostName/binary, ".", Domain/binary>>,
sdlan_hostname_regedit:insert(FullHostname, Ip);
false ->
ok
end,
%% channel之间的关系 %% channel之间的关系
MRef = monitor(process, ChannelPid), MRef = monitor(process, ChannelPid),
NUsedMap = maps:put(Mac, #endpoint{client_id = ClientId, ip = Ip, channel_pid = ChannelPid, monitor_ref = MRef}, UsedMap), NUsedMap = maps:put(Mac, #host{client_id = ClientId, channel_pid = ChannelPid, monitor_ref = MRef}, UsedMap),
{reply, {ok, Domain, Ip, MaskLen, AesKey}, State#state{used_map = NUsedMap}}; {reply, {ok, Domain, Ip, MaskLen, AesKey}, State#state{used_map = NUsedMap}};
{error, Reason} -> {error, Reason} ->
@ -204,7 +252,7 @@ handle_call({assign_ip_addr, ChannelPid, ClientId, Mac}, _From,
end; end;
handle_call(get_used_map, _From, State = #state{used_map = UsedMap}) -> handle_call(get_used_map, _From, State = #state{used_map = UsedMap}) ->
UsedInfos = maps:map(fun(_, #endpoint{hole = Hole, v6_info = V6Info}) -> UsedInfos = maps:map(fun(_, #host{hole = Hole, v6_info = V6Info}) ->
HoleMap = case Hole of HoleMap = case Hole of
#hole{peer = {NatIp, NatPort}} -> #hole{peer = {NatIp, NatPort}} ->
#{ #{
@ -231,31 +279,40 @@ handle_call(get_used_map, _From, State = #state{used_map = UsedMap}) ->
%% client设置为禁止状态 %% client设置为禁止状态
handle_call({disable_client, ClientId}, _From, State = #state{network_id = NetworkId, used_map = UsedMap}) -> handle_call({disable_client, ClientId}, _From, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
case lists:search(fun({_, #endpoint{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of case lists:search(fun({_, #host{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of
{value, {Mac, #endpoint{channel_pid = ChannelPid, monitor_ref = MRef}}} -> {value, {Mac, #host{channel_pid = ChannelPid, monitor_ref = MRef}}} ->
is_reference(MRef) andalso demonitor(MRef), is_reference(MRef) andalso demonitor(MRef),
sdlan_channel:stop(ChannelPid, disable), sdlan_channel:stop(ChannelPid, disable),
NUsedMap = maps:remove(Mac, UsedMap), NUsedMap = maps:remove(Mac, UsedMap),
%%
client_model:disable_client(NetworkId, ClientId),
{reply, ok, State#state{used_map = NUsedMap}}; {reply, ok, State#state{used_map = NUsedMap}};
false -> false ->
{reply, error, State} {reply, error, State}
end; end;
handle_call({get_channel, ClientId}, _From, State = #state{used_map = UsedMap}) -> handle_call({get_channel, ClientId}, _From, State = #state{used_map = UsedMap}) ->
case lists:search(fun({_, #endpoint{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of case lists:search(fun({_, #host{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of
{value, {_Ip, #endpoint{channel_pid = ChannelPid}}} -> {value, {_Ip, #host{channel_pid = ChannelPid}}} ->
{reply, {ok, ChannelPid}, State}; {reply, {ok, ChannelPid}, State};
false -> false ->
{reply, error, State} {reply, error, State}
end; end;
%% channel, ; drop的时候需要从当前网络中移除 %% channel, ; drop的时候需要从当前网络中移除
handle_call({dropout_client, ClientId}, _From, State = #state{used_map = UsedMap}) -> handle_call({dropout_client, ClientId}, _From, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
case lists:search(fun({_, #endpoint{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of case lists:search(fun({_, #host{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of
{value, {Mac, #endpoint{channel_pid = ChannelPid, monitor_ref = MRef}}} -> {value, {Mac, #host{channel_pid = ChannelPid, monitor_ref = MRef}}} ->
is_reference(MRef) andalso demonitor(MRef), is_reference(MRef) andalso demonitor(MRef),
NUsedMap = maps:remove(Mac, UsedMap), NUsedMap = maps:remove(Mac, UsedMap),
{reply, {ok, ChannelPid}, State#state{used_map = NUsedMap}}; %%
case client_model:delete_client(NetworkId, ClientId) of
{ok, #client{host_name = HostName}} ->
{reply, {ok, ChannelPid, HostName}, State#state{used_map = NUsedMap}};
{error, _} ->
{reply, error, State}
end;
false -> false ->
{reply, error, State} {reply, error, State}
end; end;
@ -266,10 +323,10 @@ handle_call(get_network_id, _From, State = #state{network_id = NetworkId}) ->
%% nat_peer信息 %% nat_peer信息
handle_call({peer_info, SrcMac, DstMac}, _From, State = #state{used_map = UsedMap}) -> handle_call({peer_info, SrcMac, DstMac}, _From, State = #state{used_map = UsedMap}) ->
case maps:find(DstMac, UsedMap) of case maps:find(DstMac, UsedMap) of
{ok, #endpoint{channel_pid = DstChannelPid, hole = #hole{peer = DstNatPeer, nat_type = DstNatType}, v6_info = DstV6Info}} -> {ok, #host{channel_pid = DstChannelPid, hole = #hole{peer = DstNatPeer, nat_type = DstNatType}, v6_info = DstV6Info}} ->
%% sendRegister事件(2024-06-25 ) %% sendRegister事件(2024-06-25 )
case maps:get(SrcMac, UsedMap, undefined) of case maps:get(SrcMac, UsedMap, undefined) of
#endpoint{hole = #hole{peer = {SrcNatIp, SrcNatPort}, nat_type = NatType}, v6_info = SrcV6Info} -> #host{hole = #hole{peer = {SrcNatIp, SrcNatPort}, nat_type = NatType}, v6_info = SrcV6Info} ->
Event = sdlan_pb:encode_msg(#sdl_send_register_event { Event = sdlan_pb:encode_msg(#sdl_send_register_event {
dst_mac = SrcMac, dst_mac = SrcMac,
nat_ip = sdlan_ipaddr:ipv4_to_int(SrcNatIp), nat_ip = sdlan_ipaddr:ipv4_to_int(SrcNatIp),
@ -286,13 +343,21 @@ handle_call({peer_info, SrcMac, DstMac}, _From, State = #state{used_map = UsedMa
{reply, error, State} {reply, error, State}
end; end;
handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = IpAddr, mask_len = MaskLen, owner_id = OwnerId, used_map = UsedMap}) -> handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = IpAddr, mask_len = MaskLen, owner_id = OwnerId, ips = Ips, used_map = UsedMap}) ->
Reply = #{ Reply = #{
<<"network_id">> => NetworkId, <<"network_id">> => NetworkId,
<<"ipaddr">> => IpAddr, <<"ipaddr">> => IpAddr,
<<"mask_len">> => MaskLen, <<"mask_len">> => MaskLen,
<<"owner_id">> => OwnerId, <<"owner_id">> => OwnerId,
<<"used_ips">> => lists:map(fun format_endpoint/1, maps:to_list(UsedMap)) <<"ips">> => lists:map(fun sdlan_ipaddr:int_to_ipv4/1, Ips),
<<"used_ips">> => lists:map(fun({_, Host = #host{client_id = ClientId}}) ->
case client_model:get_client(NetworkId, ClientId) of
error ->
#{};
{ok, #client{mac = Mac, ip = Ip}} ->
format_host(Host, Ip, Mac)
end
end, maps:to_list(UsedMap))
}, },
{reply, Reply, State}. {reply, Reply, State}.
@ -308,19 +373,29 @@ handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id =
PacketBytes = byte_size(Packet), PacketBytes = byte_size(Packet),
case maps:find(DstMac, UsedMap) of case maps:find(DstMac, UsedMap) of
{ok, #endpoint{hole = #hole{peer = Peer = {Ip, Port}}}} -> {ok, #host{hole = #hole{peer = Peer = {Ip, Port}}}} ->
case limiting_check(ThrottleKey) of case throttle:check(sdlan_network, ThrottleKey) of
pass -> {ok, _RestCount, _LeftToReset} ->
%% client和stun之间必须有心跳机制保持nat映射可用udp包肯定可以到达对端的nat %% client和stun之间必须有心跳机制保持nat映射可用udp包肯定可以到达对端的nat
lager:debug("[sdlan_network] forward data networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole: ~p", lager:debug("[sdlan_network] forward data networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole: ~p",
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac), Peer]), [NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac), Peer]),
gen_udp:send(Sock, Ip, Port, Packet), gen_udp:send(Sock, Ip, Port, Packet),
{noreply, State#state{forward_bytes = ForwardBytes + PacketBytes}}; {noreply, State#state{forward_bytes = ForwardBytes + PacketBytes}};
denied -> {limit_exceeded, 0, _LeftToReset} ->
lager:notice("[sdlan_network] networkd_id: ~p, src_mac: ~p, dst_mac: ~p, rate limited, discard", %%
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]), case sdlan_network_coordinator:checkout() of
{noreply, State} ok ->
lager:debug("[sdlan_network] use release forward data networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole: ~p",
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac), Peer]),
gen_udp:send(Sock, Ip, Port, Packet),
{noreply, State#state{forward_bytes = ForwardBytes + PacketBytes}};
error ->
lager:notice("[sdlan_network] networkd_id: ~p, src_mac: ~p, dst_mac: ~p, rate limited, discard",
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]),
{noreply, State}
end
end; end;
{ok, _} -> {ok, _} ->
lager:debug("[sdlan_network] networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole not found", lager:debug("[sdlan_network] networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole not found",
@ -333,7 +408,6 @@ handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id =
end; end;
%% , ip广播或组播, %% , ip广播或组播,
%% TODO arp请求arp请求就不要广播出去了network是知道目标的ip信息的
handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id = NetworkId, used_map = UsedMap, forward_bytes = ForwardBytes}) handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id = NetworkId, used_map = UsedMap, forward_bytes = ForwardBytes})
when is_map_key(SrcMac, UsedMap) -> when is_map_key(SrcMac, UsedMap) ->
%% 广 %% 广
@ -341,9 +415,10 @@ handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id =
true -> true ->
PacketBytes = byte_size(Packet), PacketBytes = byte_size(Packet),
%% 广 %% 广
maps:foreach(fun(Mac, #endpoint{hole = Hole}) -> maps:foreach(fun(Mac, #host{hole = Hole}) ->
case {Mac =/= SrcMac, Hole} of case {Mac =/= SrcMac, Hole} of
{true, #hole{peer = {NatIp, NatPort}}} -> {true, #hole{peer = {NatIp, NatPort}}} ->
lager:debug("[sdlan_network] call me here"),
gen_udp:send(Sock, NatIp, NatPort, Packet); gen_udp:send(Sock, NatIp, NatPort, Packet);
_ -> _ ->
ok ok
@ -372,16 +447,16 @@ handle_cast({unregister, _ClientId, Mac}, State = #state{network_id = NetworkId,
case maps:take(Mac, UsedMap) of case maps:take(Mac, UsedMap) of
error -> error ->
{noreply, State}; {noreply, State};
{#endpoint{channel_pid = ChannelPid, monitor_ref = MRef}, NUsedMap} -> {#host{channel_pid = ChannelPid, monitor_ref = MRef}, NUsedMap} ->
is_reference(MRef) andalso demonitor(MRef), is_reference(MRef) andalso demonitor(MRef),
sdlan_channel:stop(ChannelPid, normal), sdlan_channel:stop(ChannelPid, normal),
{noreply, State#state{used_map = NUsedMap}} {noreply, State#state{used_map = NUsedMap}}
end; end;
%% client是属于当前网络的 %% client是属于当前网络的
handle_cast({update_hole, ClientId, Mac, Peer, NatType, V6Info}, State = #state{used_map = UsedMap}) -> handle_cast({update_hole, ClientId, Mac, Peer, NatType, V6Info}, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
case maps:find(Mac, UsedMap) of case {maps:find(Mac, UsedMap), client_model:get_client(NetworkId, ClientId)} of
{ok, Endpoint0 = #endpoint{client_id = ClientId0, ip = Ip, hole = OldHole}} when ClientId =:= ClientId0 -> {{ok, Host0 = #host{client_id = ClientId0, hole = OldHole}}, {ok, #client{ip = Ip}}} when ClientId =:= ClientId0 ->
case OldHole =:= undefined orelse (OldHole#hole.peer =/= Peer orelse OldHole#hole.nat_type =/= NatType) of case OldHole =:= undefined orelse (OldHole#hole.peer =/= Peer orelse OldHole#hole.nat_type =/= NatType) of
true -> true ->
NatChangedEvent = sdlan_pb:encode_msg(#sdl_nat_changed_event{ NatChangedEvent = sdlan_pb:encode_msg(#sdl_nat_changed_event{
@ -392,9 +467,9 @@ handle_cast({update_hole, ClientId, Mac, Peer, NatType, V6Info}, State = #state{
false -> false ->
ok ok
end, end,
Endpoint = Endpoint0#endpoint{hole = #hole{peer = Peer, nat_type = NatType}, v6_info = V6Info}, Host = Host0#host{hole = #hole{peer = Peer, nat_type = NatType}, v6_info = V6Info},
{noreply, State#state{used_map = maps:put(Mac, Endpoint, UsedMap)}}; {noreply, State#state{used_map = maps:put(Mac, Host, UsedMap)}};
_ -> _ ->
{noreply, State} {noreply, State}
end. end.
@ -417,7 +492,7 @@ handle_info({'EXIT', _Pid, shutdown}, State = #state{network_id = NetworkId, use
%% Channel进程退出, hole里面的数据也需要清理 %% Channel进程退出, hole里面的数据也需要清理
handle_info({'DOWN', _MRef, process, ChannelPid, Reason}, State = #state{network_id = NetworkId, used_map = UsedMap}) -> handle_info({'DOWN', _MRef, process, ChannelPid, Reason}, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
lager:notice("[sdlan_network] network_id: ~p, channel_pid: ~p, close with reason: ~p", [NetworkId, ChannelPid, Reason]), lager:notice("[sdlan_network] network_id: ~p, channel_pid: ~p, close with reason: ~p", [NetworkId, ChannelPid, Reason]),
NUsedMap = maps:filter(fun(_, #endpoint{channel_pid = ChannelPid0}) -> ChannelPid =/= ChannelPid0 end, UsedMap), NUsedMap = maps:filter(fun(_, #host{channel_pid = ChannelPid0}) -> ChannelPid =/= ChannelPid0 end, UsedMap),
{noreply, State#state{used_map = NUsedMap}}. {noreply, State#state{used_map = NUsedMap}}.
%% @private %% @private
@ -430,11 +505,6 @@ handle_info({'DOWN', _MRef, process, ChannelPid, Reason}, State = #state{network
terminate(Reason, #state{network_id = NetworkId, used_map = UsedMap}) -> terminate(Reason, #state{network_id = NetworkId, used_map = UsedMap}) ->
lager:debug("[sdlan_network] network: ~p, will terminate with reason: ~p", [NetworkId, Reason]), lager:debug("[sdlan_network] network: ~p, will terminate with reason: ~p", [NetworkId, Reason]),
broadcast_shutdown(UsedMap), broadcast_shutdown(UsedMap),
%%
maps:foreach(fun(_, #endpoint{channel_pid = ChannelPid, monitor_ref = MRef}) ->
is_reference(MRef) andalso demonitor(MRef),
is_process_alive(ChannelPid) andalso sdlan_channel:stop(ChannelPid, normal)
end, UsedMap),
ok. ok.
%% @private %% @private
@ -449,23 +519,21 @@ code_change(_OldVsn, State = #state{}, _Extra) ->
%%% Internal functions %%% Internal functions
%%%=================================================================== %%%===================================================================
-spec limiting_check(ThrottleKey :: any()) -> pass | denied. %%
limiting_check(ThrottleKey) -> -spec create_mnesia_table(NetworkId :: integer()) -> no_return().
case throttle:check(sdlan_network, ThrottleKey) of create_mnesia_table(NetworkId) when is_integer(NetworkId) ->
{ok, _RestCount, _LeftToReset} -> Tab = client_model:get_table_name(NetworkId),
pass; Tables = mnesia:system_info(tables),
{limit_exceeded, 0, _LeftToReset} -> case lists:member(Tab, Tables) of
%% true ->
case sdlan_network_coordinator:checkout() of ok;
ok -> false ->
pass; Res = client_model:create_table(Tab),
error -> lager:debug("[sdlan_network] create table result: ~p", [Res])
denied
end
end. end.
-spec maybe_close_channel(undefined | #endpoint{}) -> no_return(). -spec maybe_close_channel(undefined | #host{}) -> no_return().
maybe_close_channel(#endpoint{channel_pid = ChannelPid0, monitor_ref = MRef0}) -> maybe_close_channel(#host{channel_pid = ChannelPid0, monitor_ref = MRef0}) ->
case is_pid(ChannelPid0) andalso is_process_alive(ChannelPid0) of case is_pid(ChannelPid0) andalso is_process_alive(ChannelPid0) of
true -> true ->
is_reference(MRef0) andalso demonitor(MRef0), is_reference(MRef0) andalso demonitor(MRef0),
@ -478,7 +546,7 @@ maybe_close_channel(_) ->
-spec broadcast(EventType :: integer(), Event :: binary(), ExcludeMac :: binary(), UsedMap :: map()) -> no_return(). -spec broadcast(EventType :: integer(), Event :: binary(), ExcludeMac :: binary(), UsedMap :: map()) -> no_return().
broadcast(EventType, Event, ExcludeMac, UsedMap) when is_map(UsedMap), is_binary(ExcludeMac), is_integer(EventType), is_binary(Event) -> broadcast(EventType, Event, ExcludeMac, UsedMap) when is_map(UsedMap), is_binary(ExcludeMac), is_integer(EventType), is_binary(Event) ->
maps:foreach(fun(Mac, #endpoint{channel_pid = ChannelPid}) -> maps:foreach(fun(Mac, #host{channel_pid = ChannelPid}) ->
case is_process_alive(ChannelPid) andalso ExcludeMac /= Mac of case is_process_alive(ChannelPid) andalso ExcludeMac /= Mac of
true -> true ->
sdlan_channel:send_event(ChannelPid, EventType, Event); sdlan_channel:send_event(ChannelPid, EventType, Event);
@ -488,7 +556,7 @@ broadcast(EventType, Event, ExcludeMac, UsedMap) when is_map(UsedMap), is_binary
end, UsedMap). end, UsedMap).
broadcast_shutdown(UsedMap) when is_map(UsedMap) -> broadcast_shutdown(UsedMap) when is_map(UsedMap) ->
maps:foreach(fun(_, #endpoint{channel_pid = ChannelPid}) -> maps:foreach(fun(_, #host{channel_pid = ChannelPid}) ->
case is_process_alive(ChannelPid) of case is_process_alive(ChannelPid) of
true -> true ->
NetworkShutdownEvent = sdlan_pb:encode_msg(#sdl_network_shutdown_event { NetworkShutdownEvent = sdlan_pb:encode_msg(#sdl_network_shutdown_event {
@ -512,18 +580,18 @@ parse_ipaddr(IpAddr0) when is_binary(IpAddr0) ->
{IpAddr0, 24} {IpAddr0, 24}
end. end.
-spec format_endpoint({Mac :: binary(), Host :: #endpoint{}}) -> map(). -spec format_host(Host :: #host{}, Ip :: integer(), Mac :: binary()) -> map().
format_endpoint({Mac, #endpoint{client_id = ClientId, ip = Ip, hole = Hole, v6_info = V6Info}}) -> format_host(#host{client_id = ClientId, hole = Hole, v6_info = V6Info}, Ip, Mac) when is_integer(Ip), is_binary(Mac) ->
HoleMap = case Hole of HoleMap = case Hole of
undefined -> undefined ->
#{}; #{};
#hole{peer = {NatIp, NatPort}, nat_type = NatType} -> #hole{peer = {NatIp, NatPort}, nat_type = NatType} ->
#{ #{
nat_ip => NatIp, nat_ip => NatIp,
nat_port => NatPort, nat_port => NatPort,
nat_type => NatType nat_type => NatType
} }
end, end,
V6InfoMap = case V6Info of V6InfoMap = case V6Info of
undefined -> undefined ->