From d64cb4235fab4acce4fed498370ebe87c4eacf3f Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Mon, 26 Jan 2026 16:38:34 +0800 Subject: [PATCH] fix --- apps/sdlan/src/sdlan_network.erl | 47 ++++++++++-------- apps/sdlan/src/sdlan_stun.erl | 84 ++++++++++++++++---------------- message.proto | 6 +++ 3 files changed, 76 insertions(+), 61 deletions(-) diff --git a/apps/sdlan/src/sdlan_network.erl b/apps/sdlan/src/sdlan_network.erl index 4b02a5e..43cfc0b 100644 --- a/apps/sdlan/src/sdlan_network.erl +++ b/apps/sdlan/src/sdlan_network.erl @@ -24,8 +24,8 @@ %% API -export([start_link/2]). --export([get_name/1, get_pid/1, lookup_pid/1, attach/6, peer_info/6, 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([get_name/1, get_pid/1, lookup_pid/1, attach/6, peer_info/5, unregister/3, debug_info/1, get_network_id/1, arp_query/2]). +-export([forward/5, stun_request/4, disable_client/2, dropout_client/2]). -export([test_event/1]). %% gen_server callbacks @@ -45,8 +45,8 @@ hole :: #hole{}, %% 记录ip和ip_v6的映射关系, #{ip_addr :: integer() => {}} v6_info :: undefined | #sdl_v6_info{}, - - last_seen :: integer() %% monotonic_time(second) + session_token :: binary(), + last_seen :: integer() %% monotonic_time(second), }). -record(state, { @@ -107,18 +107,18 @@ arp_query(Pid, TargetIp) when is_pid(Pid), is_integer(TargetIp) -> unregister(Pid, ClientId, Mac) when is_pid(Pid), is_binary(ClientId), is_binary(Mac) -> gen_server:cast(Pid, {unregister, ClientId, Mac}). --spec peer_info(Pid :: pid(), Sock :: inet:socket(), SrcIp :: inet:ip4_address(), SrcPort :: integer(), PacketId :: integer(), Query :: #sdl_query_info{}) -> no_return(). -peer_info(Pid, Sock, ClientIp, ClientPort, PacketId, Query) when is_pid(Pid) -> - gen_server:cast(Pid, {peer_info, Sock, ClientIp, ClientPort, PacketId, Query}). +-spec peer_info(Pid :: pid(), Sock :: inet:socket(), Peer :: {inet:ip4_address(), integer()}, PacketId :: integer(), Query :: #sdl_query_info{}) -> no_return(). +peer_info(Pid, Sock, ClientPeer, PacketId, Query) when is_pid(Pid) -> + gen_server:cast(Pid, {peer_info, Sock, ClientPeer, PacketId, Query}). -spec forward(pid(), Sock :: any(), SrcMac :: binary(), DstMac :: binary(), Packet :: binary()) -> no_return(). forward(Pid, Sock, SrcMac, DstMac, Packet) when is_pid(Pid), is_binary(SrcMac), is_binary(DstMac), is_binary(Packet) -> gen_server:cast(Pid, {forward, Sock, SrcMac, DstMac, Packet}). %% 更新ip地址对应的nat关系 --spec update_hole(Pid :: pid(), ClientId :: binary(), Mac :: binary(), Peer :: tuple(), NatType :: integer(), V6Info :: undefined | #sdl_v6_info{}) -> no_return(). -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 stun_request(Pid :: pid(), Sock :: inet:socket(), ClientPeer :: {inet:ip4_address(), integer()}, StunRequest :: #sdl_stun_request{}) -> no_return(). +stun_request(Pid, Sock, ClientPeer, StunRequest) when is_pid(Pid) -> + gen_server:cast(Pid, {stun_request, Sock, ClientPeer, StunRequest}). -spec disable_client(Pid :: pid(), ClientId :: binary()) -> ok. disable_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) -> @@ -208,10 +208,11 @@ handle_call({attach, Peer, ClientId, Mac, Ip, Hostname}, _From, sdlan_stun_pool:send_packets(EndpointPeers, EventPacket) end, - Endpoint = #endpoint{client_id = ClientId, mac = Mac, ip = Ip, hostname = Hostname, + SessionToken = gen_session_token(), + Endpoint = #endpoint{client_id = ClientId, mac = Mac, ip = Ip, hostname = Hostname, session_token = SessionToken, hole = #hole{peer = Peer, nat_type = 0}, last_seen = erlang:monotonic_time(second)}, - {reply, {ok, Domain, MaskLen, AesKey}, State#state{endpoints = maps:put(Mac, Endpoint, Endpoints)}}; + {reply, {ok, AesKey, SessionToken}, State#state{endpoints = maps:put(Mac, Endpoint, Endpoints)}}; %% client设置为禁止状态,不允许重连 handle_call({disable_client, ClientId}, _From, State = #state{endpoints = Endpoints}) -> @@ -234,8 +235,6 @@ handle_call({arp_query, TargetIp}, _From, State = #state{endpoints = Endpoints}) {reply, error, State} end; - - handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = IpAddr, mask_len = MaskLen, endpoints = Endpoints}) -> Reply = #{ <<"network_id">> => NetworkId, @@ -254,7 +253,7 @@ handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = I %% 网络存在的nat_peer信息 %% TODO SrcMac不存在的时候需要重新校验 -handle_cast({peer_info, Sock, ClientIp, ClientPort, PacketId, #sdl_query_info{src_mac = SrcMac, dst_mac = DstMac}}, State = #state{endpoints = Endpoints}) -> +handle_cast({peer_info, Sock, {ClientIp, ClientPort}, PacketId, #sdl_query_info{src_mac = SrcMac, dst_mac = DstMac}}, State = #state{endpoints = Endpoints}) -> case maps:find(DstMac, Endpoints) of {ok, #endpoint{hole = #hole{peer = DstNatPeer = {{Ip0, Ip1, Ip2, Ip3}, DstNatPort}, nat_type = DstNatType}, v6_info = DstV6Info}} -> %% 让目标服务器发送sendRegister事件(2024-06-25 新增,提高打洞的成功率) @@ -341,12 +340,15 @@ handle_cast({unregister, _ClientId, Mac}, State = #state{network_id = NetworkId, %% 需要判断,client是属于当前网络的 %% 被拒绝的endpoint需要通知其重新验证 -handle_cast({update_hole, ClientId, Mac, Peer, NatType, V6Info}, State = #state{network_id = NetworkId, endpoints = Endpoints}) -> +handle_cast({stun_request, Sock, Peer = {ClientIp, ClientPort}, #sdl_stun_request{mac = Mac, client_id = ClientId, nat_type = NatType, v6_info = V6Info, session_token = ST}}, + State = #state{network_id = NetworkId, endpoints = Endpoints}) -> + case maps:find(Mac, Endpoints) of - {ok, Endpoint0 = #endpoint{ip = Ip, client_id = ClientId0, hole = OldHole}} when ClientId =:= ClientId0 -> + %% ClientId =:= ClientId0, SessionToken =:= SessionToken0 + {ok, Endpoint0 = #endpoint{ip = Ip, client_id = ClientId, hole = OldHole, session_token = ST}} -> NHole = #hole{peer = Peer, nat_type = NatType}, maybe - %% hole changed -> notify peers + %% hole changed -> notify peers true ?= not same_hole(OldHole, NHole), NatChangedEvent = sdlan_pb:encode_msg(#sdl_nat_changed_event{ mac = Mac, @@ -364,7 +366,7 @@ handle_cast({update_hole, ClientId, Mac, Peer, NatType, V6Info}, State = #state{ network_id = NetworkId }), EventPacket = <>, - sdlan_stun_pool:send_packet(Peer, EventPacket), + gen_udp:send(Sock, ClientIp, ClientPort, EventPacket), {noreply, State} end. @@ -548,4 +550,9 @@ endpoint_peers(ExcludeMacs, Endpoints) when is_list(ExcludeMacs), is_map(Endpoin false -> {true, Peer} end - end, Endpoints)). \ No newline at end of file + end, Endpoints)). + +-spec gen_session_token() -> binary(). +gen_session_token() -> + Bytes = crypto:strong_rand_bytes(32), + base64:encode(Bytes). \ No newline at end of file diff --git a/apps/sdlan/src/sdlan_stun.erl b/apps/sdlan/src/sdlan_stun.erl index d79197f..5631ea0 100644 --- a/apps/sdlan/src/sdlan_stun.erl +++ b/apps/sdlan/src/sdlan_stun.erl @@ -89,7 +89,7 @@ handle_cast({stun_relay, Ip, Port, Reply}, State = #state{socket = Sock}) -> {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), NewState :: #state{}}). handle_info({udp, Sock, PeerIp, PeerPort, Packet}, State = #state{socket = Sock}) -> - catch handle_packet(Sock, PeerIp, PeerPort, Packet), + handle_packet(Sock, PeerIp, PeerPort, Packet), {noreply, State}; handle_info({udp_error , Sock, Reason}, State = #state{socket = Sock}) -> {stop, Reason, State}; @@ -120,55 +120,51 @@ code_change(_OldVsn, State = #state{}, _Extra) -> %%%=================================================================== -spec handle_packet(Sock :: inet:socket(), Ip :: inet:ip4_address(), Port :: integer(), Packet :: binary()) -> no_return(). - %% 带上token或者网络id来注册 handle_packet(Sock, Ip, Port, <>) -> sdlan_register_worker_sup:start_worker(Sock, Ip, Port, Body); %% 查询信息 handle_packet(Sock, ClientIp, ClientPort, <>) -> - Query = #sdl_query_info{network_id = NetworkId} = sdlan_pb:decode_msg(Body, sdl_query_info), maybe + Query = catch sdlan_pb:decode_msg(Body, sdl_query_info), + #sdl_query_info{network_id = NetworkId} ?= Query, + {ok, NetworkPid} ?= sdlan_network:lookup_pid(NetworkId), - sdlan_network:peer_info(NetworkPid, Sock, ClientIp, ClientPort, PacketId, Query) + sdlan_network:peer_info(NetworkPid, Sock, {ClientIp, ClientPort}, PacketId, Query) end; -handle_packet(Sock, Ip, Port, <>) -> - #sdl_stun_request{cookie = Cookie, client_id = ClientId, network_id = NetworkId, mac = Mac, nat_type = NatType, v6_info = V6Info} = sdlan_pb:decode_msg(Body, sdl_stun_request), - %% 告知网络当前的ip对应的nat的映射关系 - case sdlan_network:get_pid(NetworkId) of - undefined -> - logger:debug("[sdlan_stun] stun_request network_id: ~p, client_id: ~p, not found", [NetworkId, ClientId]), - ok; - NetworkPid when is_pid(NetworkPid) -> - sdlan_network:update_hole(NetworkPid, ClientId, Mac, {Ip, Port}, NatType, V6Info), - StunReply = sdlan_pb:encode_msg(#sdl_stun_reply{ - cookie = Cookie - }), - ok = gen_udp:send(Sock, Ip, Port, <>), - logger:debug("[sdlan_stun] stun_request network_id: ~p, client_id: ~p, hole: ~p", [NetworkId, ClientId, {Ip, Port}]) +%% 处理心跳逻辑 +handle_packet(Sock, ClientIp, ClientPort, <>) -> + maybe + StunRequest = catch sdlan_pb:decode_msg(Body, sdl_stun_request), + #sdl_stun_request{network_id = NetworkId} ?= StunRequest, + {ok, NetworkPid} ?= sdlan_network:lookup_pid(NetworkId), + %% 告知网络当前的ip对应的nat的映射关系 + sdlan_network:stun_request(NetworkPid, Sock, {ClientIp, ClientPort}, StunRequest) end; %% 网络nat类型的探测机制, 需要借助其他服务一起才能实现 %% 辅助节点没有assist的配置,不支持attr = 2的探测 handle_packet(Sock, Ip, Port, <>) -> - #sdl_stun_probe{cookie = Cookie, attr = Attr} = sdlan_pb:decode_msg(Body, sdl_stun_probe), - logger:debug("[sdlan_stun] get stun_probe request, att: ~p", [Attr]), + maybe + #sdl_stun_probe{cookie = Cookie, attr = Attr} ?= sdlan_pb:decode_msg(Body, sdl_stun_probe), + logger:debug("[sdlan_stun] get stun_probe request, att: ~p", [Attr]), + ProbeReply = sdlan_pb:encode_msg(#sdl_stun_probe_reply { + cookie = Cookie, + port = Port, + ip = int_ip(Ip) + }), + Packet = <>, - ProbeReply = sdlan_pb:encode_msg(#sdl_stun_probe_reply { - cookie = Cookie, - port = Port, - ip = int_ip(Ip) - }), - Packet = <>, - - case Attr of - ?STUN_ATTR_CHANGE_NONE -> - ok = gen_udp:send(Sock, Ip, Port, Packet); - ?STUN_ATTR_CHANGE_PORT -> - sdlan_stun_port_assist:stun_relay(Ip, Port, Packet); - ?STUN_ATTR_CHANGE_PEER -> - sdlan_stun_peer_assist:stun_relay(Ip, Port, Packet) + case Attr of + ?STUN_ATTR_CHANGE_NONE -> + gen_udp:send(Sock, Ip, Port, Packet); + ?STUN_ATTR_CHANGE_PORT -> + sdlan_stun_port_assist:stun_relay(Ip, Port, Packet); + ?STUN_ATTR_CHANGE_PEER -> + sdlan_stun_peer_assist:stun_relay(Ip, Port, Packet) + end end; %% 转发消息, 跨服务器的stun_reply的转发通过socket来转发 @@ -177,23 +173,29 @@ handle_packet(Sock, _, _, <>) -> - Data = #sdl_data{network_id = NetworkId, src_mac = SrcMac, dst_mac = DstMac, ttl = TTL} = sdlan_pb:decode_msg(Body, sdl_data), - logger:debug("[sdlan_stun] stun data, src_mac: ~p, dst_mac: ~p", [sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]), maybe + Data = catch sdlan_pb:decode_msg(Body, sdl_data), + #sdl_data{network_id = NetworkId, src_mac = SrcMac, dst_mac = DstMac, ttl = TTL} ?= Data, {ok, NetworkPid} ?= sdlan_network:lookup_pid(NetworkId), + logger:debug("[sdlan_stun] stun data, src_mac: ~p, dst_mac: ~p", [sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]), %% 重新打包数据ttl需要减1 NData = sdlan_pb:encode_msg(Data#sdl_data{ttl = TTL - 1, is_p2p = false}), sdlan_network:forward(NetworkPid, Sock, SrcMac, DstMac, <>) end; -handle_packet(Sock, Ip, Port, <>) -> - #sdl_arp_request{network_id = NetworkId, target_ip = TargetIp} = sdlan_pb:decode_msg(Body, sdl_arp_request), - logger:debug("[sdlan_stun] stun sdl_arp_request, network_id: ~p, target_ip: ~p", [NetworkId, TargetIp]), +%% 执行arp查询 +handle_packet(Sock, ClientIp, ClientPort, <>) -> maybe + #sdl_arp_request{network_id = NetworkId, target_ip = TargetIp} ?= sdlan_pb:decode_msg(Body, sdl_arp_request), + logger:debug("[sdlan_stun] stun sdl_arp_request, network_id: ~p, target_ip: ~p", [NetworkId, TargetIp]), {ok, NetworkPid} ?= sdlan_network:lookup_pid(NetworkId), {ok, TargetMac} ?= sdlan_network:arp_query(NetworkPid, TargetIp), - ArpResponse = sdlan_pb:encode_msg(#sdl_arp_response{network_id = NetworkId, target_ip = TargetIp, target_mac = TargetMac}), - ok ?= gen_udp:send(Sock, Ip, Port, ArpResponse) + ArpResponse = sdlan_pb:encode_msg(#sdl_arp_response{ + network_id = NetworkId, + target_ip = TargetIp, + target_mac = TargetMac + }), + gen_udp:send(Sock, ClientIp, ClientPort, ArpResponse) end. -spec int_ip(tuple()) -> integer(). diff --git a/message.proto b/message.proto index d32d0bd..a3aa260 100644 --- a/message.proto +++ b/message.proto @@ -101,6 +101,12 @@ message SDLStunRequest { bytes session_token = 7; } +message SDLStunReply { + uint32 network_id = 1; + uint32 code = 2; + string message = 3; +} + message SDLData { uint32 network_id = 1; bytes src_mac = 2;