fix network
This commit is contained in:
parent
a4f2ed8428
commit
eb11c05ba5
@ -15,23 +15,6 @@
|
||||
%% API
|
||||
-export([handle_request/4]).
|
||||
|
||||
handle_request("POST", "/node/list", _, #{<<"network_id">> := NetworkId}) when NetworkId > 0 ->
|
||||
Pid = sdlan_network:get_pid(NetworkId),
|
||||
UsedMap = sdlan_network:get_used_map(Pid),
|
||||
|
||||
Clients = client_model:get_clients(NetworkId),
|
||||
ClientInfos = lists:map(fun(#client{client_id = ClientId, mac = Mac, ip = Ip, status = Status}) ->
|
||||
Info = #{
|
||||
<<"client_id">> => ClientId,
|
||||
<<"mac">> => Mac,
|
||||
<<"ip">> => sdlan_ipaddr:int_to_ipv4(Ip),
|
||||
<<"status">> => atom_to_binary(Status)
|
||||
},
|
||||
maps:merge(Info, maps:get(Mac, UsedMap, #{}))
|
||||
end, Clients),
|
||||
|
||||
{ok, 200, sdlan_util:json_data(ClientInfos)};
|
||||
|
||||
handle_request("POST", "/node/disable", _, #{<<"network_id">> := NetworkId, <<"client_id">> := ClientId}) when NetworkId > 0 ->
|
||||
case sdlan_network:get_pid(NetworkId) of
|
||||
undefined ->
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
%% API
|
||||
-export([start_link/2]).
|
||||
-export([get_name/1, get_pid/1, lookup_pid/1, attach/6, peer_info/3, unregister/3, debug_info/1, get_network_id/1, get_used_map/1, arp_query/2]).
|
||||
-export([get_name/1, get_pid/1, lookup_pid/1, attach/6, peer_info/3, unregister/3, debug_info/1, get_network_id/1, arp_query/2]).
|
||||
-export([forward/5, update_hole/6, disable_client/2, dropout_client/2]).
|
||||
-export([test_event/1]).
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
%% ip的使用信息, 记录Node的运行时状态信息
|
||||
-record(endpoint, {
|
||||
client_id :: binary(),
|
||||
mac :: binary(),
|
||||
ip :: integer(),
|
||||
hostname :: binary(),
|
||||
hole :: #hole{},
|
||||
@ -51,18 +52,13 @@
|
||||
domain :: binary(),
|
||||
ipaddr :: binary(),
|
||||
mask_len :: integer(),
|
||||
owner_id :: integer(),
|
||||
|
||||
%% 设置网络带宽
|
||||
throttle_key :: atom(),
|
||||
|
||||
%% 转发流量统计
|
||||
forward_bytes = 0,
|
||||
|
||||
%% 同一个网络下公用的密钥, 采用AES-256加密算法;随机生成
|
||||
aes_key :: binary(),
|
||||
|
||||
%% 记录已经使用了的ip, #{mac :: integer() => #endpoint{}}
|
||||
%% 记录已经使用了的ip, #{mac => #endpoint{}}
|
||||
endpoints = #{}
|
||||
}).
|
||||
|
||||
@ -122,7 +118,7 @@ forward(Pid, Sock, SrcMac, DstMac, Packet) when is_pid(Pid), is_binary(SrcMac),
|
||||
update_hole(Pid, ClientId, Mac, Peer, NatType, V6Info) when is_pid(Pid), is_binary(ClientId), is_binary(Mac), is_integer(NatType) ->
|
||||
gen_server:cast(Pid, {update_hole, ClientId, Mac, Peer, NatType, V6Info}).
|
||||
|
||||
-spec disable_client(Pid :: pid(), ClientId :: binary()) -> ok | error.
|
||||
-spec disable_client(Pid :: pid(), ClientId :: binary()) -> ok.
|
||||
disable_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
||||
gen_server:call(Pid, {disable_client, ClientId}).
|
||||
|
||||
@ -135,12 +131,6 @@ dropout_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
||||
debug_info(Pid) when is_pid(Pid) ->
|
||||
gen_server:call(Pid, debug_info).
|
||||
|
||||
-spec get_used_map(Pid :: pid()) -> map().
|
||||
get_used_map(Pid) when is_pid(Pid) ->
|
||||
gen_server:call(Pid, get_used_map);
|
||||
get_used_map(undefined) ->
|
||||
#{}.
|
||||
|
||||
%% @doc Spawns the server and registers the local name (unique)
|
||||
-spec(start_link(Name :: atom(), Id :: integer()) ->
|
||||
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
||||
@ -160,7 +150,7 @@ init([Id]) when is_integer(Id) ->
|
||||
case sdlan_api:get_network(Id) of
|
||||
{ok, #{<<"ipaddr">> := Null}} when Null == <<"null">>; Null == <<"NULL">> ->
|
||||
ignore;
|
||||
{ok, #{<<"id">> := Id, <<"name">> := Name, <<"domain">> := Domain, <<"ipaddr">> := IpAddr0, <<"owner_id">> := OwnerId}} ->
|
||||
{ok, #{<<"id">> := Id, <<"name">> := Name, <<"domain">> := Domain, <<"ipaddr">> := IpAddr0}} ->
|
||||
{IpAddr, MaskLen} = parse_ipaddr(IpAddr0),
|
||||
AesKey = sdlan_util:rand_byte(32),
|
||||
|
||||
@ -173,8 +163,7 @@ init([Id]) when is_integer(Id) ->
|
||||
erlang:start_timer(?FLOW_REPORT_INTERVAL, self(), flow_report_ticker),
|
||||
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, mask_len = MaskLen, aes_key = AesKey, throttle_key = ThrottleKey}};
|
||||
{error, Reason} ->
|
||||
logger:warning("[sdlan_network] load network: ~p, get error: ~p", [Id, Reason]),
|
||||
ignore
|
||||
@ -198,43 +187,17 @@ handle_call({attach, Peer, ClientId, Mac, Ip, Hostname}, _From,
|
||||
[NetworkId, ClientId, sdlan_util:format_mac(Mac), sdlan_ipaddr:int_to_ipv4(Ip)]),
|
||||
%% 添加域名->ip的映射关系
|
||||
sdlan_hostname_regedit:insert(Hostname, Domain, Ip),
|
||||
NEndpoints = maps:put(Mac, #endpoint{client_id = ClientId, ip = Ip, hostname = Hostname, hole = #hole{peer = Peer, nat_type = 0}}, Endpoints),
|
||||
Endpoint = #endpoint{client_id = ClientId, mac = Mac, ip = Ip, hostname = Hostname, hole = #hole{peer = Peer, nat_type = 0}},
|
||||
|
||||
{reply, {ok, Domain, MaskLen, AesKey}, State#state{endpoints = NEndpoints}};
|
||||
|
||||
handle_call(get_used_map, _From, State = #state{endpoints = Endpoints}) ->
|
||||
UsedInfos = maps:map(fun(_, #endpoint{hole = Hole, v6_info = V6Info}) ->
|
||||
HoleMap = case Hole of
|
||||
#hole{peer = {NatIp, NatPort}} ->
|
||||
#{
|
||||
<<"nat_ip">> => sdlan_ipaddr:int_to_ipv4(sdlan_ipaddr:ipv4_to_int(NatIp)),
|
||||
<<"nat_port">> => NatPort
|
||||
};
|
||||
_ ->
|
||||
#{}
|
||||
end,
|
||||
|
||||
V6Map = case V6Info of
|
||||
#sdl_v6_info{v6 = IpV6, port = Port} ->
|
||||
#{
|
||||
<<"v6_ip">> => sdlan_ipaddr:ipv6_bytes_to_binary(IpV6),
|
||||
<<"v6_port">> => Port
|
||||
};
|
||||
_ ->
|
||||
#{}
|
||||
end,
|
||||
#{<<"hole">> => HoleMap, <<"v6_info">> => V6Map}
|
||||
end, Endpoints),
|
||||
|
||||
{reply, {ok, UsedInfos}, State};
|
||||
{reply, {ok, Domain, MaskLen, AesKey}, State#state{endpoints = maps:put(Mac, Endpoint, Endpoints)}};
|
||||
|
||||
%% client设置为禁止状态,不允许重连
|
||||
handle_call({disable_client, ClientId}, _From, State = #state{endpoints = Endpoints}) ->
|
||||
case lists:search(fun({_, #endpoint{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(Endpoints)) of
|
||||
{value, {Mac, #endpoint{}}} ->
|
||||
case search_endpoint(fun(_, #endpoint{client_id = ClientId0}) -> ClientId =:= ClientId0 end, Endpoints) of
|
||||
{ok, Mac, _} ->
|
||||
{reply, ok, State#state{endpoints = maps:remove(Mac, Endpoints)}};
|
||||
false ->
|
||||
{reply, error, State}
|
||||
error ->
|
||||
{reply, ok, State}
|
||||
end;
|
||||
|
||||
handle_call(get_network_id, _From, State = #state{network_id = NetworkId}) ->
|
||||
@ -265,19 +228,18 @@ handle_call({peer_info, SrcMac, DstMac}, _From, State = #state{endpoints = Endpo
|
||||
}),
|
||||
|
||||
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_SEND_REGISTER, Event/binary>>,
|
||||
sdlan_stun_pool:send_packets([{DstNatPeer, EventPacket}])
|
||||
sdlan_stun_pool:send_packet(DstNatPeer, EventPacket)
|
||||
end,
|
||||
{reply, {ok, {DstNatPeer, DstNatType}, DstV6Info}, State};
|
||||
_ ->
|
||||
{reply, error, State}
|
||||
end;
|
||||
|
||||
handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = IpAddr, mask_len = MaskLen, owner_id = OwnerId, endpoints = Endpoints}) ->
|
||||
handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = IpAddr, mask_len = MaskLen, endpoints = Endpoints}) ->
|
||||
Reply = #{
|
||||
<<"network_id">> => NetworkId,
|
||||
<<"ipaddr">> => IpAddr,
|
||||
<<"mask_len">> => MaskLen,
|
||||
<<"owner_id">> => OwnerId,
|
||||
<<"used_ips">> => lists:map(fun format_endpoint/1, maps:to_list(Endpoints))
|
||||
},
|
||||
{reply, Reply, State}.
|
||||
@ -326,15 +288,9 @@ handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id =
|
||||
true ->
|
||||
PacketBytes = byte_size(Packet),
|
||||
%% 消息广播
|
||||
broadcast(fun(#endpoint{hole = Hole}) ->
|
||||
case Hole of
|
||||
#hole{peer = {NatIp, NatPort}} ->
|
||||
gen_udp:send(Sock, NatIp, NatPort, Packet);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
broadcast(fun(#endpoint{hole = #hole{peer = {NatIp, NatPort}}}) ->
|
||||
gen_udp:send(Sock, NatIp, NatPort, Packet)
|
||||
end, [SrcMac], Endpoints),
|
||||
|
||||
%% client和stun之间必须有心跳机制保持nat映射可用,并且通过服务转发的udp包肯定可以到达对端的nat
|
||||
logger:debug("[sdlan_network] broadcast data networkd_id: ~p, src_mac: ~p, dst_mac: ~p",
|
||||
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]),
|
||||
@ -354,35 +310,25 @@ handle_cast({forward, _Sock, SrcMac, DstMac, _Packet}, State = #state{network_id
|
||||
%% 删除ip的占用并关闭channel
|
||||
handle_cast({unregister, _ClientId, Mac}, State = #state{network_id = NetworkId, endpoints = Endpoints}) ->
|
||||
logger:debug("[sdlan_network] networkd_id: ~p, unregister Mac: ~p", [NetworkId, sdlan_util:format_mac(Mac)]),
|
||||
case maps:take(Mac, Endpoints) of
|
||||
error ->
|
||||
{noreply, State};
|
||||
{#endpoint{monitor_ref = MRef}, NEndpoints} ->
|
||||
is_reference(MRef) andalso demonitor(MRef),
|
||||
{noreply, State#state{endpoints = NEndpoints}}
|
||||
end;
|
||||
{noreply, State#state{endpoints = maps:remove(Mac, Endpoints)}};
|
||||
|
||||
%% 需要判断,client是属于当前网络的
|
||||
handle_cast({update_hole, ClientId, Mac, Peer, NatType, V6Info}, State = #state{endpoints = Endpoints}) ->
|
||||
case maps:find(Mac, Endpoints) of
|
||||
{ok, Endpoint0 = #endpoint{client_id = ClientId0, ip = Ip, hole = OldHole}} when ClientId =:= ClientId0 ->
|
||||
case OldHole =:= undefined orelse (OldHole#hole.peer =/= Peer orelse OldHole#hole.nat_type =/= NatType) of
|
||||
true ->
|
||||
NatChangedEvent = sdlan_pb:encode_msg(#sdl_nat_changed_event{
|
||||
mac = Mac,
|
||||
ip = Ip
|
||||
}),
|
||||
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_NAT_CHANGED, NatChangedEvent>>,
|
||||
{ok, Endpoint0 = #endpoint{ip = Ip, client_id = ClientId0, hole = OldHole}} when ClientId =:= ClientId0 ->
|
||||
NHole = #hole{peer = Peer, nat_type = NatType},
|
||||
maybe
|
||||
true ?= same_hole(OldHole, NHole),
|
||||
NatChangedEvent = sdlan_pb:encode_msg(#sdl_nat_changed_event{
|
||||
mac = Mac,
|
||||
ip = Ip
|
||||
}),
|
||||
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_NAT_CHANGED, NatChangedEvent/binary>>,
|
||||
|
||||
broadcast(fun(#endpoint{hole = #hole{peer = Peer}}) ->
|
||||
sdlan_stun_pool:send_packet(Peer, EventPacket)
|
||||
end, [Mac], Endpoints);
|
||||
false ->
|
||||
ok
|
||||
EndpointPeers = endpoint_peers([Mac], Endpoints),
|
||||
sdlan_stun_pool:send_packets(EndpointPeers, EventPacket)
|
||||
end,
|
||||
Endpoint = Endpoint0#endpoint{hole = #hole{peer = Peer, nat_type = NatType}, v6_info = V6Info},
|
||||
|
||||
{noreply, State#state{endpoints = maps:put(Mac, Endpoint, Endpoints)}};
|
||||
{noreply, State#state{endpoints = maps:put(Mac, Endpoint0#endpoint{hole = NHole, v6_info = V6Info}, Endpoints)}};
|
||||
_ ->
|
||||
{noreply, State}
|
||||
end.
|
||||
@ -411,10 +357,9 @@ terminate(Reason, #state{network_id = NetworkId, endpoints = Endpoints}) ->
|
||||
NetworkShutdownEvent = sdlan_pb:encode_msg(#sdl_network_shutdown_event {
|
||||
message = <<"Network shutdown">>
|
||||
}),
|
||||
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_NETWORK_SHUTDOWN, NetworkShutdownEvent>>,
|
||||
broadcast(fun(#endpoint{hole = #hole{peer = Peer}}) ->
|
||||
sdlan_stun_pool:send_packet(Peer, EventPacket)
|
||||
end, Endpoints),
|
||||
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_NETWORK_SHUTDOWN, NetworkShutdownEvent/binary>>,
|
||||
EndpointPeers = endpoint_peers([], Endpoints),
|
||||
sdlan_stun_pool:send_packets(EndpointPeers, EventPacket),
|
||||
ok.
|
||||
|
||||
%% @private
|
||||
@ -443,10 +388,6 @@ limiting_check(ThrottleKey) ->
|
||||
end
|
||||
end.
|
||||
|
||||
-spec broadcast(Fun :: binary(), Endpoints :: map()) -> no_return().
|
||||
broadcast(Fun, Endpoints) when is_function(Fun, 1), is_map(Endpoints) ->
|
||||
broadcast(Fun, [], Endpoints).
|
||||
|
||||
-spec broadcast(Fun :: binary(), ExcludeMacs :: [binary()], Endpoints :: map()) -> no_return().
|
||||
broadcast(Fun, ExcludeMacs, Endpoints) when is_function(Fun, 1), is_map(Endpoints), is_list(ExcludeMacs) ->
|
||||
maps:foreach(fun(Mac, Endpoint) ->
|
||||
@ -458,17 +399,6 @@ broadcast(Fun, ExcludeMacs, Endpoints) when is_function(Fun, 1), is_map(Endpoint
|
||||
end
|
||||
end, Endpoints).
|
||||
|
||||
-spec broadcast_peers(Fun :: binary(), ExcludeMacs :: [binary()], Endpoints :: map()) -> no_return().
|
||||
broadcast_peers(Fun, ExcludeMacs, Endpoints) when is_function(Fun, 1), is_map(Endpoints), is_list(ExcludeMacs) ->
|
||||
maps:filtermap(fun(Mac, Endpoint) ->
|
||||
case lists:member(Mac, ExcludeMacs) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
Fun(Endpoint)
|
||||
end
|
||||
end, Endpoints).
|
||||
|
||||
%% 解析IpAddr: <<"192.168.172/24">>
|
||||
-spec parse_ipaddr(IpAddr0 :: binary()) -> {IpAddr :: binary(), MaskLen :: integer()}.
|
||||
parse_ipaddr(IpAddr0) when is_binary(IpAddr0) ->
|
||||
@ -522,4 +452,21 @@ search_endpoint0(F, Iter) when is_function(F, 1) ->
|
||||
end;
|
||||
'none' ->
|
||||
error
|
||||
end.
|
||||
end.
|
||||
|
||||
-spec same_hole(Hole :: #hole{}, Hole :: #hole{}) -> boolean().
|
||||
same_hole(#hole{peer = OldPeer, nat_type = OldNatType}, #hole{peer = Peer, nat_type = NatType}) when OldPeer =:= Peer, OldNatType =:= NatType ->
|
||||
true;
|
||||
same_hole(_, _) ->
|
||||
false.
|
||||
|
||||
-spec endpoint_peers(ExcludeMacs :: list(), Endpoints :: map()) -> Peers :: list().
|
||||
endpoint_peers(ExcludeMacs, Endpoints) when is_list(ExcludeMacs), is_map(Endpoints) ->
|
||||
maps:values(maps:filtermap(fun(Mac, #endpoint{hole = #hole{peer = Peer}}) ->
|
||||
case lists:member(Mac, ExcludeMacs) of
|
||||
true ->
|
||||
false;
|
||||
false ->
|
||||
{true, Peer}
|
||||
end
|
||||
end, Endpoints)).
|
||||
@ -16,7 +16,7 @@
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0, send_packets/1, send_packet/2]).
|
||||
-export([start_link/0, send_packets/2, send_packet/2]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
||||
@ -33,11 +33,13 @@
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
|
||||
-spec send_packet(Peer :: {Ip :: inet:ip4_address(), Port :: integer()}, Packet :: binary()) -> no_return().
|
||||
send_packet(Peer, Packet) ->
|
||||
gen_server:cast(?SERVER, {send_packet, Peer, Packet}).
|
||||
|
||||
send_packets(Packets) when is_list(Packets) ->
|
||||
gen_server:cast(?SERVER, {send_packets, Packets}).
|
||||
-spec send_packet(Peers :: [{Ip :: inet:ip4_address(), Port :: integer()}], Packet :: binary()) -> no_return().
|
||||
send_packets(Peers, Packet) when is_list(Peers), is_binary(Packet) ->
|
||||
gen_server:cast(?SERVER, {send_packets, Peers, Packet}).
|
||||
|
||||
%% @doc Spawns the server and registers the local name (unique)
|
||||
-spec(start_link() ->
|
||||
@ -103,9 +105,9 @@ handle_cast({send_packet, {Ip, Port}, Packet}, State = #state{workers = Workers,
|
||||
NewIdx = (Idx rem Num) + 1,
|
||||
{noreply, State#state{idx = NewIdx}};
|
||||
|
||||
handle_cast({send_packets, Packets}, State = #state{workers = Workers, idx = Idx, num = Num}) ->
|
||||
handle_cast({send_packets, Peers, Packet}, State = #state{workers = Workers, idx = Idx, num = Num}) ->
|
||||
{Sock, _} = element(Idx, Workers),
|
||||
lists:foreach(fun({{Ip, Port}, Data}) -> gen_udp:send(Sock, Ip, Port, Data) end, Packets),
|
||||
lists:foreach(fun({Ip, Port}) -> gen_udp:send(Sock, Ip, Port, Packet) end, Peers),
|
||||
NewIdx = (Idx rem Num) + 1,
|
||||
{noreply, State#state{idx = NewIdx}}.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user