@ -1,382 +0,0 @@
% % % -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
% % % @ author licheng5
% % % @ copyright ( C ) 2020 , < COMPANY >
% % % @ doc
% % %
% % % @ end
% % % Created : 10 . 12 月 2020 上 午 11 : 17
% % % -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
- module ( sdlan_channel ) .
- author ( " licheng5 " ) .
- behaviour ( gen_server ) .
- include ( " sdlan.hrl " ) .
- include ( " sdlan_pb.hrl " ) .
% % 心 跳 包 监 测 机 制
- define ( PING_TICKER , 15000 ) .
% % 注 册 失 败 的 的 错 误 码
% % token不存在
- define ( NAK_INVALID_TOKEN , 1 ) .
% % 节 点 被 禁 用
- define ( NAK_NODE_DISABLE , 2 ) .
% % 没 有 IP地址可以用
- define ( NAK_NO_IP , 3 ) .
% % 网 络 错 误
- define ( NAK_NETWORK_FAULT , 4 ) .
% % 内 部 错 误
- define ( NAK_INTERNAL_FAULT , 5 ) .
% % hostname被占用
- define ( NAK_HOSTNAME_USED , 6 ) .
% % 升 级 策 略
- define ( UPGRADE_NONE , 0 ) .
- define ( UPGRADE_NORMAL , 1 ) .
- define ( UPGRADE_FORCE , 2 ) .
% % API
- export ( [ start_link / 4 ] ) .
- export ( [ publish_command / 4 , send_event / 3 , stop / 2 , move_network / 4 ] ) .
- export ( [ init / 1 , handle_call / 3 , handle_cast / 2 , handle_info / 2 , terminate / 2 , code_change / 3 ] ) .
- record ( state , {
transport ,
socket ,
client_id : : undefined | binary ( ) ,
pub_key : : undefined | binary ( ) ,
token : : undefined | binary ( ) ,
% % 分 配 的 ip地址
assign_ip : : undefined | integer ( ) ,
% % 网 络 相 关 信 息 id
network_pid : : undefined | pid ( ) ,
% % mac地址
mac : : undefined | binary ( ) ,
% % 标 记 是 否 已 经 注 册
is_registered = false ,
% % 记 录 ping的次数
ping_counter = 0 ,
% % 发 送 消 息 对 应 的 id
packet_id = 1 : : integer ( ) ,
% % 请 求 响 应 的 对 应 关 系
inflight = #{ }
} ) .
% % 向 通 道 中 写 入 消 息
- spec publish_command ( Pid : : pid ( ) , ReceiverPid : : pid ( ) , CommandType : : integer ( ) , Msg : : binary ( ) ) - > Ref : : reference ( ) .
publish_command ( Pid , ReceiverPid , CommandType , Msg ) when is_pid ( Pid ) , is_pid ( ReceiverPid ) , is_integer ( CommandType ) , is_binary ( Msg ) - >
Ref = make_ref ( ) ,
Pid ! { publish_command , ReceiverPid , Ref , CommandType , Msg } ,
Ref .
% % 网 络 迁 移 是 一 种 特 殊 的 指 令 信 息 , 需 要 单 独 处 理
- spec move_network ( Pid : : pid ( ) , ReceiverPid : : pid ( ) , NetworkPid : : pid ( ) , HostName : : binary ( ) ) - > Ref : : reference ( ) .
move_network ( Pid , ReceiverPid , NetworkPid , HostName ) when is_pid ( Pid ) , is_pid ( ReceiverPid ) , is_pid ( NetworkPid ) , is_binary ( HostName ) - >
Ref = make_ref ( ) ,
Pid ! { move_network , ReceiverPid , Ref , NetworkPid , HostName } ,
Ref .
% % 向 通 道 中 写 入 消 息
- spec send_event ( Pid : : pid ( ) , EventType : : integer ( ) , Event : : binary ( ) ) - > no_return ( ) .
send_event ( Pid , EventType , Event ) when is_pid ( Pid ) , is_integer ( EventType ) , is_binary ( Event ) - >
Pid ! { send_event , EventType , Event } .
% % 关 闭 方 法
- spec stop ( Pid : : pid ( ) , Reason : : any ( ) ) - > no_return ( ) .
stop ( undefined , _ Reason ) - >
ok ;
stop ( Pid , Reason ) when is_pid ( Pid ) - >
Pid ! { stop , Reason } .
% % -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
% % esockd callback
% % -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
start_link ( Ref , _ Socket , Transport , Opts ) - >
{ ok , proc_lib : spawn_link ( ? MODULE , init , [ [ Ref , Transport , Opts ] ] ) } .
init ( [ Ref , Transport , _ Opts = [ ] ] ) - >
{ ok , Socket } = ranch : handshake ( Ref ) ,
logger : debug ( " [sdlan_channel] get a new connection: ~p " , [ Socket ] ) ,
Transport : setopts ( Socket , [ { active , true } , { packet , 2 } ] ) ,
erlang : start_timer ( ? PING_TICKER , self ( ) , ping_ticker ) ,
gen_server : enter_loop ( ? MODULE , [ ] , #state { transport = Transport , socket = Socket } ) .
handle_call ( _ Request , _ From , State ) - >
{ reply , ok , State } .
handle_cast ( _ Msg , State ) - >
{ noreply , State } .
% % 带 上 token或者网络id来注册
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 ) ,
% % 参 数 检 查
logger : debug ( " [sdlan_channel] client_id: ~p , public_key: ~p , token: ~p , network_code: ~p " , [ ClientId , PubKey , Token , NetworkCode ] ) ,
true = ( Mac =/= < < > > andalso PubKey =/= < < > > andalso ClientId =/= < < > > ) ,
% % Mac地址不能是广播地址
true = not ( sdlan_util : is_multicast_mac ( Mac ) orelse sdlan_util : is_broadcast_mac ( Mac ) ) ,
AuthResult = if
Token / = < < > > - >
logger : debug ( " [sdlan_channel] auth token: ~p " , [ Token ] ) ,
sdlan_api : auth_token ( ClientId , Token , Version ) ;
NetworkCode / = < < > > - >
logger : debug ( " [sdlan_channel] auth network code: ~p " , [ NetworkCode ] ) ,
sdlan_api : auth_network_code ( ClientId , NetworkCode , Version )
end ,
case AuthResult of
{ ok , #{ < < " result " > > : = #{ < < " network_id " > > : = NetworkId , < < " upgrade_type " > > : = UpgradeType , < < " upgrade_prompt " > > : = UpgradePrompt , < < " upgrade_address " > > : = UpgradeAddress } } } when is_integer ( NetworkId ) - >
logger : debug ( " [sdlan_channel] client_id: ~p , mac: ~p , token: ~p , version: ~p , registerd, alloc network_id: ~p " , [ ClientId , sdlan_util : format_mac ( Mac ) , Token , Version , NetworkId ] ) ,
% % 建 立 到 network的对应关系
case sdlan_network : get_pid ( NetworkId ) of
NetworkPid when is_pid ( NetworkPid ) - >
try sdlan_network : assign_ip_addr ( NetworkPid , self ( ) , ClientId , Mac ) of
{ ok , Domain , NetAddr , NetBitLen , AesKey } - >
RsaPubKey = sdlan_cipher : rsa_pem_decode ( PubKey ) ,
EncodedAesKey = rsa_encode ( AesKey , RsaPubKey ) ,
RegisterSuperAck = sdlan_pb : encode_msg ( #sdl_register_super_ack {
dev_addr = #sdl_dev_addr {
network_id = NetworkId ,
net_addr = NetAddr ,
mac = Mac ,
net_bit_len = NetBitLen ,
network_domain = Domain
} ,
aes_key = EncodedAesKey ,
upgrade_type = UpgradeType ,
upgrade_prompt = UpgradePrompt ,
upgrade_address = UpgradeAddress
} ) ,
% % 发 送 确 认 信 息
Reply = < < PacketId : 32 , ? PACKET_REGISTER_SUPER_ACK , RegisterSuperAck / binary > > ,
Transport : send ( Sock , Reply ) ,
logger : debug ( " [sdlan_channel] client_id: ~p , mac: ~p , alloc ip: ~p , register will send ack, aes_key: ~p , enc_aes_key: ~p " ,
[ ClientId , sdlan_util : format_mac ( Mac ) , sdlan_ipaddr : int_to_ipv4 ( NetAddr ) , AesKey , EncodedAesKey ] ) ,
% % 设 置 节 点 的 在 线 状 态
Result = sdlan_api : node_online ( ClientId , NetworkId , sdlan_ipaddr : int_to_ipv4 ( NetAddr ) ) ,
logger : debug ( " [sdlan_channel] client_id: ~p , set none online, result is: ~p " , [ ClientId , Result ] ) ,
case UpgradeType =:= ? UPGRADE_FORCE of
true - >
logger : warning ( " [sdlan_channel] client_id: ~p , need upgrade force! " , [ ClientId ] ) ,
{ stop , normal , State } ;
false - >
{ noreply , State #state { client_id = ClientId , mac = Mac , assign_ip = NetAddr , network_pid = NetworkPid , pub_key = PubKey , is_registered = true } }
end ;
{ error , no_ip } - >
logger : warning ( " [sdlan_channel] client_id: ~p , token: ~p , register get error: no_ip " , [ ClientId , Token ] ) ,
Transport : send ( Sock , register_nak_reply ( PacketId , ? NAK_NO_IP , < < " No Ip address " > > ) ) ,
{ stop , normal , State } ;
{ error , host_name_used } - >
logger : warning ( " [sdlan_channel] client_id: ~p , token: ~p , register get error: host_name_used " , [ ClientId , Token ] ) ,
Transport : send ( Sock , register_nak_reply ( PacketId , ? NAK_HOSTNAME_USED , < < " Host Name Used " > > ) ) ,
{ stop , normal , State } ;
{ error , client_disabled } - >
logger : warning ( " [sdlan_channel] client_id: ~p , token: ~p , register get error: client_disabled " , [ ClientId , Token ] ) ,
Transport : send ( Sock , register_nak_reply ( PacketId , ? NAK_NODE_DISABLE , < < " Client Connection Disable " > > ) ) ,
{ stop , normal , State }
catch _ : Error : Stack - >
logger : warning ( " [sdlan_channel] get error: ~p , stack: ~p " , [ Error , Stack ] )
end ;
undefined - >
logger : warning ( " [sdlan_channel] client_id: ~p , token: ~p , register get error: network not found " , [ ClientId , Token ] ) ,
Transport : send ( Sock , register_nak_reply ( PacketId , ? NAK_INTERNAL_FAULT , < < " Internal Error " > > ) ) ,
{ stop , normal , State }
end ;
{ ok , #{ < < " error " > > : = #{ < < " code " > > : = 1 , < < " message " > > : = Message } } } - >
logger : warning ( " [sdlan_channel] client_id: ~p , token: ~p , register get error: ~ ts, error_code: 1 " , [ ClientId , Token , Message ] ) ,
Transport : send ( Sock , register_nak_reply ( PacketId , ? NAK_INVALID_TOKEN , Message ) ) ,
{ stop , normal , State } ;
{ ok , #{ < < " error " > > : = #{ < < " code " > > : = 2 , < < " message " > > : = Message } } } - >
logger : warning ( " [sdlan_channel] client_id: ~p , token: ~p , register get error: ~p , error_code: 2 " , [ ClientId , Token , Message ] ) ,
Transport : send ( Sock , register_nak_reply ( PacketId , ? NAK_NODE_DISABLE , Message ) ) ,
{ stop , normal , State } ;
{ error , Reason } - >
logger : warning ( " [sdlan_channel] client_id: ~p , token: ~p , register get error: ~p " , [ ClientId , Token , Reason ] ) ,
Transport : send ( Sock , register_nak_reply ( PacketId , ? NAK_NETWORK_FAULT , < < " Network Error " > > ) ) ,
{ stop , normal , State }
end ;
handle_info ( { tcp , Sock , < < PacketId : 32 , ? PACKET_QUERY_INFO , Body / binary > > } , State = #state { transport = Transport , socket = Sock , network_pid = NetworkPid , mac = SrcMac , is_registered = true } ) when is_pid ( NetworkPid ) - >
#sdl_query_info { dst_mac = DstMac } = sdlan_pb : decode_msg ( Body , sdl_query_info ) ,
case sdlan_network : peer_info ( NetworkPid , SrcMac , DstMac ) of
error - >
logger : debug ( " [sdlan_channel] query_info src_mac is: ~p , dst_mac: ~p , nat_peer not found " ,
[ sdlan_util : format_mac ( SrcMac ) , sdlan_util : format_mac ( DstMac ) ] ) ,
Transport : send ( Sock , < < PacketId : 32 , ? PACKET_EMPTY > > ) ,
{ noreply , State } ;
{ ok , { NatPeer = { { Ip0 , Ip1 , Ip2 , Ip3 } , NatPort } , NatType } , V6Info } - >
logger : debug ( " [sdlan_channel] query_info src_mac is: ~p , dst_mac: ~p , nat_peer: ~p " ,
[ sdlan_util : format_mac ( SrcMac ) , sdlan_util : format_mac ( DstMac ) , NatPeer ] ) ,
PeerInfo = sdlan_pb : encode_msg ( #sdl_peer_info {
dst_mac = DstMac ,
v4_info = #sdl_v4_info {
port = NatPort ,
v4 = < < Ip0 , Ip1 , Ip2 , Ip3 > > ,
nat_type = NatType
} ,
v6_info = V6Info
} ) ,
Transport : send ( Sock , < < PacketId : 32 , ? PACKET_PEER_INFO , PeerInfo / binary > > ) ,
{ noreply , State }
end ;
handle_info ( { tcp , _ Sock , < < 0 : 32 , ? PACKET_PING > > } , State = #state { transport = Transport , socket = Sock , ping_counter = PingCounter } ) - >
% logger : debug ( " [sdlan_channel] client_id: ~p , get ping " , [ ClientId ] ) ,
Transport : send ( Sock , < < 0 : 32 , ? PACKET_PONG > > ) ,
{ noreply , State #state { ping_counter = PingCounter + 1 } } ;
handle_info ( { timeout , _ , ping_ticker } , State = #state { client_id = ClientId , ping_counter = PingCounter } ) - >
% % 等 待 下 一 次 的 心 跳 检 测
erlang : start_timer ( ? PING_TICKER , self ( ) , ping_ticker ) ,
case PingCounter > 0 of
true - >
{ noreply , State #state { ping_counter = 0 } } ;
false - >
logger : debug ( " [sdlan_channel] client_id: ~p , ping losted " , [ ClientId ] ) ,
{ stop , normal , State #state { ping_counter = 0 } }
end ;
% % 重 新 加 入 网 络
handle_info ( { move_network , ReceiverPid , Ref , NetworkPid , HostName } ,
State = #state { transport = Transport , socket = Sock , client_id = ClientId , mac = Mac , pub_key = PubKey , packet_id = PacketId , inflight = Inflight , is_registered = true } ) - >
% % 建 立 到 network的对应关系
case sdlan_network : assign_ip_addr ( NetworkPid , self ( ) , ClientId , Mac ) of
{ ok , Domain , NetAddr , NetBitLen , AesKey } - >
RsaPubKey = sdlan_cipher : rsa_pem_decode ( PubKey ) ,
EncodedAesKey = rsa_encode ( AesKey , RsaPubKey ) ,
{ ok , NetworkId } = sdlan_network : get_network_id ( NetworkPid ) ,
% % 发 送 确 认 信 息
ChangeNetworkCommand = sdlan_pb : encode_msg ( #sdl_change_network_command {
dev_addr = #sdl_dev_addr {
network_id = NetworkId ,
net_addr = NetAddr ,
net_bit_len = NetBitLen ,
network_domain = Domain
} ,
aes_key = EncodedAesKey
} ) ,
Command = < < PacketId : 32 , ? PACKET_COMMAND , ? PACKET_COMMAND_CHANGE_NETWORK , ChangeNetworkCommand / binary > > ,
Transport : send ( Sock , Command ) ,
% % 设 置 节 点 的 在 线 状 态
sdlan_api : node_online ( ClientId , NetworkId , sdlan_ipaddr : int_to_ipv4 ( NetAddr ) ) ,
logger : debug ( " [sdlan_channel] client_id: ~p , move_network will send command: ~p " , [ ClientId , Command ] ) ,
{ noreply , State #state { packet_id = PacketId + 1 , assign_ip = NetAddr , network_pid = NetworkPid , inflight = maps : put ( PacketId , { ReceiverPid , Ref } , Inflight ) } } ;
{ error , Reason } - >
logger : debug ( " [sdlan_channel] client_id: ~p , move_network get error: ~p " , [ ClientId , Reason ] ) ,
Transport : send ( Sock , register_nak_reply ( 0 , ? NAK_NO_IP , < < " No Ip address " > > ) ) ,
ReceiverPid ! { command_reply , Ref , { error , < < " assign_ip error, no ip free " > > } } ,
{ noreply , State }
end ;
% % 发 送 指 令 信 息
handle_info ( { send_event , EventType , Event } , State = #state { transport = Transport , socket = Sock , client_id = ClientId , is_registered = true } ) - >
logger : debug ( " [sdlan_channel] client_id: ~p , will send eventType: ~p , event: ~p " , [ ClientId , EventType , Event ] ) ,
Transport : send ( Sock , < < 0 : 32 , ? PACKET_EVENT , EventType , Event / binary > > ) ,
{ noreply , State } ;
% % 网 络 流 量 统 计
handle_info ( { tcp , _ Sock , < < 0 : 32 , ? PACKET_FLOW_TRACER , Body / binary > > } , State = #state { client_id = ClientId , network_pid = NetworkPid , is_registered = true } ) when is_pid ( NetworkPid ) - >
#sdl_flows { forward_num = ForwardNum , p2p_num = P2PNum , inbound_num = InboundNum } = sdlan_pb : decode_msg ( Body , sdl_flows ) ,
{ ok , NetworkId } = sdlan_network : get_network_id ( NetworkPid ) ,
ReportResult = sdlan_api : flow_report ( ClientId , NetworkId , ForwardNum , P2PNum , InboundNum ) ,
logger : debug ( " [sdlan_channel] flow_tracer, forward: ~p , p2p: ~p , inbound: ~p , result: ~p " , [ ClientId , ForwardNum , P2PNum , InboundNum , ReportResult ] ) ,
{ noreply , State } ;
% % 取 消 注 册
handle_info ( { tcp , _ Sock , < < 0 : 32 , ? PACKET_UNREGISTER > > } , State = #state { client_id = ClientId , network_pid = NetworkPid , is_registered = true } ) when is_pid ( NetworkPid ) - >
logger : warning ( " [sdlan_channel] unregister client_id: ~p " , [ ClientId ] ) ,
% sdlan_network : unregister ( NetworkPid , ClientId ) ,
{ stop , normal , State } ;
% % 发 送 指 令 信 息
handle_info ( { publish_command , ReceiverPid , Ref , CommandType , Msg } , State = #state { transport = Transport , socket = Sock , client_id = ClientId , packet_id = PacketId , inflight = Inflight , is_registered = true } ) - >
logger : warning ( " [sdlan_channel] client_id: ~p , will publish: ~p , message: ~p " , [ ClientId , CommandType , Msg ] ) ,
Transport : send ( Sock , < < PacketId : 32 , ? PACKET_COMMAND , CommandType , Msg / binary > > ) ,
{ noreply , State #state { packet_id = PacketId + 1 , inflight = maps : put ( PacketId , { ReceiverPid , Ref } , Inflight ) } } ;
% % 主 机 端 的 消 息 响 应
handle_info ( { tcp , _ Sock , < < PacketId : 32 , ? PACKET_COMMAND_ACK , Body / binary > > } , State = #state { client_id = ClientId , inflight = Inflight } ) when PacketId > 0 - >
CommandAck = #sdl_command_ack { } = sdlan_pb : decode_msg ( Body , sdl_command_ack ) ,
logger : debug ( " [sdlan_channel] client_id: ~p , get publish response message: ~p , packet_id: ~p " , [ ClientId , CommandAck , PacketId ] ) ,
case maps : take ( PacketId , Inflight ) of
error - >
logger : warning ( " [sdlan_channel] get unknown publish response message: ~p , packet_id: ~p " , [ CommandAck , PacketId ] ) ,
{ ok , State } ;
{ { ReceiverPid , Ref } , NInflight } - >
case is_pid ( ReceiverPid ) andalso is_process_alive ( ReceiverPid ) of
true - >
ReceiverPid ! { command_reply , Ref , CommandAck } ;
false - >
logger : warning ( " [sdlan_channel] get publish response message: ~p , packet_id: ~p , but receiver_pid is deaded " , [ CommandAck , PacketId ] )
end ,
{ noreply , State #state { inflight = NInflight } }
end ;
handle_info ( { tcp_error , Sock , Reason } , State = #state { socket = Sock , client_id = ClientId } ) - >
logger : notice ( " [sdlan_channel] client_id: ~p , tcp_error: ~p " , [ ClientId , Reason ] ) ,
{ stop , normal , State } ;
handle_info ( { tcp_closed , Sock } , State = #state { socket = Sock , client_id = ClientId } ) - >
logger : notice ( " [sdlan_channel] client_id: ~p , tcp_closed " , [ ClientId ] ) ,
{ stop , normal , State } ;
% % 关 闭 当 前 通 道
handle_info ( { stop , Reason } , State ) - >
{ stop , Reason , State } ;
handle_info ( Info , State ) - >
logger : warning ( " [sdlan_channel] get a unknown message: ~p , channel will closed " , [ Info ] ) ,
{ noreply , State } .
terminate ( Reason , #state { client_id = ClientId , network_pid = NetworkPid } ) - >
case ClientId / = undefined andalso is_pid ( NetworkPid ) of
true - >
{ ok , NetworkId } = sdlan_network : get_network_id ( NetworkPid ) ,
Result = sdlan_api : node_offline ( ClientId , NetworkId ) ,
logger : debug ( " [sdlan_channel] client_id: ~p , set none offline, result is: ~p " , [ ClientId , Result ] ) ;
false - >
ok
end ,
logger : warning ( " [sdlan_channel] client_id: ~p , stop with reason: ~p " , [ ClientId , Reason ] ) ,
ok .
code_change ( _ OldVsn , State , _ Extra ) - >
{ ok , State } .
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
% % helper methods
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
- spec register_nak_reply ( PacketId : : integer ( ) , ErrorCode : : integer ( ) , ErrorMsg : : binary ( ) ) - > binary ( ) .
register_nak_reply ( PacketId , ErrorCode , ErrorMsg ) when is_integer ( PacketId ) , is_integer ( ErrorCode ) , is_binary ( ErrorMsg ) - >
RegisterNakReply = sdlan_pb : encode_msg ( #sdl_register_super_nak {
error_code = ErrorCode ,
error_message = ErrorMsg
} ) ,
< < PacketId : 32 , ? PACKET_REGISTER_SUPER_NAK , RegisterNakReply / binary > > .
rsa_encode ( PlainText , RsaPubKey ) when is_binary ( PlainText ) - >
iolist_to_binary ( sdlan_cipher : rsa_encrypt ( PlainText , RsaPubKey ) ) .