Compare commits
52 Commits
main
...
202601_v2_
| Author | SHA1 | Date | |
|---|---|---|---|
| 16555f1520 | |||
| b3a51adc95 | |||
| 79eded8c96 | |||
| 6f2322174d | |||
| 83361ef1b2 | |||
| f6494df5d1 | |||
| 39a32ec73a | |||
| 08942fe73d | |||
| 86d60c9ff0 | |||
| 588b05b24b | |||
| 42dbb48e18 | |||
| 48721a0578 | |||
| 1f0cade371 | |||
| 37d46e5bbc | |||
| 11ac0a5b8c | |||
| 9403259513 | |||
| 160fb4d92b | |||
| 672da8d7b1 | |||
| a4efafd484 | |||
| d272258312 | |||
| 388096a29a | |||
| 8bdc5868c3 | |||
| 6cce8f7e5e | |||
| c9adbb5e16 | |||
| d64cb4235f | |||
| 75fa239ae9 | |||
| eb35558fe7 | |||
| eb11c05ba5 | |||
| a4f2ed8428 | |||
| f5a07e41f7 | |||
| 7cd645eb17 | |||
| abf6e11a34 | |||
| c1f196fa96 | |||
| ce69bfd730 | |||
| ab74389f07 | |||
| 771896045b | |||
| 715103bd1f | |||
| 92b3256646 | |||
| 97019a87de | |||
| 2394db892c | |||
| c1676115a6 | |||
| a17453022d | |||
| 775ec28485 | |||
| 609bcd6cd5 | |||
| 1ae54ab276 | |||
| e2fd4f18cb | |||
| a597ecbf2e | |||
| ef8ded9f35 | |||
| d7db914e88 | |||
| 8d94244689 | |||
| be855d6985 | |||
| c89091c205 |
@ -16,7 +16,6 @@
|
|||||||
-define(PACKET_EMPTY, 16#00).
|
-define(PACKET_EMPTY, 16#00).
|
||||||
-define(PACKET_REGISTER_SUPER, 16#01).
|
-define(PACKET_REGISTER_SUPER, 16#01).
|
||||||
-define(PACKET_REGISTER_SUPER_ACK, 16#02).
|
-define(PACKET_REGISTER_SUPER_ACK, 16#02).
|
||||||
-define(PACKET_REGISTER_SUPER_ACKNOWLEDGE, 16#03).
|
|
||||||
-define(PACKET_REGISTER_SUPER_NAK, 16#04).
|
-define(PACKET_REGISTER_SUPER_NAK, 16#04).
|
||||||
-define(PACKET_UNREGISTER, 16#05).
|
-define(PACKET_UNREGISTER, 16#05).
|
||||||
|
|
||||||
@ -24,42 +23,24 @@
|
|||||||
-define(PACKET_QUERY_INFO, 16#06).
|
-define(PACKET_QUERY_INFO, 16#06).
|
||||||
-define(PACKET_PEER_INFO, 16#07).
|
-define(PACKET_PEER_INFO, 16#07).
|
||||||
|
|
||||||
%% 心跳机制
|
|
||||||
-define(PACKET_PING, 16#08).
|
|
||||||
-define(PACKET_PONG, 16#09).
|
|
||||||
|
|
||||||
%% 推送的事件信息, 不需要返回值
|
%% 推送的事件信息, 不需要返回值
|
||||||
-define(PACKET_EVENT, 16#10).
|
-define(PACKET_EVENT, 16#10).
|
||||||
|
|
||||||
%% 定义事件信息
|
%% 定义事件信息
|
||||||
-define(PACKET_EVENT_KNOWN_IP, 16#01).
|
-define(PACKET_EVENT_DROP_MACS, 16#02).
|
||||||
-define(PACKET_EVENT_DROP_IP, 16#02).
|
|
||||||
-define(PACKET_EVENT_NAT_CHANGED, 16#03).
|
-define(PACKET_EVENT_NAT_CHANGED, 16#03).
|
||||||
-define(PACKET_EVENT_SEND_REGISTER, 16#04).
|
-define(PACKET_EVENT_SEND_REGISTER, 16#04).
|
||||||
|
-define(PACKET_EVENT_REFRESH_AUTH, 16#05).
|
||||||
|
|
||||||
%% 网络关闭
|
%% 网络关闭
|
||||||
-define(PACKET_EVENT_NETWORK_SHUTDOWN, 16#FF).
|
-define(PACKET_EVENT_NETWORK_SHUTDOWN, 16#FF).
|
||||||
|
|
||||||
%% 推送命令信息, 需要等待返回值
|
|
||||||
-define(PACKET_COMMAND, 16#11).
|
|
||||||
-define(PACKET_COMMAND_ACK, 16#12).
|
|
||||||
|
|
||||||
%% 网络发生变化
|
|
||||||
-define(PACKET_COMMAND_CHANGE_NETWORK, 16#01).
|
|
||||||
-define(PACKET_COMMAND_UPGRADE, 16#02).
|
|
||||||
|
|
||||||
%% 网络流量统计
|
|
||||||
-define(PACKET_FLOW_TRACER, 16#15).
|
|
||||||
|
|
||||||
-define(PACKET_REGISTER, 16#20).
|
-define(PACKET_REGISTER, 16#20).
|
||||||
-define(PACKET_REGISTER_ACK, 16#21).
|
-define(PACKET_REGISTER_ACK, 16#21).
|
||||||
|
|
||||||
%% stun相关的请求
|
%% stun相关的请求
|
||||||
|
|
||||||
%% 请求
|
%% 请求
|
||||||
-define(PACKET_STUN_REQUEST, 16#30).
|
-define(PACKET_STUN_REQUEST, 16#30).
|
||||||
%% 响应
|
|
||||||
-define(PACKET_STUN_REPLY, 16#31).
|
|
||||||
|
|
||||||
%% stun网络类型检测
|
%% stun网络类型检测
|
||||||
%% 请求
|
%% 请求
|
||||||
@ -77,6 +58,14 @@
|
|||||||
%% 数据转发
|
%% 数据转发
|
||||||
-define(PACKET_STUN_DATA, 16#FF).
|
-define(PACKET_STUN_DATA, 16#FF).
|
||||||
|
|
||||||
|
%% arp查询 request -> response
|
||||||
|
-define(PACKET_ARP_REQUEST, 16#a0).
|
||||||
|
-define(PACKET_ARP_RESPONSE, 16#a1).
|
||||||
|
|
||||||
|
%% 权限请求
|
||||||
|
-define(PACKET_POLICY_REQUEST, 16#b0).
|
||||||
|
-define(PACKET_POLICY_RESPONSE, 16#b1).
|
||||||
|
|
||||||
-record(id_generator, {
|
-record(id_generator, {
|
||||||
tab :: atom(),
|
tab :: atom(),
|
||||||
increment_id = 0 :: integer()
|
increment_id = 0 :: integer()
|
||||||
|
|||||||
@ -25,141 +25,131 @@
|
|||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_DEV_ADDR_PB_H').
|
|
||||||
-define('SDL_DEV_ADDR_PB_H', true).
|
|
||||||
-record(sdl_dev_addr,
|
|
||||||
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
|
||||||
mac = <<>> :: iodata() | undefined, % = 2, optional
|
|
||||||
net_addr = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
|
||||||
net_bit_len = 0 :: non_neg_integer() | undefined, % = 4, optional, 32 bits
|
|
||||||
network_domain = <<>> :: unicode:chardata() | undefined % = 5, optional
|
|
||||||
}).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifndef('SDL_EMPTY_PB_H').
|
-ifndef('SDL_EMPTY_PB_H').
|
||||||
-define('SDL_EMPTY_PB_H', true).
|
-define('SDL_EMPTY_PB_H', true).
|
||||||
-record(sdl_empty,
|
-record(sdl_empty,
|
||||||
{
|
{pkt_id = 0 :: non_neg_integer() | undefined % = 1, optional, 32 bits
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_REGISTER_SUPER_PB_H').
|
-ifndef('SDL_REGISTER_SUPER_PB_H').
|
||||||
-define('SDL_REGISTER_SUPER_PB_H', true).
|
-define('SDL_REGISTER_SUPER_PB_H', true).
|
||||||
-record(sdl_register_super,
|
-record(sdl_register_super,
|
||||||
{version = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
{pkt_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
installed_channel = <<>> :: unicode:chardata() | undefined, % = 2, optional
|
client_id = <<>> :: unicode:chardata() | undefined, % = 2, optional
|
||||||
client_id = <<>> :: unicode:chardata() | undefined, % = 3, optional
|
network_id = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
||||||
dev_addr = undefined :: sdlan_pb:sdl_dev_addr() | undefined, % = 4, optional
|
mac = <<>> :: iodata() | undefined, % = 4, optional
|
||||||
pub_key = <<>> :: unicode:chardata() | undefined, % = 5, optional
|
ip = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
||||||
token = <<>> :: unicode:chardata() | undefined, % = 6, optional
|
mask_len = 0 :: non_neg_integer() | undefined, % = 6, optional, 32 bits
|
||||||
network_code = <<>> :: unicode:chardata() | undefined, % = 7, optional
|
hostname = <<>> :: unicode:chardata() | undefined, % = 7, optional
|
||||||
hostname = <<>> :: unicode:chardata() | undefined % = 8, optional
|
pub_key = <<>> :: unicode:chardata() | undefined, % = 8, optional
|
||||||
|
access_token = <<>> :: unicode:chardata() | undefined % = 9, optional
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_REGISTER_SUPER_ACK_PB_H').
|
-ifndef('SDL_REGISTER_SUPER_ACK_PB_H').
|
||||||
-define('SDL_REGISTER_SUPER_ACK_PB_H', true).
|
-define('SDL_REGISTER_SUPER_ACK_PB_H', true).
|
||||||
-record(sdl_register_super_ack,
|
-record(sdl_register_super_ack,
|
||||||
{dev_addr = undefined :: sdlan_pb:sdl_dev_addr() | undefined, % = 1, optional
|
{pkt_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
aes_key = <<>> :: iodata() | undefined, % = 2, optional
|
aes_key = <<>> :: iodata() | undefined, % = 2, optional
|
||||||
upgrade_type = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
session_token = <<>> :: iodata() | undefined % = 3, optional
|
||||||
upgrade_prompt :: unicode:chardata() | undefined, % = 4, optional
|
|
||||||
upgrade_address :: unicode:chardata() | undefined % = 5, optional
|
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_REGISTER_SUPER_NAK_PB_H').
|
-ifndef('SDL_REGISTER_SUPER_NAK_PB_H').
|
||||||
-define('SDL_REGISTER_SUPER_NAK_PB_H', true).
|
-define('SDL_REGISTER_SUPER_NAK_PB_H', true).
|
||||||
-record(sdl_register_super_nak,
|
-record(sdl_register_super_nak,
|
||||||
{error_code = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
{pkt_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
error_message = <<>> :: unicode:chardata() | undefined % = 2, optional
|
error_code = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
error_message = <<>> :: unicode:chardata() | undefined % = 3, optional
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_QUERY_INFO_PB_H').
|
-ifndef('SDL_QUERY_INFO_PB_H').
|
||||||
-define('SDL_QUERY_INFO_PB_H', true).
|
-define('SDL_QUERY_INFO_PB_H', true).
|
||||||
-record(sdl_query_info,
|
-record(sdl_query_info,
|
||||||
{dst_mac = <<>> :: iodata() | undefined % = 1, optional
|
{pkt_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
|
network_id = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
src_mac = <<>> :: iodata() | undefined, % = 3, optional
|
||||||
|
dst_mac = <<>> :: iodata() | undefined, % = 4, optional
|
||||||
|
session_token = <<>> :: iodata() | undefined % = 5, optional
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_PEER_INFO_PB_H').
|
-ifndef('SDL_PEER_INFO_PB_H').
|
||||||
-define('SDL_PEER_INFO_PB_H', true).
|
-define('SDL_PEER_INFO_PB_H', true).
|
||||||
-record(sdl_peer_info,
|
-record(sdl_peer_info,
|
||||||
{dst_mac = <<>> :: iodata() | undefined, % = 1, optional
|
{pkt_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
v4_info = undefined :: sdlan_pb:sdl_v4_info() | undefined, % = 2, optional
|
network_id = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
v6_info :: sdlan_pb:sdl_v6_info() | undefined % = 3, optional
|
dst_mac = <<>> :: iodata() | undefined, % = 3, optional
|
||||||
|
v4_info = undefined :: sdlan_pb:sdl_v4_info() | undefined, % = 4, optional
|
||||||
|
v6_info :: sdlan_pb:sdl_v6_info() | undefined % = 5, optional
|
||||||
|
}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-ifndef('SDL_EMPTY_PEER_INFO_PB_H').
|
||||||
|
-define('SDL_EMPTY_PEER_INFO_PB_H', true).
|
||||||
|
-record(sdl_empty_peer_info,
|
||||||
|
{pkt_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
|
network_id = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
dst_mac = <<>> :: iodata() | undefined % = 3, optional
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_NAT_CHANGED_EVENT_PB_H').
|
-ifndef('SDL_NAT_CHANGED_EVENT_PB_H').
|
||||||
-define('SDL_NAT_CHANGED_EVENT_PB_H', true).
|
-define('SDL_NAT_CHANGED_EVENT_PB_H', true).
|
||||||
-record(sdl_nat_changed_event,
|
-record(sdl_nat_changed_event,
|
||||||
{mac = <<>> :: iodata() | undefined, % = 1, optional
|
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
ip = 0 :: non_neg_integer() | undefined % = 2, optional, 32 bits
|
mac = <<>> :: iodata() | undefined, % = 2, optional
|
||||||
|
ip = 0 :: non_neg_integer() | undefined % = 3, optional, 32 bits
|
||||||
|
}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-ifndef('SDL_DROP_MACS_EVENT_PB_H').
|
||||||
|
-define('SDL_DROP_MACS_EVENT_PB_H', true).
|
||||||
|
-record(sdl_drop_macs_event,
|
||||||
|
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
|
macs = [] :: [iodata()] | undefined % = 2, repeated
|
||||||
|
}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-ifndef('SDL_REFRESH_AUTH_EVENT_PB_H').
|
||||||
|
-define('SDL_REFRESH_AUTH_EVENT_PB_H', true).
|
||||||
|
-record(sdl_refresh_auth_event,
|
||||||
|
{network_id = 0 :: non_neg_integer() | undefined % = 1, optional, 32 bits
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_SEND_REGISTER_EVENT_PB_H').
|
-ifndef('SDL_SEND_REGISTER_EVENT_PB_H').
|
||||||
-define('SDL_SEND_REGISTER_EVENT_PB_H', true).
|
-define('SDL_SEND_REGISTER_EVENT_PB_H', true).
|
||||||
-record(sdl_send_register_event,
|
-record(sdl_send_register_event,
|
||||||
{dst_mac = <<>> :: iodata() | undefined, % = 1, optional
|
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
nat_ip = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
dst_mac = <<>> :: iodata() | undefined, % = 2, optional
|
||||||
nat_port = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
nat_ip = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
||||||
nat_type = 0 :: non_neg_integer() | undefined, % = 4, optional, 32 bits
|
nat_port = 0 :: non_neg_integer() | undefined, % = 4, optional, 32 bits
|
||||||
v6_info :: sdlan_pb:sdl_v6_info() | undefined % = 5, optional
|
nat_type = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
||||||
|
v6_info :: sdlan_pb:sdl_v6_info() | undefined % = 6, optional
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_NETWORK_SHUTDOWN_EVENT_PB_H').
|
-ifndef('SDL_NETWORK_SHUTDOWN_EVENT_PB_H').
|
||||||
-define('SDL_NETWORK_SHUTDOWN_EVENT_PB_H', true).
|
-define('SDL_NETWORK_SHUTDOWN_EVENT_PB_H', true).
|
||||||
-record(sdl_network_shutdown_event,
|
-record(sdl_network_shutdown_event,
|
||||||
{message = <<>> :: unicode:chardata() | undefined % = 1, optional
|
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
}).
|
message = <<>> :: unicode:chardata() | undefined % = 2, optional
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifndef('SDL_CHANGE_NETWORK_COMMAND_PB_H').
|
|
||||||
-define('SDL_CHANGE_NETWORK_COMMAND_PB_H', true).
|
|
||||||
-record(sdl_change_network_command,
|
|
||||||
{dev_addr = undefined :: sdlan_pb:sdl_dev_addr() | undefined, % = 1, optional
|
|
||||||
aes_key = <<>> :: iodata() | undefined % = 2, optional
|
|
||||||
}).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifndef('SDL_COMMAND_ACK_PB_H').
|
|
||||||
-define('SDL_COMMAND_ACK_PB_H', true).
|
|
||||||
-record(sdl_command_ack,
|
|
||||||
{status = false :: boolean() | 0 | 1 | undefined, % = 1, optional
|
|
||||||
message :: unicode:chardata() | undefined % = 2, optional
|
|
||||||
}).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifndef('SDL_FLOWS_PB_H').
|
|
||||||
-define('SDL_FLOWS_PB_H', true).
|
|
||||||
-record(sdl_flows,
|
|
||||||
{forward_num = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
|
||||||
p2p_num = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
|
||||||
inbound_num = 0 :: non_neg_integer() | undefined % = 3, optional, 32 bits
|
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-ifndef('SDL_STUN_REQUEST_PB_H').
|
-ifndef('SDL_STUN_REQUEST_PB_H').
|
||||||
-define('SDL_STUN_REQUEST_PB_H', true).
|
-define('SDL_STUN_REQUEST_PB_H', true).
|
||||||
-record(sdl_stun_request,
|
-record(sdl_stun_request,
|
||||||
{cookie = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
{client_id = <<>> :: unicode:chardata() | undefined, % = 1, optional
|
||||||
client_id = <<>> :: unicode:chardata() | undefined, % = 2, optional
|
network_id = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
network_id = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
mac = <<>> :: iodata() | undefined, % = 3, optional
|
||||||
mac = <<>> :: iodata() | undefined, % = 4, optional
|
ip = 0 :: non_neg_integer() | undefined, % = 4, optional, 32 bits
|
||||||
ip = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
nat_type = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
||||||
nat_type = 0 :: non_neg_integer() | undefined, % = 6, optional, 32 bits
|
v6_info :: sdlan_pb:sdl_v6_info() | undefined, % = 6, optional
|
||||||
v6_info :: sdlan_pb:sdl_v6_info() | undefined % = 7, optional
|
session_token = <<>> :: iodata() | undefined % = 7, optional
|
||||||
}).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifndef('SDL_STUN_REPLY_PB_H').
|
|
||||||
-define('SDL_STUN_REPLY_PB_H', true).
|
|
||||||
-record(sdl_stun_reply,
|
|
||||||
{cookie = 0 :: non_neg_integer() | undefined % = 1, optional, 32 bits
|
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
@ -171,7 +161,9 @@
|
|||||||
dst_mac = <<>> :: iodata() | undefined, % = 3, optional
|
dst_mac = <<>> :: iodata() | undefined, % = 3, optional
|
||||||
is_p2p = false :: boolean() | 0 | 1 | undefined, % = 4, optional
|
is_p2p = false :: boolean() | 0 | 1 | undefined, % = 4, optional
|
||||||
ttl = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
ttl = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
||||||
data = <<>> :: iodata() | undefined % = 6, optional
|
data = <<>> :: iodata() | undefined, % = 6, optional
|
||||||
|
session_token = <<>> :: iodata() | undefined, % = 7, optional
|
||||||
|
identity_id = 0 :: non_neg_integer() | undefined % = 8, optional, 32 bits
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
@ -197,7 +189,8 @@
|
|||||||
-define('SDL_STUN_PROBE_PB_H', true).
|
-define('SDL_STUN_PROBE_PB_H', true).
|
||||||
-record(sdl_stun_probe,
|
-record(sdl_stun_probe,
|
||||||
{cookie = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
{cookie = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
attr = 0 :: non_neg_integer() | undefined % = 2, optional, 32 bits
|
attr = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
step = 0 :: non_neg_integer() | undefined % = 3, optional, 32 bits
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
@ -205,8 +198,53 @@
|
|||||||
-define('SDL_STUN_PROBE_REPLY_PB_H', true).
|
-define('SDL_STUN_PROBE_REPLY_PB_H', true).
|
||||||
-record(sdl_stun_probe_reply,
|
-record(sdl_stun_probe_reply,
|
||||||
{cookie = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
{cookie = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
port = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
step = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
ip = 0 :: non_neg_integer() | undefined % = 3, optional, 32 bits
|
port = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
||||||
|
ip = 0 :: non_neg_integer() | undefined % = 4, optional, 32 bits
|
||||||
|
}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-ifndef('SDL_ARP_REQUEST_PB_H').
|
||||||
|
-define('SDL_ARP_REQUEST_PB_H', true).
|
||||||
|
-record(sdl_arp_request,
|
||||||
|
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
|
target_ip = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
session_token = <<>> :: iodata() | undefined % = 3, optional
|
||||||
|
}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-ifndef('SDL_ARP_RESPONSE_PB_H').
|
||||||
|
-define('SDL_ARP_RESPONSE_PB_H', true).
|
||||||
|
-record(sdl_arp_response,
|
||||||
|
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
|
target_ip = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
target_mac = <<>> :: iodata() | undefined % = 3, optional
|
||||||
|
}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-ifndef('SDL_POLICY_REQUEST_PB_H').
|
||||||
|
-define('SDL_POLICY_REQUEST_PB_H', true).
|
||||||
|
-record(sdl_policy_request,
|
||||||
|
{client_id = <<>> :: unicode:chardata() | undefined, % = 1, optional
|
||||||
|
network_id = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
mac = <<>> :: iodata() | undefined, % = 3, optional
|
||||||
|
src_identity_id = 0 :: non_neg_integer() | undefined, % = 4, optional, 32 bits
|
||||||
|
dst_identity_id = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
||||||
|
version = 0 :: non_neg_integer() | undefined, % = 6, optional, 32 bits
|
||||||
|
session_token = <<>> :: iodata() | undefined % = 7, optional
|
||||||
|
}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-ifndef('SDL_POLICY_RESPONSE_PB_H').
|
||||||
|
-define('SDL_POLICY_RESPONSE_PB_H', true).
|
||||||
|
-record(sdl_policy_response,
|
||||||
|
{network_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits
|
||||||
|
src_identity_id = 0 :: non_neg_integer() | undefined, % = 2, optional, 32 bits
|
||||||
|
dst_identity_id = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits
|
||||||
|
version = 0 :: non_neg_integer() | undefined, % = 4, optional, 32 bits
|
||||||
|
total_num = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits
|
||||||
|
index = 0 :: non_neg_integer() | undefined, % = 6, optional, 32 bits
|
||||||
|
rules = <<>> :: iodata() | undefined % = 7, optional
|
||||||
}).
|
}).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author licheng5
|
|
||||||
%%% @copyright (C) 2020, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 26. 4月 2020 3:36 下午
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(api_handler).
|
|
||||||
-author("licheng5").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([handle_request/4]).
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%% helper methods
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
|
|
||||||
%% 重新加载对应的主机信息
|
|
||||||
handle_request("POST", "/test/auth_token", _, PostParams) ->
|
|
||||||
logger:debug("[test_handler] get post params: ~p", [PostParams]),
|
|
||||||
[Id | _] = network_bo:get_all_networks(),
|
|
||||||
Data = #{
|
|
||||||
<<"network_id">> => Id
|
|
||||||
},
|
|
||||||
{ok, 200, sdlan_util:json_data(Data)};
|
|
||||||
|
|
||||||
handle_request(_, Path, _, _) ->
|
|
||||||
Path1 = list_to_binary(Path),
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"url: ", Path1/binary, " not found">>)}.
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%% helper methods
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@ -12,7 +12,7 @@
|
|||||||
%% API
|
%% API
|
||||||
-export([handle_request/4]).
|
-export([handle_request/4]).
|
||||||
|
|
||||||
handle_request("POST", "/network/create", _, #{<<"id">> := NetworkId}) when NetworkId > 0 ->
|
handle_request("POST", "/network/start", _, #{<<"id">> := NetworkId}) when NetworkId > 0 ->
|
||||||
case sdlan_network_sup:ensured_network_started(NetworkId) of
|
case sdlan_network_sup:ensured_network_started(NetworkId) of
|
||||||
{ok, Pid} when is_pid(Pid) ->
|
{ok, Pid} when is_pid(Pid) ->
|
||||||
{ok, 200, sdlan_util:json_data(<<"success">>)};
|
{ok, 200, sdlan_util:json_data(<<"success">>)};
|
||||||
@ -21,28 +21,7 @@ handle_request("POST", "/network/create", _, #{<<"id">> := NetworkId}) when Netw
|
|||||||
{ok, 200, sdlan_util:json_error(-1, <<"error">>)}
|
{ok, 200, sdlan_util:json_error(-1, <<"error">>)}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_request("POST", "/network/reload", _, #{<<"id">> := NetworkId}) when NetworkId > 0 ->
|
handle_request("POST", "/network/stop", _, #{<<"id">> := NetworkId}) when NetworkId > 0 ->
|
||||||
case sdlan_network:get_pid(NetworkId) of
|
|
||||||
undefined ->
|
|
||||||
case sdlan_network_sup:start_network(NetworkId) of
|
|
||||||
{ok, Pid} when is_pid(Pid) ->
|
|
||||||
sdlan_network_sup:reallocate_bind_width(),
|
|
||||||
{ok, 200, sdlan_util:json_data(<<"success">>)};
|
|
||||||
{error, Reason} ->
|
|
||||||
logger:debug("[network_handler] start network: ~p, get error: ~p", [NetworkId, Reason]),
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"error">>)}
|
|
||||||
end;
|
|
||||||
NetworkPid when is_pid(NetworkPid) ->
|
|
||||||
case sdlan_network:reload(NetworkPid) of
|
|
||||||
ok ->
|
|
||||||
{ok, 200, sdlan_util:json_data(<<"success">>)};
|
|
||||||
{error, Reason} ->
|
|
||||||
logger:debug("[network_handler] reload network: ~p, get error: ~p", [NetworkId, Reason]),
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"error">>)}
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_request("POST", "/network/delete", _, #{<<"id">> := NetworkId}) when NetworkId > 0 ->
|
|
||||||
case sdlan_network:get_pid(NetworkId) of
|
case sdlan_network:get_pid(NetworkId) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{ok, 200, sdlan_util:json_data(<<"success">>)};
|
{ok, 200, sdlan_util:json_data(<<"success">>)};
|
||||||
|
|||||||
@ -15,23 +15,6 @@
|
|||||||
%% API
|
%% API
|
||||||
-export([handle_request/4]).
|
-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 ->
|
handle_request("POST", "/node/disable", _, #{<<"network_id">> := NetworkId, <<"client_id">> := ClientId}) when NetworkId > 0 ->
|
||||||
case sdlan_network:get_pid(NetworkId) of
|
case sdlan_network:get_pid(NetworkId) of
|
||||||
undefined ->
|
undefined ->
|
||||||
@ -41,32 +24,6 @@ handle_request("POST", "/node/disable", _, #{<<"network_id">> := NetworkId, <<"c
|
|||||||
{ok, 200, sdlan_util:json_data(<<"success">>)}
|
{ok, 200, sdlan_util:json_data(<<"success">>)}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_request("POST", "/node/move", _, #{<<"client_id">> := ClientId, <<"from_network_id">> := FromNetworkId, <<"to_network_id">> := ToNetworkId, <<"timeout">> := Timeout}) ->
|
|
||||||
case {sdlan_network:get_pid(FromNetworkId), sdlan_network:get_pid(ToNetworkId)} of
|
|
||||||
{FromPid, ToPid} when is_pid(FromPid), is_pid(ToPid) ->
|
|
||||||
case sdlan_network:dropout_client(FromPid, ClientId) of
|
|
||||||
{ok, ChannelPid, HostName} ->
|
|
||||||
Ref = sdlan_channel:move_network(ChannelPid, self(), ToPid, HostName),
|
|
||||||
receive
|
|
||||||
{command_reply, Ref, {error, Reason}} ->
|
|
||||||
logger:warning("[node_handler] client_id: ~p, move network from: ~p, to: ~p, get error: ~p", [ClientId, FromPid, ToPid, Reason]),
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"move failed">>)};
|
|
||||||
{command_reply, Ref, #sdl_command_ack{status = true}} ->
|
|
||||||
{ok, 200, sdlan_util:json_data(<<"success">>)};
|
|
||||||
{command_reply, Ref, #sdl_command_ack{status = false, message = ErrorMsg}} when is_binary(ErrorMsg) ->
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"move failed: ", ErrorMsg/binary>>)}
|
|
||||||
after Timeout * 1000 ->
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"move timeout">>)}
|
|
||||||
end;
|
|
||||||
error ->
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"dropout from from_network error">>)}
|
|
||||||
end;
|
|
||||||
{FromPid, undefined} when is_pid(FromPid) ->
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"to_network not found">>)};
|
|
||||||
{undefined, ToPid} when is_pid(ToPid) ->
|
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"from_network not found">>)}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_request(_, Path, _, _) ->
|
handle_request(_, Path, _, _) ->
|
||||||
Path1 = list_to_binary(Path),
|
Path1 = list_to_binary(Path),
|
||||||
{ok, 200, sdlan_util:json_error(-1, <<"url: ", Path1/binary, " not found">>)}.
|
{ok, 200, sdlan_util:json_error(-1, <<"url: ", Path1/binary, " not found">>)}.
|
||||||
@ -19,13 +19,7 @@
|
|||||||
%% 重新加载对应的主机信息
|
%% 重新加载对应的主机信息
|
||||||
handle_request("POST", "/test/auth_token", _, PostParams) ->
|
handle_request("POST", "/test/auth_token", _, PostParams) ->
|
||||||
logger:debug("[test_handler] get post params: ~p", [PostParams]),
|
logger:debug("[test_handler] get post params: ~p", [PostParams]),
|
||||||
Data = #{
|
{ok, 200, sdlan_util:json_data(<<"ok">>)};
|
||||||
<<"network_id">> => 8,
|
|
||||||
<<"upgrade_type">> => 0,
|
|
||||||
<<"upgrade_prompt">> => <<"simple upgrade">>,
|
|
||||||
<<"upgrade_address">> => <<"upgrade_address">>
|
|
||||||
},
|
|
||||||
{ok, 200, sdlan_util:json_data(Data)};
|
|
||||||
|
|
||||||
handle_request("POST", "/test/upgrade", _, PostParams) ->
|
handle_request("POST", "/test/upgrade", _, PostParams) ->
|
||||||
logger:debug("[test_handler] get post params: ~p", [PostParams]),
|
logger:debug("[test_handler] get post params: ~p", [PostParams]),
|
||||||
@ -36,6 +30,14 @@ handle_request("POST", "/test/upgrade", _, PostParams) ->
|
|||||||
},
|
},
|
||||||
{ok, 200, sdlan_util:json_data(Data)};
|
{ok, 200, sdlan_util:json_data(Data)};
|
||||||
|
|
||||||
|
handle_request("POST", "/test/login_with_account", _, PostParams) ->
|
||||||
|
logger:debug("[test_handler] get post params: ~p", [PostParams]),
|
||||||
|
Data = #{
|
||||||
|
<<"access_token">> => <<"cookie:1234">>,
|
||||||
|
<<"network_id">> => 1234
|
||||||
|
},
|
||||||
|
{ok, 200, sdlan_util:json_data(Data)};
|
||||||
|
|
||||||
handle_request("GET", "/test/get_all_networks", _, _) ->
|
handle_request("GET", "/test/get_all_networks", _, _) ->
|
||||||
{ok, 200, sdlan_util:json_data([8, 9, 10])};
|
{ok, 200, sdlan_util:json_data([8, 9, 10])};
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ handle_request("GET", "/test/get_network", #{<<"id">> := Id0}, _) ->
|
|||||||
<<"name">> => <<"test1">>,
|
<<"name">> => <<"test1">>,
|
||||||
<<"ipaddr">> => <<"10.211.179.0/24">>,
|
<<"ipaddr">> => <<"10.211.179.0/24">>,
|
||||||
<<"owner_id">> => 1234,
|
<<"owner_id">> => 1234,
|
||||||
|
<<"domain">> => <<"punchnet8.net">>,
|
||||||
<<"disabled_clients">> => []
|
<<"disabled_clients">> => []
|
||||||
},
|
},
|
||||||
9 => #{
|
9 => #{
|
||||||
@ -54,6 +57,7 @@ handle_request("GET", "/test/get_network", #{<<"id">> := Id0}, _) ->
|
|||||||
<<"name">> => <<"test2">>,
|
<<"name">> => <<"test2">>,
|
||||||
<<"ipaddr">> => <<"10.211.180.0/24">>,
|
<<"ipaddr">> => <<"10.211.180.0/24">>,
|
||||||
<<"owner_id">> => 1234,
|
<<"owner_id">> => 1234,
|
||||||
|
<<"domain">> => <<"punchnet9.net">>,
|
||||||
<<"disabled_clients">> => []
|
<<"disabled_clients">> => []
|
||||||
},
|
},
|
||||||
10 => #{
|
10 => #{
|
||||||
@ -61,6 +65,7 @@ handle_request("GET", "/test/get_network", #{<<"id">> := Id0}, _) ->
|
|||||||
<<"name">> => <<"test3">>,
|
<<"name">> => <<"test3">>,
|
||||||
<<"ipaddr">> => <<"10.211.181.0/24">>,
|
<<"ipaddr">> => <<"10.211.181.0/24">>,
|
||||||
<<"owner_id">> => 1234,
|
<<"owner_id">> => 1234,
|
||||||
|
<<"domain">> => <<"punchnet10.net">>,
|
||||||
<<"disabled_clients">> => []
|
<<"disabled_clients">> => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,162 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author aresei
|
|
||||||
%%% @copyright (C) 2023, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 04. 7月 2023 12:31
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(client_model).
|
|
||||||
-author("aresei").
|
|
||||||
-include("sdlan_tables.hrl").
|
|
||||||
-include_lib("stdlib/include/qlc.hrl").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([create_table/1, get_table_name/1]).
|
|
||||||
-export([get_clients/1, get_client/2, delete_clients/1, delete_client/2, disable_client/2, alloc_ip/6]).
|
|
||||||
-export([debug/1]).
|
|
||||||
|
|
||||||
create_table(Tab) when is_atom(Tab) ->
|
|
||||||
mnesia:create_table(Tab, [
|
|
||||||
{attributes, record_info(fields, client)},
|
|
||||||
{record_name, client},
|
|
||||||
{disc_copies, [node()]},
|
|
||||||
{type, set}
|
|
||||||
]).
|
|
||||||
|
|
||||||
-spec get_table_name(NetworkId :: integer()) -> TableName :: atom().
|
|
||||||
get_table_name(NetworkId) when is_integer(NetworkId) ->
|
|
||||||
list_to_atom("client_" ++ integer_to_list(NetworkId)).
|
|
||||||
|
|
||||||
-spec get_client(NetworkId :: integer(), ClientId :: binary()) -> error | {ok, Client :: #client{}} .
|
|
||||||
get_client(NetworkId, ClientId) when is_integer(NetworkId), is_binary(ClientId) ->
|
|
||||||
Tab = get_table_name(NetworkId),
|
|
||||||
case mnesia:dirty_read(Tab, ClientId) of
|
|
||||||
[] ->
|
|
||||||
error;
|
|
||||||
[Client|_] ->
|
|
||||||
{ok, Client}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_clients(NetworkId :: integer()) -> [Client :: #client{}].
|
|
||||||
get_clients(NetworkId) when is_integer(NetworkId) ->
|
|
||||||
Tab = get_table_name(NetworkId),
|
|
||||||
case mnesia:transaction(fun() -> mnesia:foldl(fun(R, Acc0) -> [R|Acc0] end, [], Tab) end) of
|
|
||||||
{'atomic', Items} ->
|
|
||||||
lists:reverse(Items);
|
|
||||||
{'aborted', _} ->
|
|
||||||
[]
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec delete_clients(NetworkId :: integer()) -> ok | {error, Reason :: any()}.
|
|
||||||
delete_clients(NetworkId) when is_integer(NetworkId) ->
|
|
||||||
Tab = get_table_name(NetworkId),
|
|
||||||
case mnesia:transaction(fun() -> mnesia:clear_table(Tab) end) of
|
|
||||||
{'atomic', ok} ->
|
|
||||||
ok;
|
|
||||||
{'aborted', Reason} ->
|
|
||||||
{error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec delete_client(NetworkId :: integer(), ClientId :: binary()) -> {ok, Client :: #client{}} | {error, Reason :: any()}.
|
|
||||||
delete_client(NetworkId, ClientId) when is_integer(NetworkId), is_binary(ClientId) ->
|
|
||||||
Tab = get_table_name(NetworkId),
|
|
||||||
|
|
||||||
Fun = fun() ->
|
|
||||||
case mnesia:read(Tab, ClientId, write) of
|
|
||||||
[] ->
|
|
||||||
mnesia:abort(not_found);
|
|
||||||
[Record] ->
|
|
||||||
mnesia:delete(Tab, ClientId, write),
|
|
||||||
{ok, Record}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
case mnesia:transaction(Fun) of
|
|
||||||
{'atomic', {ok, Client}} ->
|
|
||||||
{ok, Client};
|
|
||||||
{'aborted', Reason} ->
|
|
||||||
{error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec disable_client(NetworkId :: integer(), ClientId :: binary()) -> ok | {error, Reason :: any()}.
|
|
||||||
disable_client(NetworkId, ClientId) when is_integer(NetworkId), is_binary(ClientId) ->
|
|
||||||
Tab = get_table_name(NetworkId),
|
|
||||||
Fun = fun() ->
|
|
||||||
case mnesia:read(Tab, ClientId, read) of
|
|
||||||
[] ->
|
|
||||||
ok;
|
|
||||||
[Client] ->
|
|
||||||
mnesia:write(Tab, Client#client{status = disabled}, write)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
case mnesia:transaction(Fun) of
|
|
||||||
{'atomic', ok} ->
|
|
||||||
ok;
|
|
||||||
{'aborted', Reason} ->
|
|
||||||
{error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% 分配ip地址的时候,以mac地址为唯一基准
|
|
||||||
-spec alloc_ip(NetworkId :: integer(), Ips :: list(), ClientId :: binary(), Mac :: binary(), NetAddr0 :: integer(), HostName :: binary()) ->
|
|
||||||
{ok, Ip :: integer()} | {error, Reason :: any()}.
|
|
||||||
alloc_ip(NetworkId, Ips, ClientId, Mac, NetAddr0, HostName) when is_binary(ClientId), is_integer(NetAddr0), is_binary(Mac), is_binary(HostName) ->
|
|
||||||
case mnesia:transaction(fun() -> alloc_ip0(NetworkId, Ips, ClientId, Mac, NetAddr0, HostName) end) of
|
|
||||||
{'atomic', Res} ->
|
|
||||||
{ok, Res};
|
|
||||||
{'aborted', Reason} ->
|
|
||||||
{error, Reason}
|
|
||||||
end.
|
|
||||||
alloc_ip0(NetworkId, Ips, ClientId, Mac, NetAddr0, HostName) ->
|
|
||||||
Tab = get_table_name(NetworkId),
|
|
||||||
|
|
||||||
case mnesia:read(Tab, ClientId) of
|
|
||||||
[Client=#client{ip = Ip, status = normal}] ->
|
|
||||||
ok = mnesia:write(Tab, Client#client{mac = Mac}, write),
|
|
||||||
Ip;
|
|
||||||
[#client{status = disabled}] ->
|
|
||||||
mnesia:abort(client_disabled);
|
|
||||||
[] ->
|
|
||||||
{UsedIps, UsedHostNames} = mnesia:foldl(fun(#client{ip = Ip0, host_name = HostName0}, {IpAcc, HostNameAcc}) ->
|
|
||||||
{[Ip0|IpAcc], [HostName0|HostNameAcc]}
|
|
||||||
end, {[], []}, Tab),
|
|
||||||
case HostName =/= <<>> andalso lists:member(HostName, UsedHostNames) of
|
|
||||||
true ->
|
|
||||||
mnesia:abort(host_name_used);
|
|
||||||
false ->
|
|
||||||
case lists:member(NetAddr0, Ips) andalso not lists:member(NetAddr0, UsedIps) of
|
|
||||||
true ->
|
|
||||||
%% 如果ip没有被占用,则分配給当前请求
|
|
||||||
Client = #client{client_id = ClientId, mac = Mac, ip = NetAddr0, host_name = HostName, status = normal},
|
|
||||||
ok = mnesia:write(Tab, Client, write),
|
|
||||||
NetAddr0;
|
|
||||||
false ->
|
|
||||||
case Ips -- UsedIps of
|
|
||||||
[] ->
|
|
||||||
mnesia:abort(no_ip);
|
|
||||||
[Ip|_] ->
|
|
||||||
Client = #client{client_id = ClientId, mac = Mac, ip = Ip, host_name = HostName, status = normal},
|
|
||||||
ok = mnesia:write(Tab, Client, write),
|
|
||||||
Ip
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% helper functions
|
|
||||||
%%%===================================================================
|
|
||||||
|
|
||||||
debug(NetworkId) when is_integer(NetworkId) ->
|
|
||||||
Tab = get_table_name(NetworkId),
|
|
||||||
F = fun() ->
|
|
||||||
Q = qlc:q([E || E <- mnesia:table(Tab)]),
|
|
||||||
qlc:e(Q)
|
|
||||||
end,
|
|
||||||
case mnesia:transaction(F) of
|
|
||||||
{'atomic', Records} ->
|
|
||||||
lists:foreach(fun(C) -> logger:debug("client: ~p", [C]) end, Records);
|
|
||||||
{'aborted', Reason} ->
|
|
||||||
logger:warning("read clients get error: ~p", [Reason])
|
|
||||||
end.
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author aresei
|
|
||||||
%%% @copyright (C) 2023, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 04. 7月 2023 12:31
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(mnesia_id_generator).
|
|
||||||
-author("aresei").
|
|
||||||
-include("sdlan.hrl").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([next_id/1, create_table/0]).
|
|
||||||
|
|
||||||
create_table() ->
|
|
||||||
%% id生成器
|
|
||||||
mnesia:create_table(id_generator, [
|
|
||||||
{attributes, record_info(fields, id_generator)},
|
|
||||||
{record_name, id_generator},
|
|
||||||
{disc_copies, [node()]},
|
|
||||||
{type, ordered_set}
|
|
||||||
]).
|
|
||||||
|
|
||||||
next_id(Tab) when is_atom(Tab) ->
|
|
||||||
mnesia:dirty_update_counter(id_generator, Tab, 1).
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author anlicheng
|
|
||||||
%%% @copyright (C) 2024, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%% TODO 数据库暂时不启用
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 28. 3月 2024 11:01
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(mnesia_manager).
|
|
||||||
-author("anlicheng").
|
|
||||||
-include("sdlan.hrl").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([init_database/0, join/1, copy_database/1]).
|
|
||||||
|
|
||||||
init_database() ->
|
|
||||||
%% 清理掉以前的schema
|
|
||||||
mnesia:stop(),
|
|
||||||
mnesia:delete_schema([node()]),
|
|
||||||
|
|
||||||
%% 创建schema
|
|
||||||
ok = mnesia:create_schema([node()]),
|
|
||||||
ok = mnesia:start(),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
%% 加入集群
|
|
||||||
join(MasterNode) when is_atom(MasterNode) ->
|
|
||||||
net_kernel:connect_node(MasterNode).
|
|
||||||
|
|
||||||
%% 初始化slave数据库
|
|
||||||
copy_database(MasterNode) when is_atom(MasterNode) ->
|
|
||||||
%% 清理旧的schema
|
|
||||||
mnesia:stop(),
|
|
||||||
mnesia:delete_schema([node()]),
|
|
||||||
%% 重新启动数据库
|
|
||||||
mnesia:start(),
|
|
||||||
|
|
||||||
rpc:call(MasterNode, mnesia, change_config, [extra_db_nodes, [node()]]),
|
|
||||||
mnesia:change_table_copy_type(schema, node(), disc_copies),
|
|
||||||
|
|
||||||
%% 增加表的分区复制
|
|
||||||
% mnesia:add_table_copy(client, node(), ram_copies),
|
|
||||||
ok.
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author aresei
|
|
||||||
%%% @copyright (C) 2023, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 16. 5月 2023 12:48
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(network_bo).
|
|
||||||
-author("aresei").
|
|
||||||
-include("sdlan.hrl").
|
|
||||||
|
|
||||||
-define(POOL_NAME, mysql_sdlan).
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([get_all_networks/0, get_network_by_id/1]).
|
|
||||||
|
|
||||||
-spec get_all_networks() -> Networks :: [integer()].
|
|
||||||
get_all_networks() ->
|
|
||||||
case mysql_pool:get_all(?POOL_NAME, <<"SELECT id FROM network">>) of
|
|
||||||
{ok, Networks} ->
|
|
||||||
lists:map(fun(#{<<"id">> := Id}) -> Id end, Networks);
|
|
||||||
{error, _} ->
|
|
||||||
[]
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_network_by_id(Id :: integer()) -> undefined | {ok, NetworkInfo :: map()}.
|
|
||||||
get_network_by_id(Id) when is_integer(Id) ->
|
|
||||||
mysql_pool:get_row(?POOL_NAME, <<"SELECT * FROM network WHERE id = ? LIMIT 1">>, [Id]).
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author aresei
|
|
||||||
%%% @copyright (C) 2018, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 29. 九月 2018 17:01
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(mysql_pool).
|
|
||||||
-author("aresei").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([get_row/2, get_row/3, get_all/2, get_all/3]).
|
|
||||||
-export([update/4, update_by/2, update_by/3, insert/4]).
|
|
||||||
|
|
||||||
%% 从数据库中查找一行记录
|
|
||||||
-spec get_row(Pool :: atom(), Sql::binary()) -> {ok, Record::map()} | undefined.
|
|
||||||
get_row(Pool, Sql) when is_atom(Pool), is_binary(Sql) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:get_row(ConnPid, Sql) end).
|
|
||||||
|
|
||||||
-spec get_row(Pool :: atom(), Sql::binary(), Params::list()) -> {ok, Record::map()} | undefined.
|
|
||||||
get_row(Pool, Sql, Params) when is_atom(Pool), is_binary(Sql), is_list(Params) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:get_row(ConnPid, Sql, Params) end).
|
|
||||||
|
|
||||||
-spec get_all(Pool :: atom(), Sql::binary()) -> {ok, Rows::list()} | {error, Reason :: any()}.
|
|
||||||
get_all(Pool, Sql) when is_atom(Pool), is_binary(Sql) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:get_all(ConnPid, Sql) end).
|
|
||||||
|
|
||||||
-spec get_all(Pool :: atom(), Sql::binary(), Params::list()) -> {ok, Rows::list()} | {error, Reason::any()}.
|
|
||||||
get_all(Pool, Sql, Params) when is_atom(Pool), is_binary(Sql), is_list(Params) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:get_all(ConnPid, Sql, Params) end).
|
|
||||||
|
|
||||||
-spec insert(Pool :: atom(), Table :: binary(), Fields :: map() | list(), boolean()) ->
|
|
||||||
ok | {ok, InsertId :: integer()} | {error, Reason :: any()}.
|
|
||||||
insert(Pool, Table, Fields, FetchInsertId) when is_atom(Pool), is_binary(Table), is_list(Fields); is_map(Fields), is_boolean(FetchInsertId) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:insert(ConnPid, Table, Fields, FetchInsertId) end).
|
|
||||||
|
|
||||||
-spec update_by(Pool :: atom(), UpdateSql :: binary()) -> {ok, AffectedRows :: integer()} | {error, Reason :: any()}.
|
|
||||||
update_by(Pool, UpdateSql) when is_atom(Pool), is_binary(UpdateSql) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:update_by(ConnPid, UpdateSql) end).
|
|
||||||
|
|
||||||
-spec update_by(Pool :: atom(), UpdateSql :: binary(), Params :: list()) -> {ok, AffectedRows :: integer()} | {error, Reason :: any()}.
|
|
||||||
update_by(Pool, UpdateSql, Params) when is_atom(Pool), is_binary(UpdateSql) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:update_by(ConnPid, UpdateSql, Params) end).
|
|
||||||
|
|
||||||
-spec update(Pool :: atom(), Table :: binary(), Fields :: map(), WhereFields :: map()) -> {ok, AffectedRows::integer()} | {error, Reason::any()}.
|
|
||||||
update(Pool, Table, Fields, WhereFields) when is_atom(Pool), is_binary(Table), is_map(Fields), is_map(WhereFields) ->
|
|
||||||
poolboy:transaction(Pool, fun(ConnPid) -> mysql_provider:update(ConnPid, Table, Fields, WhereFields) end).
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author aresei
|
|
||||||
%%% @copyright (C) 2018, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 29. 九月 2018 17:01
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(mysql_provider).
|
|
||||||
-author("aresei").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([get_row/2, get_row/3, get_all/2, get_all/3]).
|
|
||||||
-export([update/4, update_by/2, update_by/3, insert/4]).
|
|
||||||
|
|
||||||
%% 从数据库中查找一行记录
|
|
||||||
-spec get_row(ConnPid :: pid(), Sql::binary()) -> {ok, Record::map()} | undefined.
|
|
||||||
get_row(ConnPid, Sql) when is_pid(ConnPid), is_binary(Sql) ->
|
|
||||||
logger:debug("[mysql_client] get_row sql is: ~p", [Sql]),
|
|
||||||
case mysql:query(ConnPid, Sql) of
|
|
||||||
{ok, Names, [Row | _]} ->
|
|
||||||
{ok, maps:from_list(lists:zip(Names, Row))};
|
|
||||||
{ok, _, []} ->
|
|
||||||
undefined;
|
|
||||||
Error ->
|
|
||||||
logger:warning("[mysql_client] get error: ~p", [Error]),
|
|
||||||
undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_row(ConnPid :: pid(), Sql::binary(), Params::list()) -> {ok, Record::map()} | undefined.
|
|
||||||
get_row(ConnPid, Sql, Params) when is_pid(ConnPid), is_binary(Sql), is_list(Params) ->
|
|
||||||
logger:debug("[mysql_client] get_row sql is: ~p, params: ~p", [Sql, Params]),
|
|
||||||
case mysql:query(ConnPid, Sql, Params) of
|
|
||||||
{ok, Names, [Row | _]} ->
|
|
||||||
{ok, maps:from_list(lists:zip(Names, Row))};
|
|
||||||
{ok, _, []} ->
|
|
||||||
undefined;
|
|
||||||
Error ->
|
|
||||||
logger:warning("[mysql_client] get error: ~p", [Error]),
|
|
||||||
undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_all(ConnPid :: pid(), Sql::binary()) -> {ok, Rows::list()} | {error, Reason :: any()}.
|
|
||||||
get_all(ConnPid, Sql) when is_pid(ConnPid), is_binary(Sql) ->
|
|
||||||
logger:debug("[mysql_client] get_all sql is: ~p", [Sql]),
|
|
||||||
case mysql:query(ConnPid, Sql) of
|
|
||||||
{ok, Names, Rows} ->
|
|
||||||
{ok, lists:map(fun(Row) -> maps:from_list(lists:zip(Names, Row)) end, Rows)};
|
|
||||||
{error, Reason} ->
|
|
||||||
logger:warning("[mysql_client] get error: ~p", [Reason]),
|
|
||||||
{error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_all(ConnPid :: pid(), Sql::binary(), Params::list()) -> {ok, Rows::list()} | {error, Reason::any()}.
|
|
||||||
get_all(ConnPid, Sql, Params) when is_pid(ConnPid), is_binary(Sql), is_list(Params) ->
|
|
||||||
logger:debug("[mysql_client] get_all sql is: ~p, params: ~p", [Sql, Params]),
|
|
||||||
case mysql:query(ConnPid, Sql, Params) of
|
|
||||||
{ok, Names, Rows} ->
|
|
||||||
{ok, lists:map(fun(Row) -> maps:from_list(lists:zip(Names, Row)) end, Rows)};
|
|
||||||
{error, Reason} ->
|
|
||||||
logger:warning("[mysql_client] get error: ~p", [Reason]),
|
|
||||||
{error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec insert(ConnPid :: pid(), Table :: binary(), Fields :: map() | list(), boolean()) ->
|
|
||||||
ok | {ok, InsertId :: integer()} | {error, Reason :: any()}.
|
|
||||||
insert(ConnPid, Table, Fields, FetchInsertId) when is_pid(ConnPid), is_binary(Table), is_map(Fields), is_boolean(FetchInsertId) ->
|
|
||||||
insert(ConnPid, Table, maps:to_list(Fields), FetchInsertId);
|
|
||||||
insert(ConnPid, Table, Fields, FetchInsertId) when is_pid(ConnPid), is_binary(Table), is_list(Fields), is_boolean(FetchInsertId) ->
|
|
||||||
{Keys, Values} = kvs(Fields),
|
|
||||||
|
|
||||||
FieldSql = iolist_to_binary(lists:join(<<", ">>, Keys)),
|
|
||||||
Placeholders = lists:duplicate(length(Keys), <<"?">>),
|
|
||||||
ValuesPlaceholder = iolist_to_binary(lists:join(<<", ">>, Placeholders)),
|
|
||||||
|
|
||||||
Sql = <<"INSERT INTO ", Table/binary, "(", FieldSql/binary, ") VALUES(", ValuesPlaceholder/binary, ")">>,
|
|
||||||
logger:debug("[mysql_client] insert sql is: ~p, params: ~p", [Sql, Values]),
|
|
||||||
case mysql:query(ConnPid, Sql, Values) of
|
|
||||||
ok ->
|
|
||||||
case FetchInsertId of
|
|
||||||
true ->
|
|
||||||
InsertId = mysql:insert_id(ConnPid),
|
|
||||||
{ok, InsertId};
|
|
||||||
false ->
|
|
||||||
ok
|
|
||||||
end;
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec update_by(ConnPid :: pid(), UpdateSql :: binary()) -> {ok, AffectedRows :: integer()} | {error, Reason :: any()}.
|
|
||||||
update_by(ConnPid, UpdateSql) when is_pid(ConnPid), is_binary(UpdateSql) ->
|
|
||||||
logger:debug("[mysql_client] updateBySql sql: ~p", [UpdateSql]),
|
|
||||||
case mysql:query(ConnPid, UpdateSql) of
|
|
||||||
ok ->
|
|
||||||
AffectedRows = mysql:affected_rows(ConnPid),
|
|
||||||
{ok, AffectedRows};
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec update_by(ConnPid :: pid(), UpdateSql :: binary(), Params :: list()) -> {ok, AffectedRows :: integer()} | {error, Reason :: any()}.
|
|
||||||
update_by(ConnPid, UpdateSql, Params) when is_pid(ConnPid), is_binary(UpdateSql) ->
|
|
||||||
logger:debug("[mysql_client] updateBySql sql: ~p, params: ~p", [UpdateSql, Params]),
|
|
||||||
case mysql:query(ConnPid, UpdateSql, Params) of
|
|
||||||
ok ->
|
|
||||||
AffectedRows = mysql:affected_rows(ConnPid),
|
|
||||||
{ok, AffectedRows};
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec update(ConnPid :: pid(), Sql :: binary(), Fields :: map(), WhereFields :: map()) ->
|
|
||||||
{ok, AffectedRows::integer()} | {error, Reason::any()}.
|
|
||||||
update(ConnPid, Table, Fields, WhereFields) when is_pid(ConnPid), is_binary(Table), is_map(Fields), is_map(WhereFields) ->
|
|
||||||
%% 拼接set
|
|
||||||
{SetKeys, SetVals} = kvs(Fields),
|
|
||||||
SetKeys1 = lists:map(fun(K) when is_binary(K) -> <<"`", K/binary, "` = ?">> end, SetKeys),
|
|
||||||
SetSql = iolist_to_binary(lists:join(<<", ">>, SetKeys1)),
|
|
||||||
|
|
||||||
%% 拼接where
|
|
||||||
{WhereKeys, WhereVals} = kvs(WhereFields),
|
|
||||||
WhereKeys1 = lists:map(fun(K) when is_binary(K) -> <<"`", K/binary, "` = ?">> end, WhereKeys),
|
|
||||||
WhereSql = iolist_to_binary(lists:join(<<" AND ">>, WhereKeys1)),
|
|
||||||
|
|
||||||
Params = SetVals ++ WhereVals,
|
|
||||||
|
|
||||||
Sql = <<"UPDATE ", Table/binary, " SET ", SetSql/binary, " WHERE ", WhereSql/binary>>,
|
|
||||||
logger:debug("[mysql_client] update sql is: ~p, params: ~p", [Sql, Params]),
|
|
||||||
case mysql:query(ConnPid, Sql, Params) of
|
|
||||||
ok ->
|
|
||||||
AffectedRows = mysql:affected_rows(ConnPid),
|
|
||||||
{ok, AffectedRows};
|
|
||||||
Error ->
|
|
||||||
logger:error("[mysql_client] update sql: ~p, params: ~p, get a error: ~p", [Sql, Params, Error]),
|
|
||||||
Error
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec kvs(Fields :: map() | list()) -> {Keys :: list(), Values :: list()}.
|
|
||||||
kvs(Fields) when is_map(Fields) ->
|
|
||||||
kvs(maps:to_list(Fields));
|
|
||||||
kvs(Fields) when is_list(Fields) ->
|
|
||||||
{Keys0, Values0} = lists:foldl(fun({K, V}, {Acc0, Acc1}) -> {[K|Acc0], [V|Acc1]} end, {[], []}, Fields),
|
|
||||||
{lists:reverse(Keys0), lists:reverse(Values0)}.
|
|
||||||
176
apps/sdlan/src/net/arp_packet.erl
Normal file
176
apps/sdlan/src/net/arp_packet.erl
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @doc ARP packet parser / builder (Ethernet + IPv4)
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(arp_packet).
|
||||||
|
|
||||||
|
-export([parse/1, marshal/1, is_broadcast_mac/1, arp_request/3, arp_response/3]).
|
||||||
|
-export([opcode/1, target_ip/1]).
|
||||||
|
|
||||||
|
-define(ETHERNET, 16#0001).
|
||||||
|
-define(IPV4, 16#0800).
|
||||||
|
|
||||||
|
-define(ARP_REQUEST, 1).
|
||||||
|
-define(ARP_RESPONSE, 2).
|
||||||
|
|
||||||
|
-define(BROADCAST_MAC, <<255,255,255,255,255,255>>).
|
||||||
|
|
||||||
|
-record(arp_packet, {
|
||||||
|
hardware_type,
|
||||||
|
protocol_type,
|
||||||
|
hardware_size,
|
||||||
|
protocol_size,
|
||||||
|
opcode,
|
||||||
|
sender_mac,
|
||||||
|
sender_ip,
|
||||||
|
target_mac,
|
||||||
|
target_ip
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type arp_packet() :: #arp_packet{
|
||||||
|
hardware_type :: integer(),
|
||||||
|
protocol_type :: integer(),
|
||||||
|
hardware_size :: integer(),
|
||||||
|
protocol_size :: integer(),
|
||||||
|
opcode :: atom() | integer(),
|
||||||
|
sender_mac :: binary(),
|
||||||
|
sender_ip :: integer(),
|
||||||
|
target_mac :: binary(),
|
||||||
|
target_ip :: integer()
|
||||||
|
}.
|
||||||
|
|
||||||
|
-export_type([arp_packet/0]).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Parsing
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec parse(binary()) ->
|
||||||
|
{ok, #arp_packet{}} | {error, invalid_length | invalid_opcode}.
|
||||||
|
parse(Bin) when is_binary(Bin) ->
|
||||||
|
case Bin of
|
||||||
|
<<
|
||||||
|
HType:16/big,
|
||||||
|
PType:16/big,
|
||||||
|
HLen:8,
|
||||||
|
PLen:8,
|
||||||
|
Op:16/big,
|
||||||
|
SMac:6/binary,
|
||||||
|
SIP:32/big,
|
||||||
|
TMac:6/binary,
|
||||||
|
TIP:32/big,
|
||||||
|
_/binary
|
||||||
|
>> ->
|
||||||
|
case decode_opcode(Op) of
|
||||||
|
{ok, Opcode} ->
|
||||||
|
{ok, #arp_packet{
|
||||||
|
hardware_type = HType,
|
||||||
|
protocol_type = PType,
|
||||||
|
hardware_size = HLen,
|
||||||
|
protocol_size = PLen,
|
||||||
|
opcode = Opcode,
|
||||||
|
sender_mac = SMac,
|
||||||
|
sender_ip = SIP,
|
||||||
|
target_mac = TMac,
|
||||||
|
target_ip = TIP
|
||||||
|
}};
|
||||||
|
error ->
|
||||||
|
{error, invalid_opcode}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{error, invalid_length}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec opcode(Arp_packet :: #arp_packet{}) -> atom().
|
||||||
|
opcode(#arp_packet{opcode = Opcode}) ->
|
||||||
|
Opcode.
|
||||||
|
|
||||||
|
-spec target_ip(Arp_packet :: #arp_packet{}) -> TargetIp :: integer().
|
||||||
|
target_ip(#arp_packet{target_ip = TargetIp}) ->
|
||||||
|
TargetIp.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Marshal
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec marshal(#arp_packet{}) -> binary().
|
||||||
|
marshal(#arp_packet{
|
||||||
|
hardware_type = HType,
|
||||||
|
protocol_type = PType,
|
||||||
|
hardware_size = HLen,
|
||||||
|
protocol_size = PLen,
|
||||||
|
opcode = Opcode,
|
||||||
|
sender_mac = SMac,
|
||||||
|
sender_ip = SIP,
|
||||||
|
target_mac = TMac,
|
||||||
|
target_ip = TIP
|
||||||
|
}) ->
|
||||||
|
Op = encode_opcode(Opcode),
|
||||||
|
<<
|
||||||
|
HType:16/big,
|
||||||
|
PType:16/big,
|
||||||
|
HLen:8,
|
||||||
|
PLen:8,
|
||||||
|
Op:16/big,
|
||||||
|
SMac/binary,
|
||||||
|
SIP:32/big,
|
||||||
|
TMac/binary,
|
||||||
|
TIP:32/big
|
||||||
|
>>.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Opcode helpers
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
decode_opcode(?ARP_REQUEST) -> {ok, request};
|
||||||
|
decode_opcode(?ARP_RESPONSE) -> {ok, response};
|
||||||
|
decode_opcode(_) -> error.
|
||||||
|
|
||||||
|
encode_opcode(request) -> ?ARP_REQUEST;
|
||||||
|
encode_opcode(response) -> ?ARP_RESPONSE.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Utils
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec is_broadcast_mac(binary()) -> boolean().
|
||||||
|
is_broadcast_mac(?BROADCAST_MAC) -> true;
|
||||||
|
is_broadcast_mac(_) -> false.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Builders (对齐 Swift 扩展)
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec arp_request(integer(), binary(), integer()) -> #arp_packet{}.
|
||||||
|
arp_request(SenderIP, SenderMAC, TargetIP) ->
|
||||||
|
#arp_packet{
|
||||||
|
hardware_type = ?ETHERNET,
|
||||||
|
protocol_type = ?IPV4,
|
||||||
|
hardware_size = 6,
|
||||||
|
protocol_size = 4,
|
||||||
|
opcode = request,
|
||||||
|
sender_mac = SenderMAC,
|
||||||
|
sender_ip = SenderIP,
|
||||||
|
target_mac = <<0,0,0,0,0,0>>,
|
||||||
|
target_ip = TargetIP
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec arp_response(#arp_packet{}, binary(), integer()) -> #arp_packet{}.
|
||||||
|
arp_response(#arp_packet{
|
||||||
|
hardware_type = HType,
|
||||||
|
protocol_type = PType,
|
||||||
|
hardware_size = HLen,
|
||||||
|
protocol_size = PLen,
|
||||||
|
sender_mac = ReqMac,
|
||||||
|
sender_ip = ReqIP
|
||||||
|
}, Mac, IP) ->
|
||||||
|
#arp_packet{
|
||||||
|
hardware_type = HType,
|
||||||
|
protocol_type = PType,
|
||||||
|
hardware_size = HLen,
|
||||||
|
protocol_size = PLen,
|
||||||
|
opcode = response,
|
||||||
|
sender_mac = Mac,
|
||||||
|
sender_ip = IP,
|
||||||
|
target_mac = ReqMac,
|
||||||
|
target_ip = ReqIP
|
||||||
|
}.
|
||||||
96
apps/sdlan/src/net/layer_packet.erl
Normal file
96
apps/sdlan/src/net/layer_packet.erl
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @doc Ethernet II layer packet parser
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(layer_packet).
|
||||||
|
-export([parse/1, marshal/1, is_broadcast_mac/1, is_multicast_mac/1]).
|
||||||
|
-export([packet_type/1, payload/1]).
|
||||||
|
|
||||||
|
-define(ETH_ARP, 16#0806).
|
||||||
|
-define(ETH_IPV4, 16#0800).
|
||||||
|
-define(ETH_IPV6, 16#86DD).
|
||||||
|
-define(ETH_VLAN, 16#8100).
|
||||||
|
|
||||||
|
-record(layer_packet, {
|
||||||
|
dst_mac,
|
||||||
|
src_mac,
|
||||||
|
type,
|
||||||
|
payload
|
||||||
|
}).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% API
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec parse(binary()) -> {ok, #layer_packet{}} | {error, invalid_length | invalid_type}.
|
||||||
|
parse(Bin) when is_binary(Bin) ->
|
||||||
|
case Bin of
|
||||||
|
<<Dst:6/binary, Src:6/binary, Type:16, Payload/binary>> ->
|
||||||
|
case decode_type(Type) of
|
||||||
|
{ok, T} ->
|
||||||
|
{ok, #layer_packet{
|
||||||
|
dst_mac = Dst,
|
||||||
|
src_mac = Src,
|
||||||
|
type = T,
|
||||||
|
payload = Payload
|
||||||
|
}};
|
||||||
|
error ->
|
||||||
|
{error, invalid_type}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{error, invalid_length}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec packet_type(Layer_packet :: #layer_packet{}) -> atom().
|
||||||
|
packet_type(#layer_packet{type = T}) ->
|
||||||
|
T.
|
||||||
|
|
||||||
|
-spec payload(Layer_packet :: #layer_packet{}) -> binary().
|
||||||
|
payload(#layer_packet{payload = Payload}) ->
|
||||||
|
Payload.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec marshal(#layer_packet{}) -> binary().
|
||||||
|
marshal(#layer_packet{dst_mac = Dst, src_mac = Src, type = Type, payload = Payload}) ->
|
||||||
|
TypeBin = encode_type(Type),
|
||||||
|
<<Dst/binary, Src/binary, TypeBin:16, Payload/binary>>.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% EtherType mapping
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
decode_type(?ETH_ARP) ->
|
||||||
|
{ok, arp};
|
||||||
|
decode_type(?ETH_IPV4) ->
|
||||||
|
{ok, ipv4};
|
||||||
|
decode_type(?ETH_IPV6) ->
|
||||||
|
{ok, ipv6};
|
||||||
|
decode_type(?ETH_VLAN) ->
|
||||||
|
{ok, tagged_frame};
|
||||||
|
decode_type(_) ->
|
||||||
|
error.
|
||||||
|
|
||||||
|
encode_type(arp) ->
|
||||||
|
?ETH_ARP;
|
||||||
|
encode_type(ipv4) ->
|
||||||
|
?ETH_IPV4;
|
||||||
|
encode_type(ipv6) ->
|
||||||
|
?ETH_IPV6;
|
||||||
|
encode_type(tagged_frame) ->
|
||||||
|
?ETH_VLAN.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% MAC helpers (对齐 Swift MacAddress)
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec is_broadcast_mac(binary()) -> boolean().
|
||||||
|
is_broadcast_mac(<<255,255,255,255,255,255>>) ->
|
||||||
|
true;
|
||||||
|
is_broadcast_mac(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
-spec is_multicast_mac(binary()) -> boolean().
|
||||||
|
is_multicast_mac(<<1,0,94,_/binary>>) ->
|
||||||
|
true;
|
||||||
|
is_multicast_mac(_) ->
|
||||||
|
false.
|
||||||
@ -16,7 +16,7 @@
|
|||||||
throttle,
|
throttle,
|
||||||
dns_erlang,
|
dns_erlang,
|
||||||
pkt,
|
pkt,
|
||||||
quicer,
|
parse_trans,
|
||||||
mnesia,
|
mnesia,
|
||||||
erts,
|
erts,
|
||||||
public_key,
|
public_key,
|
||||||
|
|||||||
@ -13,7 +13,9 @@
|
|||||||
|
|
||||||
%% API
|
%% API
|
||||||
-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_access_token/1, node_online/3, node_offline/2, flow_report/5, network_forward_report/2]).
|
||||||
|
|
||||||
|
-export([assign_ip_address/3]).
|
||||||
|
|
||||||
-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() ->
|
||||||
@ -47,9 +49,9 @@ get_network(Id) when is_integer(Id) ->
|
|||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec auth_token(ClientId :: binary(), Token :: binary(), Version :: integer()) -> {ok, Resp :: map()} | {error, Reason :: any()}.
|
-spec auth_access_token(Params :: map()) -> {ok, Resp :: map()} | {error, Reason :: any()}.
|
||||||
auth_token(ClientId, Token, Version) when is_binary(ClientId), is_binary(Token), is_integer(Version) ->
|
auth_access_token(Params) when is_map(Params) ->
|
||||||
case catch do_post("auth_token", #{<<"client_id">> => ClientId, <<"token">> => Token, <<"version">> => Version}) of
|
case catch do_post("auth_token", Params) of
|
||||||
{ok, Resp} ->
|
{ok, Resp} ->
|
||||||
case catch jiffy:decode(Resp, [return_maps]) of
|
case catch jiffy:decode(Resp, [return_maps]) of
|
||||||
Result when is_map(Result) ->
|
Result when is_map(Result) ->
|
||||||
@ -61,13 +63,26 @@ auth_token(ClientId, Token, Version) when is_binary(ClientId), is_binary(Token),
|
|||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec auth_network_code(ClientId :: binary(), NetworkCode :: binary(), Version :: integer()) -> {ok, Resp :: map()} | {error, Reason :: any()}.
|
%% 请求ip地址的分配
|
||||||
auth_network_code(ClientId, NetworkCode, Version) when is_binary(ClientId), is_binary(NetworkCode), is_integer(Version) ->
|
-spec assign_ip_address(NetworkId :: integer(), ClientId :: binary(), Mac :: binary()) -> {ok, map()} | {error, Reason :: any()}.
|
||||||
case catch do_post("check_network", #{<<"client_id">> => ClientId, <<"code">> => NetworkCode, <<"version">> => Version}) of
|
assign_ip_address(NetworkId, ClientId, Mac) when is_integer(NetworkId), is_binary(ClientId), is_binary(Mac) ->
|
||||||
|
Params = #{
|
||||||
|
<<"network_id">> => NetworkId,
|
||||||
|
<<"client_id">> => ClientId,
|
||||||
|
<<"mac">> => Mac
|
||||||
|
},
|
||||||
|
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
|
||||||
Result when is_map(Result) ->
|
Json when is_map(Json) ->
|
||||||
{ok, Result};
|
case Json of
|
||||||
|
#{<<"result">> := Result} ->
|
||||||
|
{ok, Result};
|
||||||
|
#{<<"error">> := #{<<"message">> := Message}} ->
|
||||||
|
{error, Message};
|
||||||
|
_ ->
|
||||||
|
{error, <<"invalid response">>}
|
||||||
|
end;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end;
|
end;
|
||||||
|
|||||||
@ -22,7 +22,6 @@ start(_StartType, _StartArgs) ->
|
|||||||
dns_pending_wheel:start(),
|
dns_pending_wheel:start(),
|
||||||
|
|
||||||
start_http_server(),
|
start_http_server(),
|
||||||
start_tcp_server(),
|
|
||||||
sdlan_sup:start_link().
|
sdlan_sup:start_link().
|
||||||
|
|
||||||
stop(_State) ->
|
stop(_State) ->
|
||||||
@ -57,25 +56,4 @@ start_http_server() ->
|
|||||||
|
|
||||||
{ok, Pid} = cowboy:start_clear(http_listener, TransOpts, #{env => #{dispatch => Dispatcher}}),
|
{ok, Pid} = cowboy:start_clear(http_listener, TransOpts, #{env => #{dispatch => Dispatcher}}),
|
||||||
|
|
||||||
logger:debug("[iot_app] the http server start at: ~p, pid is: ~p", [Port, Pid]).
|
logger:debug("[iot_app] the http server start at: ~p, pid is: ~p", [Port, Pid]).
|
||||||
|
|
||||||
%% 启动tcp服务
|
|
||||||
start_tcp_server() ->
|
|
||||||
{ok, Props} = application:get_env(sdlan, tcp_server),
|
|
||||||
Acceptors = proplists:get_value(acceptors, Props, 50),
|
|
||||||
MaxConnections = proplists:get_value(max_connections, Props, 10240),
|
|
||||||
Backlog = proplists:get_value(backlog, Props, 1024),
|
|
||||||
Port = proplists:get_value(port, Props),
|
|
||||||
|
|
||||||
TransOpts = #{
|
|
||||||
num_acceptors => Acceptors,
|
|
||||||
max_connections => MaxConnections,
|
|
||||||
socket_opts => [
|
|
||||||
{port, Port},
|
|
||||||
{nodelay, false},
|
|
||||||
{backlog, Backlog}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ok, _} = ranch:start_listener('sdlan/tcp_server', ranch_tcp, TransOpts, sdlan_channel, []),
|
|
||||||
|
|
||||||
logger:debug("[sdlan_app] the tcp server start at: ~p", [Port]).
|
|
||||||
@ -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, 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),
|
|
||||||
|
|
||||||
%% 参数检查
|
|
||||||
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, NetAddr0, HostName) 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, 0, HostName) 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)).
|
|
||||||
@ -8,19 +8,12 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(sdlan_cipher).
|
-module(sdlan_cipher).
|
||||||
-author("anlicheng").
|
-author("anlicheng").
|
||||||
|
-include_lib("public_key/include/public_key.hrl").
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([rsa_encrypt/2, rsa_pem_decode/1]).
|
-export([rsa_encrypt/2, rsa_pem_decode/1, rsa_decrypt/2]).
|
||||||
-export([aes_encrypt/3, aes_decrypt/3]).
|
-export([aes_encrypt/3, aes_decrypt/3]).
|
||||||
-export([test/0]).
|
-export([rsa_generate_key/0]).
|
||||||
|
|
||||||
test() ->
|
|
||||||
Key = <<"abcdabcdabcdabcd">>,
|
|
||||||
X = aes_encrypt(Key, Key, <<"hello world">>),
|
|
||||||
logger:debug("x is: ~p, raw: ~p", [X, aes_decrypt(Key, Key, X)]),
|
|
||||||
|
|
||||||
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec rsa_pem_decode(PubKey :: binary()) -> public_key:rsa_public_key().
|
-spec rsa_pem_decode(PubKey :: binary()) -> public_key:rsa_public_key().
|
||||||
rsa_pem_decode(PubKey) when is_binary(PubKey) ->
|
rsa_pem_decode(PubKey) when is_binary(PubKey) ->
|
||||||
@ -32,6 +25,23 @@ rsa_pem_decode(PubKey) when is_binary(PubKey) ->
|
|||||||
rsa_encrypt(BinData, PublicKey) when is_binary(BinData) ->
|
rsa_encrypt(BinData, PublicKey) when is_binary(BinData) ->
|
||||||
public_key:encrypt_public(BinData, PublicKey, [{rsa_padding, rsa_pkcs1_padding}]).
|
public_key:encrypt_public(BinData, PublicKey, [{rsa_padding, rsa_pkcs1_padding}]).
|
||||||
|
|
||||||
|
-spec rsa_decrypt(EncData :: binary(), PrivateKey :: public_key:rsa_private_key()) -> binary().
|
||||||
|
rsa_decrypt(EncData, PrivateKey) when is_binary(EncData) ->
|
||||||
|
public_key:decrypt_private(EncData, PrivateKey, [{rsa_padding, rsa_pkcs1_padding}]).
|
||||||
|
|
||||||
|
rsa_generate_key() ->
|
||||||
|
PrivateKey = public_key:generate_key({rsa, 2048, 65537}),
|
||||||
|
PublicKey = #'RSAPublicKey'{
|
||||||
|
modulus = PrivateKey#'RSAPrivateKey'.modulus,
|
||||||
|
publicExponent = PrivateKey#'RSAPrivateKey'.publicExponent
|
||||||
|
},
|
||||||
|
|
||||||
|
PemBin = public_key:pem_encode([
|
||||||
|
public_key:pem_entry_encode('RSAPublicKey', PublicKey)
|
||||||
|
]),
|
||||||
|
PK = binary_to_list(PemBin),
|
||||||
|
{PK, PrivateKey}.
|
||||||
|
|
||||||
%% 基于aes的加密算法
|
%% 基于aes的加密算法
|
||||||
-spec aes_encrypt(binary(), binary(), binary()) -> binary().
|
-spec aes_encrypt(binary(), binary(), binary()) -> binary().
|
||||||
aes_encrypt(Key, IVec, PlainText) when is_binary(Key), is_binary(IVec), is_binary(PlainText) ->
|
aes_encrypt(Key, IVec, PlainText) when is_binary(Key), is_binary(IVec), is_binary(PlainText) ->
|
||||||
@ -40,4 +50,5 @@ aes_encrypt(Key, IVec, PlainText) when is_binary(Key), is_binary(IVec), is_binar
|
|||||||
%% 基于aes的解密算法
|
%% 基于aes的解密算法
|
||||||
-spec aes_decrypt(binary(), binary(), binary()) -> binary().
|
-spec aes_decrypt(binary(), binary(), binary()) -> binary().
|
||||||
aes_decrypt(Key, IVec, CipherText) when is_binary(Key), is_binary(IVec), is_binary(CipherText) ->
|
aes_decrypt(Key, IVec, CipherText) when is_binary(Key), is_binary(IVec), is_binary(CipherText) ->
|
||||||
crypto:crypto_one_time(aes_128_ofb, Key, IVec, CipherText, [{encrypt, false}, {padding, pkcs_padding}]).
|
crypto:crypto_one_time(aes_128_ofb, Key, IVec, CipherText, [{encrypt, false}, {padding, pkcs_padding}]).
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
-author("anlicheng").
|
-author("anlicheng").
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/0, lookup/1, insert/2]).
|
-export([init/0, lookup/1, insert/2, insert/3]).
|
||||||
|
|
||||||
-define(TABLE, sdlan_hostname_regedit).
|
-define(TABLE, sdlan_hostname_regedit).
|
||||||
|
|
||||||
@ -26,6 +26,13 @@ lookup(FullHostname) when is_binary(FullHostname) ->
|
|||||||
error
|
error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec insert(any(), Domain :: binary(), Ip :: integer()) -> no_return().
|
||||||
|
insert(HostName, Domain, Ip) when is_binary(HostName), is_binary(Domain), is_integer(Ip), HostName /= <<>> ->
|
||||||
|
FullHostname = <<HostName/binary, ".", Domain/binary>>,
|
||||||
|
insert(FullHostname, Ip);
|
||||||
|
insert(_, _, _) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
-spec insert(FullHostname :: binary(), Ip :: integer()) -> no_return().
|
-spec insert(FullHostname :: binary(), Ip :: integer()) -> no_return().
|
||||||
insert(FullHostname, Ip) when is_binary(FullHostname), is_integer(Ip) ->
|
insert(FullHostname, Ip) when is_binary(FullHostname), is_integer(Ip) ->
|
||||||
<<Ip0, Ip1, Ip2, Ip3>> = <<Ip:32>>,
|
<<Ip0, Ip1, Ip2, Ip3>> = <<Ip:32>>,
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
%%% Created : 27. 3月 2024 15:13
|
%%% Created : 27. 3月 2024 15:13
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(sdlan_network).
|
-module(sdlan_network).
|
||||||
|
-feature(maybe_expr, enable).
|
||||||
|
|
||||||
-author("anlicheng").
|
-author("anlicheng").
|
||||||
-include("sdlan.hrl").
|
-include("sdlan.hrl").
|
||||||
-include("sdlan_pb.hrl").
|
-include("sdlan_pb.hrl").
|
||||||
@ -14,15 +16,16 @@
|
|||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-define(FLOW_REPORT_INTERVAL, 60 * 1000).
|
-define(FLOW_REPORT_INTERVAL, 60_000).
|
||||||
|
-define(ENDPOINT_GC_INTERVAL, 30_000).
|
||||||
|
|
||||||
%% broadcast, "FF-FF-FF-FF-FF-FF"
|
%% broadcast, "FF-FF-FF-FF-FF-FF"
|
||||||
-define(BROADCAST_MAC, <<16#FF,16#FF,16#FF,16#FF,16#FF,16#FF>>).
|
-define(BROADCAST_MAC, <<16#FF,16#FF,16#FF,16#FF,16#FF,16#FF>>).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/2]).
|
-export([start_link/2]).
|
||||||
-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([get_name/1, get_pid/1, lookup_pid/1, attach/6, peer_info/4, unregister/3, debug_info/1, get_network_id/1, arp_query/2]).
|
||||||
-export([forward/5, update_hole/6, disable_client/2, get_channel/2, dropout_client/2, reload/1]).
|
-export([forward/5, stun_request/4, policy_request/4, disable_client/2, dropout_client/2]).
|
||||||
-export([test_event/1]).
|
-export([test_event/1]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
@ -33,14 +36,17 @@
|
|||||||
nat_type :: integer()
|
nat_type :: integer()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%% ip的使用信息, 记录主机的动态信息
|
%% ip的使用信息, 记录Node的运行时状态信息
|
||||||
-record(host, {
|
-record(endpoint, {
|
||||||
client_id :: binary(),
|
client_id :: binary(),
|
||||||
channel_pid :: undefined | pid(),
|
mac :: binary(),
|
||||||
monitor_ref :: undefined | reference(),
|
ip :: integer(),
|
||||||
hole :: undefined | #hole{},
|
hostname :: binary(),
|
||||||
|
hole :: #hole{},
|
||||||
%% 记录ip和ip_v6的映射关系, #{ip_addr :: integer() => {}}
|
%% 记录ip和ip_v6的映射关系, #{ip_addr :: integer() => {}}
|
||||||
v6_info :: undefined | #sdl_v6_info{}
|
v6_info :: undefined | #sdl_v6_info{},
|
||||||
|
session_token :: binary(),
|
||||||
|
last_seen :: integer() %% monotonic_time(second),
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
@ -49,23 +55,14 @@
|
|||||||
domain :: binary(),
|
domain :: binary(),
|
||||||
ipaddr :: binary(),
|
ipaddr :: binary(),
|
||||||
mask_len :: integer(),
|
mask_len :: integer(),
|
||||||
owner_id :: integer(),
|
|
||||||
|
|
||||||
%% 设置网络带宽
|
%% 设置网络带宽
|
||||||
throttle_key :: atom(),
|
throttle_key :: atom(),
|
||||||
|
|
||||||
%% 转发流量统计
|
%% 转发流量统计
|
||||||
forward_bytes = 0,
|
forward_bytes = 0,
|
||||||
|
|
||||||
%% 同一个网络下公用的密钥, 采用AES-256加密算法;随机生成
|
%% 同一个网络下公用的密钥, 采用AES-256加密算法;随机生成
|
||||||
aes_key :: binary(),
|
aes_key :: binary(),
|
||||||
|
%% 记录已经使用了的ip, #{mac => #endpoint{}}
|
||||||
%% ip分配器
|
endpoints = #{} :: map()
|
||||||
%% 记录当前网络下的全部ip地址
|
|
||||||
ips = [] :: [Ip :: integer()],
|
|
||||||
|
|
||||||
%% 记录已经使用了的ip, #{mac :: integer() => Host :: #host{}}
|
|
||||||
used_map = #{}
|
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -80,49 +77,57 @@ test_event(Pid) ->
|
|||||||
get_pid(Id) when is_integer(Id) ->
|
get_pid(Id) when is_integer(Id) ->
|
||||||
whereis(get_name(Id)).
|
whereis(get_name(Id)).
|
||||||
|
|
||||||
|
-spec lookup_pid(Id :: integer()) -> error | {ok, Pid :: pid()}.
|
||||||
|
lookup_pid(Id) when is_integer(Id) ->
|
||||||
|
case whereis(get_name(Id)) of
|
||||||
|
Pid when is_pid(Pid) ->
|
||||||
|
{ok, Pid};
|
||||||
|
undefined ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
-spec get_name(Id :: integer()) -> atom().
|
-spec get_name(Id :: integer()) -> atom().
|
||||||
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 reload(Pid :: pid()) -> ok | {error, Reason :: any()}.
|
-spec attach(Pid :: pid(), Peer :: {Ip :: inet:ip4_address(), Port :: integer()}, ClientId :: binary(), Mac :: binary(), Ip :: integer(), HostName :: binary()) ->
|
||||||
reload(Pid) when is_pid(Pid) ->
|
{ok, AesKey :: binary(), SessionToken :: binary()}.
|
||||||
gen_server:call(Pid, reload).
|
attach(Pid, Peer, ClientId, Mac, Ip, HostName) when is_pid(Pid), is_binary(ClientId), is_binary(Mac), is_integer(Ip) ->
|
||||||
|
gen_server:call(Pid, {attach, Peer, ClientId, Mac, Ip, HostName}).
|
||||||
-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()}.
|
|
||||||
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, 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) ->
|
||||||
gen_server:call(Pid, get_network_id).
|
gen_server:call(Pid, get_network_id).
|
||||||
|
|
||||||
|
-spec arp_query(Pid :: pid(), TargetIp :: integer()) -> {ok, TargetMac :: binary()} | error.
|
||||||
|
arp_query(Pid, TargetIp) when is_pid(Pid), is_integer(TargetIp) ->
|
||||||
|
gen_server:call(Pid, {arp_query, TargetIp}).
|
||||||
|
|
||||||
-spec unregister(Pid :: pid(), ClientId :: binary(), Mac :: binary()) -> no_return().
|
-spec unregister(Pid :: pid(), ClientId :: binary(), Mac :: binary()) -> no_return().
|
||||||
unregister(Pid, ClientId, Mac) when is_pid(Pid), is_binary(ClientId), is_binary(Mac) ->
|
unregister(Pid, ClientId, Mac) when is_pid(Pid), is_binary(ClientId), is_binary(Mac) ->
|
||||||
gen_server:cast(Pid, {unregister, ClientId, Mac}).
|
gen_server:cast(Pid, {unregister, ClientId, Mac}).
|
||||||
|
|
||||||
-spec peer_info(Pid :: pid(), SrcMac :: binary(), DstMac :: binary()) ->
|
-spec peer_info(Pid :: pid(), Sock :: inet:socket(), Peer :: {inet:ip4_address(), integer()}, Query :: #sdl_query_info{}) -> no_return().
|
||||||
error | {ok, {NatPeer :: {Ip :: inet:ip4_address(), Port :: integer()}, NatType :: integer()}, V6Info :: undefined | #sdl_v6_info{}}.
|
peer_info(Pid, Sock, ClientPeer, Query) when is_pid(Pid) ->
|
||||||
peer_info(Pid, SrcMac, DstMac) when is_pid(Pid), is_binary(SrcMac), is_binary(DstMac) ->
|
gen_server:cast(Pid, {peer_info, Sock, ClientPeer, Query}).
|
||||||
gen_server:call(Pid, {peer_info, SrcMac, DstMac}).
|
|
||||||
|
|
||||||
-spec forward(pid(), Sock :: any(), SrcMac :: binary(), DstMac :: binary(), Packet :: binary()) -> no_return().
|
-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) ->
|
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}).
|
gen_server:cast(Pid, {forward, Sock, SrcMac, DstMac, Packet}).
|
||||||
|
|
||||||
%% 更新ip地址对应的nat关系
|
%% 更新ip地址对应的nat关系
|
||||||
-spec update_hole(Pid :: pid(), ClientId :: binary(), Mac :: binary(), Peer :: tuple(), NatType :: integer(), V6Info :: undefined | #sdl_v6_info{}) -> no_return().
|
-spec stun_request(Pid :: pid(), Sock :: inet:socket(), ClientPeer :: {inet:ip4_address(), integer()}, StunRequest :: #sdl_stun_request{}) -> no_return().
|
||||||
update_hole(Pid, ClientId, Mac, Peer, NatType, V6Info) when is_pid(Pid), is_binary(ClientId), is_binary(Mac), is_integer(NatType) ->
|
stun_request(Pid, Sock, ClientPeer, StunRequest) when is_pid(Pid) ->
|
||||||
gen_server:cast(Pid, {update_hole, ClientId, Mac, Peer, NatType, V6Info}).
|
gen_server:cast(Pid, {stun_request, Sock, ClientPeer, StunRequest}).
|
||||||
|
|
||||||
-spec disable_client(Pid :: pid(), ClientId :: binary()) -> ok | error.
|
-spec policy_request(Pid :: pid(), Sock :: inet:socket(), ClientPeer :: {inet:ip4_address(), integer()}, PolicyRequest :: #sdl_policy_request{}) -> no_return().
|
||||||
|
policy_request(Pid, Sock, ClientPeer, PolicyRequest) when is_pid(Pid) ->
|
||||||
|
gen_server:cast(Pid, {policy_request, Sock, ClientPeer, PolicyRequest}).
|
||||||
|
|
||||||
|
-spec disable_client(Pid :: pid(), ClientId :: binary()) -> ok.
|
||||||
disable_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
disable_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
||||||
gen_server:call(Pid, {disable_client, ClientId}).
|
gen_server:call(Pid, {disable_client, ClientId}).
|
||||||
|
|
||||||
-spec get_channel(Pid :: pid(), ClientId :: binary()) -> error | {ok, ChannelPid :: pid()}.
|
|
||||||
get_channel(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
|
||||||
gen_server:call(Pid, {get_channel, ClientId}).
|
|
||||||
|
|
||||||
%% 剔除client_id,channel不关闭; channel会被重新绑定到其他的network里面
|
%% 剔除client_id,channel不关闭; channel会被重新绑定到其他的network里面
|
||||||
-spec dropout_client(Pid :: pid(), ClientId :: binary()) -> {ok, ChannelPid :: pid(), HostName :: binary()} | error.
|
-spec dropout_client(Pid :: pid(), ClientId :: binary()) -> {ok, ChannelPid :: pid(), HostName :: binary()} | error.
|
||||||
dropout_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
dropout_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
||||||
@ -132,12 +137,6 @@ dropout_client(Pid, ClientId) when is_pid(Pid), is_binary(ClientId) ->
|
|||||||
debug_info(Pid) when is_pid(Pid) ->
|
debug_info(Pid) when is_pid(Pid) ->
|
||||||
gen_server:call(Pid, debug_info).
|
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)
|
%% @doc Spawns the server and registers the local name (unique)
|
||||||
-spec(start_link(Name :: atom(), Id :: integer()) ->
|
-spec(start_link(Name :: atom(), Id :: integer()) ->
|
||||||
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
||||||
@ -154,14 +153,13 @@ start_link(Name, Id) when is_atom(Name), is_integer(Id) ->
|
|||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term()} | ignore).
|
{stop, Reason :: term()} | ignore).
|
||||||
init([Id]) when is_integer(Id) ->
|
init([Id]) when is_integer(Id) ->
|
||||||
erlang:process_flag(trap_exit, true),
|
|
||||||
case sdlan_api:get_network(Id) of
|
case sdlan_api:get_network(Id) of
|
||||||
{ok, #{<<"ipaddr">> := Null}} when Null == <<"null">>; Null == <<"NULL">> ->
|
{ok, #{<<"ipaddr">> := Null}} when Null == <<"null">>; Null == <<"NULL">> ->
|
||||||
ignore;
|
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),
|
{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)),
|
||||||
%% 绑定到资源协调器
|
%% 绑定到资源协调器
|
||||||
@ -170,14 +168,12 @@ 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),
|
||||||
|
|
||||||
%% 创建数据库表
|
%% endpoint定时扫描器
|
||||||
create_mnesia_table(Id),
|
erlang:start_timer(?ENDPOINT_GC_INTERVAL, self(), endpoint_gc),
|
||||||
|
|
||||||
logger: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, ips = Ips, 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} ->
|
{error, Reason} ->
|
||||||
logger:warning("[sdlan_network] load network: ~p, get error: ~p", [Id, Reason]),
|
logger:warning("[sdlan_network] load network: ~p, get error: ~p", [Id, Reason]),
|
||||||
ignore
|
ignore
|
||||||
@ -193,171 +189,62 @@ 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{}}).
|
||||||
%% 重新加载网络信息
|
|
||||||
handle_call(reload, _From, State = #state{network_id = Id, ipaddr = OldIpAddr, mask_len = OldMarkLen, used_map = UsedMap}) ->
|
|
||||||
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 ->
|
|
||||||
logger: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),
|
|
||||||
|
|
||||||
{reply, ok, State#state{name = Name, ipaddr = IpAddr,
|
|
||||||
owner_id = OwnerId, mask_len = MaskLen, ips = Ips, used_map = maps:new()}}
|
|
||||||
end;
|
|
||||||
{error, Reason} ->
|
|
||||||
logger:warning("[sdlan_network] reload network: ~p, get error: ~p", [Id, Reason]),
|
|
||||||
{reply, {error, Reason}, State}
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% 给客户端分配ip地址
|
%% 给客户端分配ip地址
|
||||||
handle_call({assign_ip_addr, ChannelPid, ClientId, Mac, NetAddr0, HostName}, _From,
|
handle_call({attach, Peer, ClientId, Mac, Ip, Hostname}, _From,
|
||||||
State = #state{network_id = NetworkId, domain = Domain, ips = Ips, used_map = UsedMap, mask_len = MaskLen, aes_key = AesKey}) ->
|
State = #state{network_id = NetworkId, domain = Domain, endpoints = Endpoints, aes_key = AesKey}) ->
|
||||||
|
|
||||||
%% 分配ip地址的时候,以mac地址为唯一基准
|
%% 分配ip地址的时候,以mac地址为唯一基准
|
||||||
logger:debug("[sdlan_network] alloc_ip, network_id: ~p, ips: ~p, client_id: ~p, mac: ~p, net_addr: ~p",
|
logger:debug("[sdlan_network] alloc_ip, network_id: ~p, client_id: ~p, mac: ~p, ip_addr: ~p",
|
||||||
[NetworkId, Ips, ClientId, sdlan_util:format_mac(Mac), sdlan_ipaddr:int_to_ipv4(NetAddr0)]),
|
[NetworkId, ClientId, sdlan_util:format_mac(Mac), sdlan_ipaddr:int_to_ipv4(Ip)]),
|
||||||
|
%% 添加域名->ip的映射关系
|
||||||
|
sdlan_hostname_regedit:insert(Hostname, Domain, Ip),
|
||||||
|
|
||||||
case client_model:alloc_ip(NetworkId, Ips, ClientId, Mac, NetAddr0, HostName) of
|
%% mac对应的Endpoint存在,并且对应的ip变了,需要通知端上清理arp
|
||||||
{ok, Ip} ->
|
maybe
|
||||||
%% 关闭之前的channel
|
{ok, #endpoint{ip = OldIp}} ?= maps:find(Mac, Endpoints),
|
||||||
maybe_close_channel(maps:get(Mac, UsedMap, undefined)),
|
true ?= OldIp =/= Ip,
|
||||||
|
NatChangedEvent = sdlan_pb:encode_msg(#sdl_nat_changed_event{
|
||||||
|
mac = Mac,
|
||||||
|
ip = Ip
|
||||||
|
}),
|
||||||
|
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_NAT_CHANGED, NatChangedEvent/binary>>,
|
||||||
|
|
||||||
%% 添加域名->ip的映射关系
|
EndpointPeers = endpoint_peers([Mac], Endpoints),
|
||||||
case HostName =/= <<>> of
|
sdlan_stun_pool:send_packets(EndpointPeers, EventPacket)
|
||||||
true ->
|
end,
|
||||||
FullHostname = <<HostName/binary, ".", Domain/binary>>,
|
|
||||||
sdlan_hostname_regedit:insert(FullHostname, Ip);
|
|
||||||
false ->
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
|
|
||||||
%% 建立到新的channel之间的关系
|
SessionToken = gen_session_token(),
|
||||||
MRef = monitor(process, ChannelPid),
|
Endpoint = #endpoint{client_id = ClientId, mac = Mac, ip = Ip, hostname = Hostname, session_token = SessionToken,
|
||||||
NUsedMap = maps:put(Mac, #host{client_id = ClientId, channel_pid = ChannelPid, monitor_ref = MRef}, UsedMap),
|
hole = #hole{peer = Peer, nat_type = 0}, last_seen = erlang:monotonic_time(second)},
|
||||||
|
|
||||||
{reply, {ok, Domain, Ip, MaskLen, AesKey}, State#state{used_map = NUsedMap}};
|
{reply, {ok, AesKey, SessionToken}, State#state{endpoints = maps:put(Mac, Endpoint, Endpoints)}};
|
||||||
{error, Reason} ->
|
|
||||||
{reply, {error, Reason}, State}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_call(get_used_map, _From, State = #state{used_map = UsedMap}) ->
|
|
||||||
UsedInfos = maps:map(fun(_, #host{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, UsedMap),
|
|
||||||
|
|
||||||
{reply, {ok, UsedInfos}, State};
|
|
||||||
|
|
||||||
%% client设置为禁止状态,不允许重连
|
%% client设置为禁止状态,不允许重连
|
||||||
handle_call({disable_client, ClientId}, _From, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
|
handle_call({disable_client, ClientId}, _From, State = #state{endpoints = Endpoints}) ->
|
||||||
case lists:search(fun({_, #host{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of
|
case search_endpoint(fun(_, #endpoint{client_id = ClientId0}) -> ClientId =:= ClientId0 end, Endpoints) of
|
||||||
{value, {Mac, #host{channel_pid = ChannelPid, monitor_ref = MRef}}} ->
|
{ok, Mac, _} ->
|
||||||
is_reference(MRef) andalso demonitor(MRef),
|
{reply, ok, State#state{endpoints = maps:remove(Mac, Endpoints)}};
|
||||||
sdlan_channel:stop(ChannelPid, disable),
|
error ->
|
||||||
NUsedMap = maps:remove(Mac, UsedMap),
|
{reply, ok, State}
|
||||||
%% 将客户端设置为禁止状态
|
|
||||||
client_model:disable_client(NetworkId, ClientId),
|
|
||||||
|
|
||||||
{reply, ok, State#state{used_map = NUsedMap}};
|
|
||||||
false ->
|
|
||||||
{reply, error, State}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_call({get_channel, ClientId}, _From, State = #state{used_map = UsedMap}) ->
|
|
||||||
case lists:search(fun({_, #host{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of
|
|
||||||
{value, {_Ip, #host{channel_pid = ChannelPid}}} ->
|
|
||||||
{reply, {ok, ChannelPid}, State};
|
|
||||||
false ->
|
|
||||||
{reply, error, State}
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% 区别在于是否关闭掉channel, 这里是在网络迁移中的功能; drop的时候需要从当前网络中移除
|
|
||||||
handle_call({dropout_client, ClientId}, _From, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
|
|
||||||
case lists:search(fun({_, #host{client_id = ClientId0}}) -> ClientId =:= ClientId0 end, maps:to_list(UsedMap)) of
|
|
||||||
{value, {Mac, #host{channel_pid = ChannelPid, monitor_ref = MRef}}} ->
|
|
||||||
is_reference(MRef) andalso demonitor(MRef),
|
|
||||||
NUsedMap = maps:remove(Mac, UsedMap),
|
|
||||||
%% 从数据库删除
|
|
||||||
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 ->
|
|
||||||
{reply, error, State}
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_call(get_network_id, _From, State = #state{network_id = NetworkId}) ->
|
handle_call(get_network_id, _From, State = #state{network_id = NetworkId}) ->
|
||||||
{reply, {ok, NetworkId}, State};
|
{reply, {ok, NetworkId}, State};
|
||||||
|
|
||||||
%% 网络存在的nat_peer信息
|
%% arp查询
|
||||||
handle_call({peer_info, SrcMac, DstMac}, _From, State = #state{used_map = UsedMap}) ->
|
handle_call({arp_query, TargetIp}, _From, State = #state{endpoints = Endpoints}) ->
|
||||||
case maps:find(DstMac, UsedMap) of
|
case search_endpoint(fun(_, #endpoint{ip = Ip0}) -> TargetIp =:= Ip0 end, Endpoints) of
|
||||||
{ok, #host{channel_pid = DstChannelPid, hole = #hole{peer = DstNatPeer, nat_type = DstNatType}, v6_info = DstV6Info}} ->
|
{ok, TargetMac, _} ->
|
||||||
%% 让目标服务器发送sendRegister事件(2024-06-25 新增,提高打洞的成功率)
|
{reply, {ok, TargetMac}, State};
|
||||||
case maps:get(SrcMac, UsedMap, undefined) of
|
error ->
|
||||||
#host{hole = #hole{peer = {SrcNatIp, SrcNatPort}, nat_type = NatType}, v6_info = SrcV6Info} ->
|
|
||||||
Event = sdlan_pb:encode_msg(#sdl_send_register_event {
|
|
||||||
dst_mac = SrcMac,
|
|
||||||
nat_ip = sdlan_ipaddr:ipv4_to_int(SrcNatIp),
|
|
||||||
nat_type = NatType,
|
|
||||||
nat_port = SrcNatPort,
|
|
||||||
v6_info = SrcV6Info
|
|
||||||
}),
|
|
||||||
sdlan_channel:send_event(DstChannelPid, ?PACKET_EVENT_SEND_REGISTER, Event);
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
{reply, {ok, {DstNatPeer, DstNatType}, DstV6Info}, State};
|
|
||||||
_ ->
|
|
||||||
{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, ips = Ips, used_map = UsedMap}) ->
|
handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = IpAddr, mask_len = MaskLen, endpoints = Endpoints}) ->
|
||||||
Reply = #{
|
Reply = #{
|
||||||
<<"network_id">> => NetworkId,
|
<<"network_id">> => NetworkId,
|
||||||
<<"ipaddr">> => IpAddr,
|
<<"ipaddr">> => IpAddr,
|
||||||
<<"mask_len">> => MaskLen,
|
<<"mask_len">> => MaskLen,
|
||||||
<<"owner_id">> => OwnerId,
|
<<"used_ips">> => lists:map(fun format_endpoint/1, maps:to_list(Endpoints))
|
||||||
<<"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}.
|
||||||
|
|
||||||
@ -367,35 +254,63 @@ handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = I
|
|||||||
{noreply, NewState :: #state{}} |
|
{noreply, NewState :: #state{}} |
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
{stop, Reason :: term(), NewState :: #state{}}).
|
||||||
|
|
||||||
|
%% 网络存在的nat_peer信息
|
||||||
|
%% TODO SrcMac不存在的时候需要重新校验
|
||||||
|
handle_cast({peer_info, Sock, {ClientIp, ClientPort}, #sdl_query_info{pkt_id = PktId, src_mac = SrcMac, dst_mac = DstMac}},
|
||||||
|
State = #state{network_id = NetworkId, endpoints = Endpoints}) ->
|
||||||
|
|
||||||
|
logger:debug("[sdlan_network] network_id: ~p, query_info, src_mac: ~p, dst_mac: ~p",
|
||||||
|
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]),
|
||||||
|
|
||||||
|
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 新增,提高打洞的成功率)
|
||||||
|
maybe_preload_hole(DstNatPeer, maps:get(SrcMac, Endpoints, undefined)),
|
||||||
|
|
||||||
|
PeerInfo = sdlan_pb:encode_msg(#sdl_peer_info{
|
||||||
|
pkt_id = PktId,
|
||||||
|
network_id = NetworkId,
|
||||||
|
dst_mac = DstMac,
|
||||||
|
v4_info = #sdl_v4_info {
|
||||||
|
port = DstNatPort,
|
||||||
|
v4 = <<Ip0, Ip1, Ip2, Ip3>>,
|
||||||
|
nat_type = DstNatType
|
||||||
|
},
|
||||||
|
v6_info = DstV6Info
|
||||||
|
}),
|
||||||
|
PeerPacket = <<?PACKET_PEER_INFO, PeerInfo/binary>>,
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, PeerPacket);
|
||||||
|
_ ->
|
||||||
|
EmptyPeerInfo = sdlan_pb:encode_msg(#sdl_empty_peer_info{
|
||||||
|
pkt_id = PktId,
|
||||||
|
network_id = NetworkId,
|
||||||
|
dst_mac = DstMac
|
||||||
|
}),
|
||||||
|
EmptyPeerPacket = <<?PACKET_PEER_INFO, EmptyPeerInfo/binary>>,
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, EmptyPeerPacket)
|
||||||
|
end,
|
||||||
|
{noreply, State};
|
||||||
|
|
||||||
%% 网络数据转发, mac地址单播
|
%% 网络数据转发, mac地址单播
|
||||||
handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id = NetworkId, used_map = UsedMap, throttle_key = ThrottleKey, forward_bytes = ForwardBytes})
|
handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id = NetworkId, endpoints = Endpoints, throttle_key = ThrottleKey, forward_bytes = ForwardBytes})
|
||||||
when is_map_key(SrcMac, UsedMap), is_map_key(DstMac, UsedMap) ->
|
when is_map_key(SrcMac, Endpoints), is_map_key(DstMac, Endpoints) ->
|
||||||
|
|
||||||
PacketBytes = byte_size(Packet),
|
PacketBytes = byte_size(Packet),
|
||||||
case maps:find(DstMac, UsedMap) of
|
case maps:find(DstMac, Endpoints) of
|
||||||
{ok, #host{hole = #hole{peer = Peer = {Ip, Port}}}} ->
|
{ok, #endpoint{hole = #hole{peer = Peer = {Ip, Port}}}} ->
|
||||||
case throttle:check(sdlan_network, ThrottleKey) of
|
case limiting_check(ThrottleKey) of
|
||||||
{ok, _RestCount, _LeftToReset} ->
|
pass ->
|
||||||
%% client和stun之间必须有心跳机制保持nat映射可用,并且通过服务转发的udp包肯定可以到达对端的nat
|
%% client和stun之间必须有心跳机制保持nat映射可用,并且通过服务转发的udp包肯定可以到达对端的nat
|
||||||
logger:debug("[sdlan_network] forward data networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole: ~p",
|
logger: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}};
|
||||||
{limit_exceeded, 0, _LeftToReset} ->
|
denied ->
|
||||||
%% 尝试获取其他网络是否有让渡的资源
|
logger:notice("[sdlan_network] networkd_id: ~p, src_mac: ~p, dst_mac: ~p, rate limited, discard",
|
||||||
case sdlan_network_coordinator:checkout() of
|
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]),
|
||||||
ok ->
|
{noreply, State}
|
||||||
logger: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 ->
|
|
||||||
logger: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, _} ->
|
||||||
logger:debug("[sdlan_network] networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole not found",
|
logger:debug("[sdlan_network] networkd_id: ~p, src_mac: ~p, dst_mac: ~p, hole not found",
|
||||||
@ -408,23 +323,16 @@ handle_cast({forward, Sock, SrcMac, DstMac, Packet}, State = #state{network_id =
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
%% 网络数据转发, ip广播或组播, 不限流
|
%% 网络数据转发, 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, endpoints = Endpoints, forward_bytes = ForwardBytes})
|
||||||
when is_map_key(SrcMac, UsedMap) ->
|
when is_map_key(SrcMac, Endpoints) ->
|
||||||
%% 广播地址和组播地址,需要转发到整个网络
|
%% 广播地址和组播地址,需要转发到整个网络
|
||||||
case sdlan_util:is_broadcast_mac(DstMac) orelse sdlan_util:is_multicast_mac(DstMac) of
|
case sdlan_util:is_broadcast_mac(DstMac) orelse sdlan_util:is_multicast_mac(DstMac) of
|
||||||
true ->
|
true ->
|
||||||
PacketBytes = byte_size(Packet),
|
PacketBytes = byte_size(Packet),
|
||||||
%% 消息广播
|
%% 消息广播
|
||||||
maps:foreach(fun(Mac, #host{hole = Hole}) ->
|
broadcast(fun(#endpoint{hole = #hole{peer = {NatIp, NatPort}}}) ->
|
||||||
case {Mac =/= SrcMac, Hole} of
|
gen_udp:send(Sock, NatIp, NatPort, Packet)
|
||||||
{true, #hole{peer = {NatIp, NatPort}}} ->
|
end, [SrcMac], Endpoints),
|
||||||
logger:debug("[sdlan_network] call me here"),
|
|
||||||
gen_udp:send(Sock, NatIp, NatPort, Packet);
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end, UsedMap),
|
|
||||||
|
|
||||||
%% client和stun之间必须有心跳机制保持nat映射可用,并且通过服务转发的udp包肯定可以到达对端的nat
|
%% client和stun之间必须有心跳机制保持nat映射可用,并且通过服务转发的udp包肯定可以到达对端的nat
|
||||||
logger:debug("[sdlan_network] broadcast data networkd_id: ~p, src_mac: ~p, dst_mac: ~p",
|
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)]),
|
[NetworkId, sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]),
|
||||||
@ -442,37 +350,95 @@ handle_cast({forward, _Sock, SrcMac, DstMac, _Packet}, State = #state{network_id
|
|||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
|
||||||
%% 删除ip的占用并关闭channel
|
%% 删除ip的占用并关闭channel
|
||||||
handle_cast({unregister, _ClientId, Mac}, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
|
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)]),
|
logger:debug("[sdlan_network] networkd_id: ~p, unregister Mac: ~p", [NetworkId, sdlan_util:format_mac(Mac)]),
|
||||||
case maps:take(Mac, UsedMap) of
|
{noreply, State#state{endpoints = maps:remove(Mac, Endpoints)}};
|
||||||
error ->
|
|
||||||
{noreply, State};
|
|
||||||
{#host{channel_pid = ChannelPid, monitor_ref = MRef}, NUsedMap} ->
|
|
||||||
is_reference(MRef) andalso demonitor(MRef),
|
|
||||||
sdlan_channel:stop(ChannelPid, normal),
|
|
||||||
{noreply, State#state{used_map = NUsedMap}}
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% 需要判断,client是属于当前网络的
|
%% 需要判断,client是属于当前网络的
|
||||||
handle_cast({update_hole, ClientId, Mac, Peer, NatType, V6Info}, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
|
%% 被拒绝的endpoint需要通知其重新验证
|
||||||
case {maps:find(Mac, UsedMap), client_model:get_client(NetworkId, ClientId)} of
|
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}},
|
||||||
{{ok, Host0 = #host{client_id = ClientId0, hole = OldHole}}, {ok, #client{ip = Ip}}} when ClientId =:= ClientId0 ->
|
State = #state{network_id = NetworkId, endpoints = Endpoints}) ->
|
||||||
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
|
|
||||||
}),
|
|
||||||
broadcast(?PACKET_EVENT_NAT_CHANGED, NatChangedEvent, Mac, UsedMap);
|
|
||||||
false ->
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
Host = Host0#host{hole = #hole{peer = Peer, nat_type = NatType}, v6_info = V6Info},
|
|
||||||
|
|
||||||
{noreply, State#state{used_map = maps:put(Mac, Host, UsedMap)}};
|
case maps:find(Mac, Endpoints) of
|
||||||
|
%% 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
|
||||||
|
true ?= not 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>>,
|
||||||
|
EndpointPeers = endpoint_peers([Mac], Endpoints),
|
||||||
|
logger:debug("[sdlan_network] hole changed, mac: ~p, ip: ~p, notify peers: ~p", [Mac, Ip, EndpointPeers]),
|
||||||
|
|
||||||
|
sdlan_stun_pool:send_packets(EndpointPeers, EventPacket)
|
||||||
|
end,
|
||||||
|
NEndpoint = Endpoint0#endpoint{hole = NHole, v6_info = V6Info, last_seen = erlang:monotonic_time(second)},
|
||||||
|
logger:debug("[sdlan_network] mac: ~p, ip: ~p, endpoint is: ~p", [Mac, Ip, NEndpoint]),
|
||||||
|
|
||||||
|
{noreply, State#state{endpoints = maps:put(Mac, NEndpoint, Endpoints)}};
|
||||||
_ ->
|
_ ->
|
||||||
|
%% 客户端需要重新校验
|
||||||
|
RefreshAuthEvent = sdlan_pb:encode_msg(#sdl_refresh_auth_event{
|
||||||
|
network_id = NetworkId
|
||||||
|
}),
|
||||||
|
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_REFRESH_AUTH, RefreshAuthEvent/binary>>,
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, EventPacket),
|
||||||
|
|
||||||
{noreply, State}
|
{noreply, State}
|
||||||
end.
|
end;
|
||||||
|
|
||||||
|
%% 处理权限逻辑
|
||||||
|
handle_cast({policy_request, Sock, {ClientIp, ClientPort}, #sdl_policy_request{client_id = ClientId, version = Version, mac = Mac, src_identity_id = SrcIdentityId, dst_identity_id = DstIdentityId, session_token = ST}},
|
||||||
|
State = #state{network_id = NetworkId, endpoints = Endpoints}) ->
|
||||||
|
logger:debug("[sdlan_network] policy_request, src_identity: ~p, dst_identity: ~p, version: ~p", [SrcIdentityId, DstIdentityId, Version]),
|
||||||
|
|
||||||
|
%% 分片逻辑,index必须从0开始
|
||||||
|
maybe
|
||||||
|
{ok, #endpoint{client_id = ClientId, session_token = ST}} ?= maps:find(Mac, Endpoints),
|
||||||
|
|
||||||
|
%% todo
|
||||||
|
Bin = <<1, 80:16, 2, 9090:16>>,
|
||||||
|
RulesBin = iolist_to_binary(lists:map(fun(_Id) -> Bin end, lists:seq(1, 1000))),
|
||||||
|
case byte_size(RulesBin) > 1200 of
|
||||||
|
true ->
|
||||||
|
%% 分组
|
||||||
|
Groups = chunk_rules(RulesBin, 1200),
|
||||||
|
TotalNum = length(Groups),
|
||||||
|
Fragments = lists:zip(lists:seq(0, TotalNum - 1), Groups),
|
||||||
|
[begin
|
||||||
|
FragmentPolicyResponse = sdlan_pb:encode_msg(#sdl_policy_response {
|
||||||
|
network_id = NetworkId,
|
||||||
|
src_identity_id = SrcIdentityId,
|
||||||
|
dst_identity_id = DstIdentityId,
|
||||||
|
version = Version,
|
||||||
|
total_num = TotalNum,
|
||||||
|
index = Idx,
|
||||||
|
rules = ChunkRulesBin
|
||||||
|
}),
|
||||||
|
FragmentPolicyResponsePkt = <<?PACKET_POLICY_RESPONSE, FragmentPolicyResponse/binary>>,
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, FragmentPolicyResponsePkt)
|
||||||
|
end || {Idx, ChunkRulesBin} <- Fragments];
|
||||||
|
false ->
|
||||||
|
%% 小于1200字节不分组
|
||||||
|
PolicyResponse = sdlan_pb:encode_msg(#sdl_policy_response {
|
||||||
|
network_id = NetworkId,
|
||||||
|
src_identity_id = SrcIdentityId,
|
||||||
|
dst_identity_id = DstIdentityId,
|
||||||
|
version = Version,
|
||||||
|
total_num = 1,
|
||||||
|
index = 0,
|
||||||
|
rules = RulesBin
|
||||||
|
}),
|
||||||
|
PolicyResponsePkt = <<?PACKET_POLICY_RESPONSE, PolicyResponse/binary>>,
|
||||||
|
logger:debug("[sdlan_network] will send policy response: ~p", [PolicyResponsePkt]),
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, PolicyResponsePkt)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc Handling all non call/cast messages
|
%% @doc Handling all non call/cast messages
|
||||||
@ -485,15 +451,33 @@ handle_info({timeout, _, flow_report_ticker}, State = #state{network_id = Networ
|
|||||||
catch sdlan_api:network_forward_report(NetworkId, ForwardBytes),
|
catch sdlan_api:network_forward_report(NetworkId, ForwardBytes),
|
||||||
{noreply, State#state{forward_bytes = 0}};
|
{noreply, State#state{forward_bytes = 0}};
|
||||||
|
|
||||||
handle_info({'EXIT', _Pid, shutdown}, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
|
handle_info({timeout, _, endpoint_gc}, State = #state{network_id = NetworkId, endpoints = Endpoints}) ->
|
||||||
logger:warning("[sdlan_network] network: ~p, get shutdown message", [NetworkId]),
|
Now = erlang:monotonic_time(second),
|
||||||
broadcast_shutdown(UsedMap),
|
{AliveEndpoints, ExpiredEndpoints} = maps:fold(fun(Mac, Ep = #endpoint{last_seen = Last}, {Alive, Expired}) ->
|
||||||
{stop, shutdown, State};
|
case Now - Last =< ?ENDPOINT_GC_INTERVAL of
|
||||||
%% Channel进程退出, hole里面的数据也需要清理
|
true ->
|
||||||
handle_info({'DOWN', _MRef, process, ChannelPid, Reason}, State = #state{network_id = NetworkId, used_map = UsedMap}) ->
|
{maps:put(Mac, Ep, Alive), Expired};
|
||||||
logger:notice("[sdlan_network] network_id: ~p, channel_pid: ~p, close with reason: ~p", [NetworkId, ChannelPid, Reason]),
|
false ->
|
||||||
NUsedMap = maps:filter(fun(_, #host{channel_pid = ChannelPid0}) -> ChannelPid =/= ChannelPid0 end, UsedMap),
|
{Alive, maps:put(Mac, Ep, Expired)}
|
||||||
{noreply, State#state{used_map = NUsedMap}}.
|
end
|
||||||
|
end,
|
||||||
|
{#{}, #{}}, Endpoints),
|
||||||
|
|
||||||
|
%% 通知活着的Endpoints
|
||||||
|
maybe
|
||||||
|
true ?= maps:size(ExpiredEndpoints) > 0,
|
||||||
|
DropMacsEvent = sdlan_pb:encode_msg(#sdl_drop_macs_event {
|
||||||
|
network_id = NetworkId,
|
||||||
|
macs = maps:keys(ExpiredEndpoints)
|
||||||
|
}),
|
||||||
|
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_DROP_MACS, DropMacsEvent/binary>>,
|
||||||
|
|
||||||
|
EndpointPeers = endpoint_peers([], AliveEndpoints),
|
||||||
|
sdlan_stun_pool:send_packets(EndpointPeers, EventPacket)
|
||||||
|
end,
|
||||||
|
erlang:start_timer(?ENDPOINT_GC_INTERVAL, self(), endpoint_gc),
|
||||||
|
|
||||||
|
{noreply, State#state{endpoints = AliveEndpoints}}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc This function is called by a gen_server when it is about to
|
%% @doc This function is called by a gen_server when it is about to
|
||||||
@ -501,10 +485,16 @@ handle_info({'DOWN', _MRef, process, ChannelPid, Reason}, State = #state{network
|
|||||||
%% necessary cleaning up. When it returns, the gen_server terminates
|
%% necessary cleaning up. When it returns, the gen_server terminates
|
||||||
%% with Reason. The return value is ignored.
|
%% with Reason. The return value is ignored.
|
||||||
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
|
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
|
||||||
State :: #state{}) -> term()).
|
State :: #state{}) -> no_return()).
|
||||||
terminate(Reason, #state{network_id = NetworkId, used_map = UsedMap}) ->
|
terminate(Reason, #state{network_id = NetworkId, endpoints = Endpoints}) ->
|
||||||
logger:debug("[sdlan_network] network: ~p, will terminate with reason: ~p", [NetworkId, Reason]),
|
logger:debug("[sdlan_network] network: ~p, will terminate with reason: ~p", [NetworkId, Reason]),
|
||||||
broadcast_shutdown(UsedMap),
|
|
||||||
|
NetworkShutdownEvent = sdlan_pb:encode_msg(#sdl_network_shutdown_event {
|
||||||
|
message = <<"Network shutdown">>
|
||||||
|
}),
|
||||||
|
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_NETWORK_SHUTDOWN, NetworkShutdownEvent/binary>>,
|
||||||
|
EndpointPeers = endpoint_peers([], Endpoints),
|
||||||
|
sdlan_stun_pool:send_packets(EndpointPeers, EventPacket),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
@ -519,55 +509,45 @@ code_change(_OldVsn, State = #state{}, _Extra) ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
%% 创建数据库表
|
-spec maybe_preload_hole(DstNatPeer :: {Ip :: inet:ip4_address(), Port :: integer()}, any()) -> no_return().
|
||||||
-spec create_mnesia_table(NetworkId :: integer()) -> no_return().
|
maybe_preload_hole(DstNatPeer, #endpoint{mac = SrcMac, hole = #hole{peer = {SrcNatIp, SrcNatPort}, nat_type = NatType}, v6_info = SrcV6Info}) ->
|
||||||
create_mnesia_table(NetworkId) when is_integer(NetworkId) ->
|
Event = sdlan_pb:encode_msg(#sdl_send_register_event {
|
||||||
Tab = client_model:get_table_name(NetworkId),
|
dst_mac = SrcMac,
|
||||||
Tables = mnesia:system_info(tables),
|
nat_ip = sdlan_ipaddr:ipv4_to_int(SrcNatIp),
|
||||||
case lists:member(Tab, Tables) of
|
nat_type = NatType,
|
||||||
true ->
|
nat_port = SrcNatPort,
|
||||||
ok;
|
v6_info = SrcV6Info
|
||||||
false ->
|
}),
|
||||||
Res = client_model:create_table(Tab),
|
EventPacket = <<?PACKET_EVENT, ?PACKET_EVENT_SEND_REGISTER, Event/binary>>,
|
||||||
logger:debug("[sdlan_network] create table result: ~p", [Res])
|
sdlan_stun_pool:send_packet(DstNatPeer, EventPacket);
|
||||||
end.
|
maybe_preload_hole(_, _) ->
|
||||||
|
|
||||||
-spec maybe_close_channel(undefined | #host{}) -> no_return().
|
|
||||||
maybe_close_channel(#host{channel_pid = ChannelPid0, monitor_ref = MRef0}) ->
|
|
||||||
case is_pid(ChannelPid0) andalso is_process_alive(ChannelPid0) of
|
|
||||||
true ->
|
|
||||||
is_reference(MRef0) andalso demonitor(MRef0),
|
|
||||||
sdlan_channel:stop(ChannelPid0, channel_rebind);
|
|
||||||
false ->
|
|
||||||
ok
|
|
||||||
end;
|
|
||||||
maybe_close_channel(_) ->
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec broadcast(EventType :: integer(), Event :: binary(), ExcludeMac :: binary(), UsedMap :: map()) -> no_return().
|
-spec limiting_check(ThrottleKey :: any()) -> pass | denied.
|
||||||
broadcast(EventType, Event, ExcludeMac, UsedMap) when is_map(UsedMap), is_binary(ExcludeMac), is_integer(EventType), is_binary(Event) ->
|
limiting_check(ThrottleKey) ->
|
||||||
maps:foreach(fun(Mac, #host{channel_pid = ChannelPid}) ->
|
case throttle:check(sdlan_network, ThrottleKey) of
|
||||||
case is_process_alive(ChannelPid) andalso ExcludeMac /= Mac of
|
{ok, _RestCount, _LeftToReset} ->
|
||||||
true ->
|
pass;
|
||||||
sdlan_channel:send_event(ChannelPid, EventType, Event);
|
{limit_exceeded, 0, _LeftToReset} ->
|
||||||
false ->
|
%% 尝试获取其他网络是否有让渡的资源
|
||||||
ok
|
case sdlan_network_coordinator:checkout() of
|
||||||
end
|
ok ->
|
||||||
end, UsedMap).
|
pass;
|
||||||
|
error ->
|
||||||
|
denied
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
broadcast_shutdown(UsedMap) when is_map(UsedMap) ->
|
-spec broadcast(Fun :: fun((#endpoint{}) -> no_return()), ExcludeMacs :: [binary()], Endpoints :: map()) -> no_return().
|
||||||
maps:foreach(fun(_, #host{channel_pid = ChannelPid}) ->
|
broadcast(Fun, ExcludeMacs, Endpoints) when is_function(Fun, 1), is_map(Endpoints), is_list(ExcludeMacs) ->
|
||||||
case is_process_alive(ChannelPid) of
|
maps:foreach(fun(Mac, Endpoint) ->
|
||||||
|
case lists:member(Mac, ExcludeMacs) of
|
||||||
true ->
|
true ->
|
||||||
NetworkShutdownEvent = sdlan_pb:encode_msg(#sdl_network_shutdown_event {
|
ok;
|
||||||
message = <<"Network shutdown">>
|
|
||||||
}),
|
|
||||||
sdlan_channel:send_event(ChannelPid, ?PACKET_EVENT_NETWORK_SHUTDOWN, NetworkShutdownEvent),
|
|
||||||
sdlan_channel:stop(ChannelPid, normal);
|
|
||||||
false ->
|
false ->
|
||||||
ok
|
Fun(Endpoint)
|
||||||
end
|
end
|
||||||
end, UsedMap).
|
end, Endpoints).
|
||||||
|
|
||||||
%% 解析IpAddr: <<"192.168.172/24">>
|
%% 解析IpAddr: <<"192.168.172/24">>
|
||||||
-spec parse_ipaddr(IpAddr0 :: binary()) -> {IpAddr :: binary(), MaskLen :: integer()}.
|
-spec parse_ipaddr(IpAddr0 :: binary()) -> {IpAddr :: binary(), MaskLen :: integer()}.
|
||||||
@ -580,18 +560,13 @@ parse_ipaddr(IpAddr0) when is_binary(IpAddr0) ->
|
|||||||
{IpAddr0, 24}
|
{IpAddr0, 24}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec format_host(Host :: #host{}, Ip :: integer(), Mac :: binary()) -> map().
|
-spec format_endpoint({Mac :: binary(), Host :: #endpoint{}}) -> map().
|
||||||
format_host(#host{client_id = ClientId, hole = Hole, v6_info = V6Info}, Ip, Mac) when is_integer(Ip), is_binary(Mac) ->
|
format_endpoint({Mac, #endpoint{client_id = ClientId, ip = Ip, hole = #hole{peer = {NatIp, NatPort}, nat_type = NatType}, v6_info = V6Info}}) ->
|
||||||
HoleMap = case Hole of
|
HoleMap = #{
|
||||||
undefined ->
|
nat_ip => NatIp,
|
||||||
#{};
|
nat_port => NatPort,
|
||||||
#hole{peer = {NatIp, NatPort}, nat_type = NatType} ->
|
nat_type => NatType
|
||||||
#{
|
},
|
||||||
nat_ip => NatIp,
|
|
||||||
nat_port => NatPort,
|
|
||||||
nat_type => NatType
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
|
|
||||||
V6InfoMap = case V6Info of
|
V6InfoMap = case V6Info of
|
||||||
undefined ->
|
undefined ->
|
||||||
@ -607,3 +582,50 @@ format_host(#host{client_id = ClientId, hole = Hole, v6_info = V6Info}, Ip, Mac)
|
|||||||
hole_map => HoleMap,
|
hole_map => HoleMap,
|
||||||
v6_info => V6InfoMap
|
v6_info => V6InfoMap
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
-spec search_endpoint(F :: fun((term(), term()) -> boolean()), Endpoints :: map()) -> error | {ok, Key :: any(), Val :: any()}.
|
||||||
|
search_endpoint(F, Endpoints) when is_function(F, 2), is_map(Endpoints) ->
|
||||||
|
search_endpoint0(F, maps:iterator(Endpoints)).
|
||||||
|
search_endpoint0(F, Iter) when is_function(F, 2) ->
|
||||||
|
case maps:next(Iter) of
|
||||||
|
{Key, Value, NextIter} ->
|
||||||
|
case F(Key, Value) of
|
||||||
|
true ->
|
||||||
|
{ok, Key, Value};
|
||||||
|
false ->
|
||||||
|
search_endpoint0(F, NextIter)
|
||||||
|
end;
|
||||||
|
'none' ->
|
||||||
|
error
|
||||||
|
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)).
|
||||||
|
|
||||||
|
-spec gen_session_token() -> binary().
|
||||||
|
gen_session_token() ->
|
||||||
|
Bytes = crypto:strong_rand_bytes(32),
|
||||||
|
base64:encode(Bytes).
|
||||||
|
|
||||||
|
%% 对rules进行分组
|
||||||
|
chunk_rules(<<>>, _) ->
|
||||||
|
[];
|
||||||
|
chunk_rules(Bin, Size) when byte_size(Bin) =< Size ->
|
||||||
|
[Bin];
|
||||||
|
chunk_rules(Bin, Size) ->
|
||||||
|
<<Head:Size/binary, Tail/binary>> = Bin,
|
||||||
|
[Head | chunk_rules(Tail, Size)].
|
||||||
@ -41,7 +41,7 @@ start_link() ->
|
|||||||
init([]) ->
|
init([]) ->
|
||||||
SupFlags = #{strategy => one_for_one, intensity => 1000, period => 3600},
|
SupFlags = #{strategy => one_for_one, intensity => 1000, period => 3600},
|
||||||
{ok, NetworkIds} = sdlan_api:get_all_networks(),
|
{ok, NetworkIds} = sdlan_api:get_all_networks(),
|
||||||
Specs = lists:map(fun child_spec/1, []),
|
Specs = lists:map(fun child_spec/1, NetworkIds),
|
||||||
|
|
||||||
set_network_bind(length(Specs)),
|
set_network_bind(length(Specs)),
|
||||||
|
|
||||||
@ -110,8 +110,6 @@ child_spec(Id) when is_integer(Id) ->
|
|||||||
modules => ['sdlan_network']
|
modules => ['sdlan_network']
|
||||||
}.
|
}.
|
||||||
|
|
||||||
set_network_bind(0) ->
|
|
||||||
ok;
|
|
||||||
set_network_bind(Count) when is_integer(Count) ->
|
set_network_bind(Count) when is_integer(Count) ->
|
||||||
{ok, BindWidth} = application:get_env(sdlan, band_width),
|
{ok, BindWidth} = application:get_env(sdlan, band_width),
|
||||||
NetworkBindWidth = BindWidth div Count,
|
NetworkBindWidth = BindWidth div Count,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
103
apps/sdlan/src/sdlan_register_worker.erl
Normal file
103
apps/sdlan/src/sdlan_register_worker.erl
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author anlicheng
|
||||||
|
%%% @copyright (C) 2026, <COMPANY>
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 23. 1月 2026 15:20
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(sdlan_register_worker).
|
||||||
|
-author("anlicheng").
|
||||||
|
-include("sdlan_pb.hrl").
|
||||||
|
-include("sdlan.hrl").
|
||||||
|
|
||||||
|
%% 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).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([start_link/4, do_register/4]).
|
||||||
|
|
||||||
|
-spec start_link(Sock :: inet:socket(), Ip :: inet:ip4_address(), Port :: integer(), Packet :: #sdl_register_super{}) -> {ok, pid()}.
|
||||||
|
start_link(Sock, Ip, Port, Packet) ->
|
||||||
|
{ok, erlang:spawn_link(?MODULE, do_register, [Sock, Ip, Port, Packet])}.
|
||||||
|
|
||||||
|
do_register(Sock, ClientIp, ClientPort, #sdl_register_super{
|
||||||
|
pkt_id = PktId, client_id = ClientId, network_id = NetworkId, mac = Mac, ip = Ip, mask_len = MaskLen,
|
||||||
|
hostname = HostName, pub_key = PubKey, access_token = AccessToken}) ->
|
||||||
|
|
||||||
|
true = (Mac =/= <<>> andalso PubKey =/= <<>> andalso ClientId =/= <<>>),
|
||||||
|
%% Mac地址不能是广播地址
|
||||||
|
true = not (sdlan_util:is_multicast_mac(Mac) orelse sdlan_util:is_broadcast_mac(Mac)),
|
||||||
|
|
||||||
|
MacBinStr = sdlan_util:format_mac(Mac),
|
||||||
|
IpAddr = sdlan_ipaddr:int_to_ipv4(Ip),
|
||||||
|
Params = #{
|
||||||
|
<<"network_id">> => NetworkId,
|
||||||
|
<<"client_id">> => ClientId,
|
||||||
|
<<"mac">> => MacBinStr,
|
||||||
|
<<"ip">> => IpAddr,
|
||||||
|
<<"mask_len">> => MaskLen,
|
||||||
|
<<"hostname">> => HostName,
|
||||||
|
<<"access_token">> => AccessToken
|
||||||
|
},
|
||||||
|
%% 参数检查
|
||||||
|
logger:debug("[sdlan_register_worker] client_id: ~p, ip: ~p, mac: ~p, host_name: ~p, access_token: ~p, network_id: ~p",
|
||||||
|
[ClientId, Ip, Mac, HostName, AccessToken, NetworkId]),
|
||||||
|
|
||||||
|
case sdlan_api:auth_access_token(Params) of
|
||||||
|
{ok, #{<<"result">> := <<"ok">>}} ->
|
||||||
|
%% 建立到network的对应关系
|
||||||
|
case sdlan_network:get_pid(NetworkId) of
|
||||||
|
NetworkPid when is_pid(NetworkPid) ->
|
||||||
|
{ok, AesKey, SessionToken} = sdlan_network:attach(NetworkPid, {ClientIp, ClientPort}, ClientId, Mac, Ip, HostName),
|
||||||
|
RsaPubKey = sdlan_cipher:rsa_pem_decode(PubKey),
|
||||||
|
EncodedAesKey = rsa_encode(AesKey, RsaPubKey),
|
||||||
|
|
||||||
|
RegisterSuperAck = sdlan_pb:encode_msg(#sdl_register_super_ack {
|
||||||
|
pkt_id = PktId,
|
||||||
|
aes_key = EncodedAesKey,
|
||||||
|
session_token = SessionToken
|
||||||
|
}),
|
||||||
|
|
||||||
|
%% 发送确认信息
|
||||||
|
Reply = <<?PACKET_REGISTER_SUPER_ACK, RegisterSuperAck/binary>>,
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, Reply),
|
||||||
|
|
||||||
|
%% 设置节点的在线状态
|
||||||
|
Result = sdlan_api:node_online(ClientId, NetworkId, sdlan_ipaddr:int_to_ipv4(Ip)),
|
||||||
|
logger:debug("[sdlan_register_worker] client_id: ~p, set none online result is: ~p", [ClientId, Result]);
|
||||||
|
undefined ->
|
||||||
|
logger:warning("[sdlan_register_worker] client_id: ~p, register get error: network not found", [ClientId]),
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, register_nak_reply(PktId, ?NAK_INTERNAL_FAULT, <<"Internal Error">>))
|
||||||
|
end;
|
||||||
|
{ok, #{<<"error">> := #{<<"code">> := Code, <<"message">> := Message}}} ->
|
||||||
|
logger:warning("[sdlan_register_worker] network_id: ~p, client_id: ~p, register get error: ~ts, error_code: ~p", [NetworkId, ClientId, Message, Code]),
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, register_nak_reply(PktId, Code, Message));
|
||||||
|
{error, Reason} ->
|
||||||
|
logger:warning("[sdlan_register_worker] network_id: ~p, client_id: ~p, register get error: ~p", [NetworkId, ClientId, Reason]),
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, register_nak_reply(PktId, ?NAK_NETWORK_FAULT, <<"Network Error">>))
|
||||||
|
end,
|
||||||
|
exit(normal).
|
||||||
|
|
||||||
|
-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 {
|
||||||
|
pkt_id = PacketId,
|
||||||
|
error_code = ErrorCode,
|
||||||
|
error_message = ErrorMsg
|
||||||
|
}),
|
||||||
|
<<?PACKET_REGISTER_SUPER_NAK, RegisterNakReply/binary>>.
|
||||||
|
|
||||||
|
-spec rsa_encode(PlainText :: binary(), RsaPubKey :: public_key:rsa_public_key()) -> binary().
|
||||||
|
rsa_encode(PlainText, RsaPubKey) when is_binary(PlainText) ->
|
||||||
|
iolist_to_binary(sdlan_cipher:rsa_encrypt(PlainText, RsaPubKey)).
|
||||||
66
apps/sdlan/src/sdlan_register_worker_sup.erl
Normal file
66
apps/sdlan/src/sdlan_register_worker_sup.erl
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author anlicheng
|
||||||
|
%%% @copyright (C) 2026, <COMPANY>
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 23. 1月 2026 15:19
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(sdlan_register_worker_sup).
|
||||||
|
-author("anlicheng").
|
||||||
|
-include("sdlan_pb.hrl").
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([start_link/0]).
|
||||||
|
-export([start_worker/4]).
|
||||||
|
|
||||||
|
%% Supervisor callbacks
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API functions
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
%% @doc Starts the supervisor
|
||||||
|
-spec(start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
||||||
|
start_link() ->
|
||||||
|
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Supervisor callbacks
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
%% @private
|
||||||
|
%% @doc Whenever a supervisor is started using supervisor:start_link/[2,3],
|
||||||
|
%% this function is called by the new process to find out about
|
||||||
|
%% restart strategy, maximum restart frequency and child
|
||||||
|
%% specifications.
|
||||||
|
-spec(init(Args :: term()) ->
|
||||||
|
{ok, {SupFlags :: supervisor:sup_flags(), [ChildSpec :: supervisor:child_spec()]}}
|
||||||
|
| ignore | {error, Reason :: term()}).
|
||||||
|
init([]) ->
|
||||||
|
SupFlags = #{strategy => simple_one_for_one, intensity => 1000, period => 3600},
|
||||||
|
|
||||||
|
ChildSpec = #{
|
||||||
|
id => sdlan_register_worker,
|
||||||
|
start => {sdlan_register_worker, start_link, []},
|
||||||
|
restart => temporary,
|
||||||
|
shutdown => 2000,
|
||||||
|
type => worker,
|
||||||
|
modules => [sdlan_register_worker]
|
||||||
|
},
|
||||||
|
|
||||||
|
{ok, {SupFlags, [ChildSpec]}}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
-spec start_worker(Sock :: inet:socket(), Ip :: inet:ip4_address(), Port :: integer(), Packet :: #sdl_register_super{}) -> supervisor:startchild_ret().
|
||||||
|
start_worker(Sock, Ip, Port, Packet) ->
|
||||||
|
%% Start a temporary worker under the supervisor
|
||||||
|
supervisor:start_child(?SERVER, [Sock, Ip, Port, Packet]).
|
||||||
@ -7,6 +7,8 @@
|
|||||||
%%% Created : 09. 4月 2024 17:37
|
%%% Created : 09. 4月 2024 17:37
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(sdlan_stun).
|
-module(sdlan_stun).
|
||||||
|
-feature(maybe_expr, enable).
|
||||||
|
|
||||||
-author("anlicheng").
|
-author("anlicheng").
|
||||||
-include("sdlan.hrl").
|
-include("sdlan.hrl").
|
||||||
-include("sdlan_pb.hrl").
|
-include("sdlan_pb.hrl").
|
||||||
@ -14,8 +16,8 @@
|
|||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/2]).
|
-export([start_link/1]).
|
||||||
-export([get_name/1]).
|
-export([send_packets/3]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
||||||
@ -23,22 +25,22 @@
|
|||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
socket
|
socket :: inet:socket()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
-spec get_name(Id :: integer()) -> atom().
|
-spec send_packets(Pid :: pid(), Peers :: [{Ip :: inet:ip4_address(), Port :: integer()}], Packet :: binary()) -> no_return().
|
||||||
get_name(Id) when is_integer(Id) ->
|
send_packets(Pid, Peers, Packet) when is_pid(Pid), is_list(Peers), is_binary(Packet) ->
|
||||||
list_to_atom("sdlan_stun:" ++ integer_to_list(Id)).
|
gen_server:cast(Pid, {send_packets, Peers, Packet}).
|
||||||
|
|
||||||
%% @doc Spawns the server and registers the local name (unique)
|
%% @doc Spawns the server and registers the local name (unique)
|
||||||
-spec(start_link(Name :: atom(), Port :: integer()) ->
|
-spec(start_link(Port :: integer()) ->
|
||||||
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
||||||
start_link(Name, Port) when is_atom(Name), is_integer(Port) ->
|
start_link(Port) ->
|
||||||
gen_server:start_link({local, Name}, ?MODULE, [Port], []).
|
gen_server:start_link(?MODULE, [Port], []).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
@ -50,8 +52,6 @@ start_link(Name, Port) when is_atom(Name), is_integer(Port) ->
|
|||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term()} | ignore).
|
{stop, Reason :: term()} | ignore).
|
||||||
init([Port]) ->
|
init([Port]) ->
|
||||||
%% 需要提高进程的调度优先级
|
|
||||||
erlang:process_flag(priority, high),
|
|
||||||
Opts = [
|
Opts = [
|
||||||
binary,
|
binary,
|
||||||
{reuseaddr, true},
|
{reuseaddr, true},
|
||||||
@ -60,10 +60,11 @@ init([Port]) ->
|
|||||||
{recbuf, 5 * 1024 * 1024},
|
{recbuf, 5 * 1024 * 1024},
|
||||||
{sndbuf, 5 * 1024 * 1024}
|
{sndbuf, 5 * 1024 * 1024}
|
||||||
],
|
],
|
||||||
{ok, Socket} = gen_udp:open(Port, Opts),
|
|
||||||
inet_udp:controlling_process(Socket, self()),
|
|
||||||
|
|
||||||
logger:debug("[sdlan_stun] start at port: ~p", [Port]),
|
{ok, Socket} = gen_udp:open(Port, Opts),
|
||||||
|
%% 需要提高进程的调度优先级
|
||||||
|
erlang:process_flag(priority, high),
|
||||||
|
inet_udp:controlling_process(Socket, self()),
|
||||||
{ok, #state{socket = Socket}}.
|
{ok, #state{socket = Socket}}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
@ -85,7 +86,10 @@ handle_call(_Request, _From, State = #state{}) ->
|
|||||||
{noreply, NewState :: #state{}} |
|
{noreply, NewState :: #state{}} |
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
{stop, Reason :: term(), NewState :: #state{}}).
|
||||||
|
%% 转发数据
|
||||||
|
handle_cast({send_packets, Peers, Packet}, State = #state{socket = Sock}) ->
|
||||||
|
lists:foreach(fun({Ip, Port}) -> gen_udp:send(Sock, Ip, Port, Packet) end, Peers),
|
||||||
|
{noreply, State};
|
||||||
%% 当前node下的转发,基于进程间的通讯
|
%% 当前node下的转发,基于进程间的通讯
|
||||||
handle_cast({stun_relay, Ip, Port, Reply}, State = #state{socket = Sock}) ->
|
handle_cast({stun_relay, Ip, Port, Reply}, State = #state{socket = Sock}) ->
|
||||||
ok = gen_udp:send(Sock, Ip, Port, Reply),
|
ok = gen_udp:send(Sock, Ip, Port, Reply),
|
||||||
@ -97,73 +101,13 @@ handle_cast({stun_relay, Ip, Port, Reply}, State = #state{socket = Sock}) ->
|
|||||||
{noreply, NewState :: #state{}} |
|
{noreply, NewState :: #state{}} |
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
{stop, Reason :: term(), NewState :: #state{}}).
|
||||||
|
handle_info({udp, Sock, PeerIp, PeerPort, Packet}, State = #state{socket = Sock}) ->
|
||||||
handle_info({udp, Sock, Ip, Port, <<?PACKET_STUN_REQUEST:8, Body/binary>>}, State = #state{socket = Sock}) ->
|
handle_packet(Sock, PeerIp, PeerPort, Packet),
|
||||||
#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]),
|
|
||||||
{noreply, State};
|
|
||||||
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, <<?PACKET_STUN_REPLY, StunReply/binary>>),
|
|
||||||
logger:debug("[sdlan_stun] stun_request network_id: ~p, client_id: ~p, hole: ~p", [NetworkId, ClientId, {Ip, Port}]),
|
|
||||||
{noreply, State}
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% 网络nat类型的探测机制, 需要借助其他服务一起才能实现
|
|
||||||
%% 辅助节点没有assist的配置,不支持attr = 2的探测
|
|
||||||
handle_info({udp, Sock, ClientIp, ClientPort, <<?PACKET_STUN_PROBE:8, Body/binary>>}, State = #state{socket = Sock}) ->
|
|
||||||
#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]),
|
|
||||||
|
|
||||||
ProbeReplyPkt = sdlan_pb:encode_msg(#sdl_stun_probe_reply {
|
|
||||||
cookie = Cookie,
|
|
||||||
port = ClientPort,
|
|
||||||
ip = int_ip(ClientIp)
|
|
||||||
}),
|
|
||||||
|
|
||||||
case Attr of
|
|
||||||
?STUN_ATTR_CHANGE_NONE ->
|
|
||||||
ok = gen_udp:send(Sock, ClientIp, ClientPort, <<?PACKET_STUN_PROBE_REPLY, ProbeReplyPkt/binary>>);
|
|
||||||
?STUN_ATTR_CHANGE_PORT ->
|
|
||||||
%% 切换端口和ip
|
|
||||||
sdlan_stun_peer_assist:stun_relay(ClientIp, ClientPort, ProbeReplyPkt);
|
|
||||||
?STUN_ATTR_CHANGE_PEER ->
|
|
||||||
%% 切换端口返回
|
|
||||||
sdlan_stun_port_assist:stun_relay(ClientIp, ClientPort, ProbeReplyPkt)
|
|
||||||
end,
|
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
handle_info({udp_error , Sock, Reason}, State = #state{socket = Sock}) ->
|
||||||
%% 转发消息, 跨服务器的stun_reply的转发通过socket来转发
|
{stop, Reason, State};
|
||||||
handle_info({udp, Sock, _, _, <<?PACKET_STUN_PROBE_RELAY:8, Ip0, Ip1, Ip2, Ip3, Port:16, Reply/binary>>}, State = #state{socket = Sock}) ->
|
|
||||||
logger:debug("[sdlan_stun] get stun_probe_replay request, reply: ~p", [Reply]),
|
|
||||||
gen_udp:send(Sock, {Ip0, Ip1, Ip2, Ip3}, Port, Reply),
|
|
||||||
{noreply, State};
|
|
||||||
|
|
||||||
handle_info({udp, _, _Ip, _Port, <<?PACKET_STUN_DATA, Body/binary>>}, State = #state{socket = 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)]),
|
|
||||||
|
|
||||||
%% 重新打包数据ttl需要减1
|
|
||||||
case sdlan_network:get_pid(NetworkId) of
|
|
||||||
NetworkPid when is_pid(NetworkPid) ->
|
|
||||||
NData = sdlan_pb:encode_msg(Data#sdl_data{ttl = TTL - 1, is_p2p = false}),
|
|
||||||
sdlan_network:forward(NetworkPid, Sock, SrcMac, DstMac, <<?PACKET_STUN_DATA, NData/binary>>);
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
|
|
||||||
{noreply, State};
|
|
||||||
|
|
||||||
handle_info(Info, State) ->
|
handle_info(Info, State) ->
|
||||||
logger:error("[sdlan_stun] get a unknown message: ~p, channel will closed", [Info]),
|
logger:error("[sdlan_stun] get a unknown message?: ~p, channel will closed, state: ~p", [Info, State]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
@ -188,6 +132,103 @@ code_change(_OldVsn, State = #state{}, _Extra) ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
|
-spec handle_packet(Sock :: inet:socket(), Ip :: inet:ip4_address(), Port :: integer(), Packet :: binary()) -> no_return().
|
||||||
|
%% 带上token或者网络id来注册
|
||||||
|
handle_packet(Sock, Ip, Port, <<?PACKET_REGISTER_SUPER, Body/binary>>) ->
|
||||||
|
maybe
|
||||||
|
Register = catch sdlan_pb:decode_msg(Body, sdl_register_super),
|
||||||
|
#sdl_register_super{} ?= Register,
|
||||||
|
sdlan_register_worker_sup:start_worker(Sock, Ip, Port, Register)
|
||||||
|
end;
|
||||||
|
|
||||||
|
%% 查询信息
|
||||||
|
handle_packet(Sock, ClientIp, ClientPort, <<?PACKET_QUERY_INFO, Body/binary>>) ->
|
||||||
|
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}, Query)
|
||||||
|
end;
|
||||||
|
|
||||||
|
%% 处理心跳逻辑
|
||||||
|
handle_packet(Sock, ClientIp, ClientPort, <<?PACKET_STUN_REQUEST:8, Body/binary>>) ->
|
||||||
|
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;
|
||||||
|
|
||||||
|
%% 处理网络的权限请求
|
||||||
|
handle_packet(Sock, ClientIp, ClientPort, <<?PACKET_POLICY_REQUEST:8, Body/binary>>) ->
|
||||||
|
maybe
|
||||||
|
PolicyRequest = catch sdlan_pb:decode_msg(Body, sdl_policy_request),
|
||||||
|
#sdl_policy_request{network_id = NetworkId} ?= PolicyRequest,
|
||||||
|
logger:debug("[sdlan_stun] get policy request, network_id: ~p", [NetworkId]),
|
||||||
|
{ok, NetworkPid} ?= sdlan_network:lookup_pid(NetworkId),
|
||||||
|
sdlan_network:policy_request(NetworkPid, Sock, {ClientIp, ClientPort}, PolicyRequest)
|
||||||
|
end;
|
||||||
|
|
||||||
|
%% 网络nat类型的探测机制, 需要借助其他服务一起才能实现
|
||||||
|
%% 辅助节点没有assist的配置,不支持attr = 2的探测
|
||||||
|
handle_packet(Sock, Ip, Port, <<?PACKET_STUN_PROBE:8, Body/binary>>) ->
|
||||||
|
maybe
|
||||||
|
#sdl_stun_probe{cookie = Cookie, step = Step, attr = Attr} ?= sdlan_pb:decode_msg(Body, sdl_stun_probe),
|
||||||
|
logger:debug("[sdlan_stun] get stun_probe request, cookie: ~p", [Cookie]),
|
||||||
|
|
||||||
|
ProbeReplyPacket = sdlan_pb:encode_msg(#sdl_stun_probe_reply {
|
||||||
|
cookie = Cookie,
|
||||||
|
ip = int_ip(Ip),
|
||||||
|
port = Port,
|
||||||
|
step = Step
|
||||||
|
}),
|
||||||
|
|
||||||
|
case Attr of
|
||||||
|
?STUN_ATTR_CHANGE_NONE ->
|
||||||
|
%% 什么不都改变
|
||||||
|
gen_udp:send(Sock, Ip, Port, <<?PACKET_STUN_PROBE_REPLY, ProbeReplyPacket/binary>>);
|
||||||
|
?STUN_ATTR_CHANGE_PEER ->
|
||||||
|
%% 切换端口和ip
|
||||||
|
sdlan_stun_peer_assist:stun_relay(Ip, Port, ProbeReplyPacket);
|
||||||
|
?STUN_ATTR_CHANGE_PORT ->
|
||||||
|
%% 切换端口返回
|
||||||
|
sdlan_stun_port_assist:stun_relay(Ip, Port, ProbeReplyPacket)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
|
||||||
|
%% 转发消息, 跨服务器的stun_reply的转发通过socket来转发
|
||||||
|
handle_packet(Sock, _, _, <<?PACKET_STUN_PROBE_RELAY:8, Ip0, Ip1, Ip2, Ip3, Port:16, Reply/binary>>) ->
|
||||||
|
logger:debug("[sdlan_stun] get stun_probe_replay request, reply: ~p", [Reply]),
|
||||||
|
gen_udp:send(Sock, {Ip0, Ip1, Ip2, Ip3}, Port, Reply);
|
||||||
|
|
||||||
|
handle_packet(Sock, _Ip, _Port, <<?PACKET_STUN_DATA, Body/binary>>) ->
|
||||||
|
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, <<?PACKET_STUN_DATA, NData/binary>>)
|
||||||
|
end;
|
||||||
|
|
||||||
|
%% 执行arp查询
|
||||||
|
handle_packet(Sock, ClientIp, ClientPort, <<?PACKET_ARP_REQUEST, Body/binary>>) ->
|
||||||
|
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
|
||||||
|
}),
|
||||||
|
gen_udp:send(Sock, ClientIp, ClientPort, ArpResponse)
|
||||||
|
end.
|
||||||
|
|
||||||
-spec int_ip(tuple()) -> integer().
|
-spec int_ip(tuple()) -> integer().
|
||||||
int_ip({Ip0, Ip1, Ip2, Ip3}) ->
|
int_ip({Ip0, Ip1, Ip2, Ip3}) ->
|
||||||
<<Ip:32>> = <<Ip0, Ip1, Ip2, Ip3>>,
|
<<Ip:32>> = <<Ip0, Ip1, Ip2, Ip3>>,
|
||||||
|
|||||||
@ -4,15 +4,19 @@
|
|||||||
%%% @doc
|
%%% @doc
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%% Created : 29. 3月 2024 14:32
|
%%% Created : 09. 4月 2024 17:37
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(sdlan_tcp_client).
|
-module(sdlan_stun_pool).
|
||||||
|
-feature(maybe_expr, enable).
|
||||||
|
|
||||||
-author("anlicheng").
|
-author("anlicheng").
|
||||||
|
-include("sdlan.hrl").
|
||||||
|
-include("sdlan_pb.hrl").
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0]).
|
-export([start_link/0, send_packets/2, send_packet/2]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
||||||
@ -20,13 +24,23 @@
|
|||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
socket
|
workers :: tuple(),
|
||||||
|
idx = 1,
|
||||||
|
num = 0
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
|
-spec send_packet(Peer :: {Ip :: inet:ip4_address(), Port :: integer()}, Packet :: binary()) -> no_return().
|
||||||
|
send_packet(Peer, Packet) ->
|
||||||
|
gen_server:cast(?SERVER, {send_packets, [Peer], Packet}).
|
||||||
|
|
||||||
|
-spec send_packets(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)
|
%% @doc Spawns the server and registers the local name (unique)
|
||||||
-spec(start_link() ->
|
-spec(start_link() ->
|
||||||
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
||||||
@ -43,9 +57,15 @@ start_link() ->
|
|||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term()} | ignore).
|
{stop, Reason :: term()} | ignore).
|
||||||
init([]) ->
|
init([]) ->
|
||||||
{ok, Socket} = gen_tcp:connect("localhost", 18083, [binary, {packet, 2}, {active, true}]),
|
{ok, StunServerProps} = application:get_env(sdlan, stun_servers),
|
||||||
ok = gen_tcp:send(Socket, <<"hello world">>),
|
Port = proplists:get_value(port, StunServerProps),
|
||||||
{ok, #state{socket = Socket}}.
|
AcceptorNums = proplists:get_value(acceptor_nums, StunServerProps),
|
||||||
|
|
||||||
|
Workers = lists:map(fun(_Id) ->
|
||||||
|
{ok, Pid} = sdlan_stun:start_link(Port),
|
||||||
|
Pid
|
||||||
|
end, lists:seq(1, AcceptorNums)),
|
||||||
|
{ok, #state{workers = list_to_tuple(Workers), idx = 1, num = length(Workers)}}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc Handling call messages
|
%% @doc Handling call messages
|
||||||
@ -66,7 +86,13 @@ handle_call(_Request, _From, State = #state{}) ->
|
|||||||
{noreply, NewState :: #state{}} |
|
{noreply, NewState :: #state{}} |
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
{stop, Reason :: term(), NewState :: #state{}}).
|
||||||
handle_cast(_Request, State = #state{}) ->
|
%% 当前node下的转发,基于进程间的通讯
|
||||||
|
handle_cast({send_packets, Peers, Packet}, State = #state{workers = Workers, idx = Idx, num = Num}) ->
|
||||||
|
WorkerPid = element(Idx, Workers),
|
||||||
|
sdlan_stun:send_packets(WorkerPid, Peers, Packet),
|
||||||
|
NewIdx = (Idx rem Num) + 1,
|
||||||
|
{noreply, State#state{idx = NewIdx}};
|
||||||
|
handle_cast(_Request, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
@ -75,7 +101,8 @@ handle_cast(_Request, State = #state{}) ->
|
|||||||
{noreply, NewState :: #state{}} |
|
{noreply, NewState :: #state{}} |
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
{stop, Reason :: term(), NewState :: #state{}}).
|
||||||
handle_info(_Info, State = #state{}) ->
|
handle_info(Info, State) ->
|
||||||
|
logger:error("[sdlan_stun] get a unknown message:xx ~p, channel will closed", [Info]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
@ -98,4 +125,4 @@ code_change(_OldVsn, State = #state{}, _Extra) ->
|
|||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -27,6 +27,15 @@ start_link() ->
|
|||||||
init([]) ->
|
init([]) ->
|
||||||
SupFlags = #{strategy => one_for_one, intensity => 1000, period => 3600},
|
SupFlags = #{strategy => one_for_one, intensity => 1000, period => 3600},
|
||||||
Specs = [
|
Specs = [
|
||||||
|
#{
|
||||||
|
id => sdlan_stun_pool,
|
||||||
|
start => {sdlan_stun_pool, start_link, []},
|
||||||
|
restart => permanent,
|
||||||
|
shutdown => 2000,
|
||||||
|
type => worker,
|
||||||
|
modules => ['sdlan_stun_pool']
|
||||||
|
},
|
||||||
|
|
||||||
#{
|
#{
|
||||||
id => sdlan_stun_port_assist,
|
id => sdlan_stun_port_assist,
|
||||||
start => {sdlan_stun_port_assist, start_link, []},
|
start => {sdlan_stun_port_assist, start_link, []},
|
||||||
@ -45,21 +54,4 @@ init([]) ->
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
{ok, {SupFlags, Specs ++ stun_acceptors()}}.
|
{ok, {SupFlags, Specs}}.
|
||||||
|
|
||||||
stun_acceptors() ->
|
|
||||||
{ok, StunServers} = application:get_env(sdlan, stun_servers),
|
|
||||||
Port = proplists:get_value(port, StunServers),
|
|
||||||
AcceptorNums = proplists:get_value(acceptor_nums, StunServers),
|
|
||||||
|
|
||||||
lists:map(fun(Id) ->
|
|
||||||
Name = sdlan_stun:get_name(Id),
|
|
||||||
#{
|
|
||||||
id => Name,
|
|
||||||
start => {sdlan_stun, start_link, [Name, Port]},
|
|
||||||
restart => permanent,
|
|
||||||
shutdown => 2000,
|
|
||||||
type => worker,
|
|
||||||
modules => ['sdlan_stun']
|
|
||||||
}
|
|
||||||
end, lists:seq(1, AcceptorNums)).
|
|
||||||
@ -45,6 +45,7 @@ init([]) ->
|
|||||||
type => worker,
|
type => worker,
|
||||||
modules => ['sdlan_network_coordinator']
|
modules => ['sdlan_network_coordinator']
|
||||||
},
|
},
|
||||||
|
|
||||||
#{
|
#{
|
||||||
id => sdlan_network_sup,
|
id => sdlan_network_sup,
|
||||||
start => {sdlan_network_sup, start_link, []},
|
start => {sdlan_network_sup, start_link, []},
|
||||||
@ -54,6 +55,15 @@ init([]) ->
|
|||||||
modules => ['sdlan_network_sup']
|
modules => ['sdlan_network_sup']
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#{
|
||||||
|
id => sdlan_register_worker_sup,
|
||||||
|
start => {sdlan_register_worker_sup, start_link, []},
|
||||||
|
restart => permanent,
|
||||||
|
shutdown => 2000,
|
||||||
|
type => supervisor,
|
||||||
|
modules => ['sdlan_register_worker_sup']
|
||||||
|
},
|
||||||
|
|
||||||
#{
|
#{
|
||||||
id => sdlan_stun_sup,
|
id => sdlan_stun_sup,
|
||||||
start => {sdlan_stun_sup, start_link, []},
|
start => {sdlan_stun_sup, start_link, []},
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author anlicheng
|
|
||||||
%%% @copyright (C) 2024, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 09. 3月 2024 15:25
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(sdlan_test).
|
|
||||||
-author("anlicheng").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([test/1]).
|
|
||||||
|
|
||||||
test(X) when X band 1 == 0 ->
|
|
||||||
ok;
|
|
||||||
test(_) ->
|
|
||||||
error.
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author anlicheng
|
|
||||||
%%% @copyright (C) 2026, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 11. 2月 2026 15:49
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(sdlan_quicer_test).
|
|
||||||
-author("anlicheng").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([test/0]).
|
|
||||||
|
|
||||||
test() ->
|
|
||||||
{ok, Conn} = quicer:connect("http3.is", 443, [{alpn, ["h3"]},
|
|
||||||
{verify, verify_peer},
|
|
||||||
{peer_unidi_stream_count, 3}], 5000),
|
|
||||||
logger:debug("conn is: ~p", [Conn]),
|
|
||||||
quicer:shutdown_connection(Conn).
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author anlicheng
|
|
||||||
%%% @copyright (C) 2024, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 17. 4月 2024 10:35
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(sdlan_udp_downloader).
|
|
||||||
-author("anlicheng").
|
|
||||||
|
|
||||||
-behaviour(gen_server).
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([start_link/0]).
|
|
||||||
|
|
||||||
%% gen_server callbacks
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
|
|
||||||
code_change/3]).
|
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
|
||||||
|
|
||||||
-record(state, {
|
|
||||||
socket
|
|
||||||
}).
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% API
|
|
||||||
%%%===================================================================
|
|
||||||
|
|
||||||
%% @doc Spawns the server and registers the local name (unique)
|
|
||||||
-spec(start_link() ->
|
|
||||||
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
|
||||||
start_link() ->
|
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% gen_server callbacks
|
|
||||||
%%%===================================================================
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Initializes the server
|
|
||||||
-spec(init(Args :: term()) ->
|
|
||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term()} | ignore).
|
|
||||||
init([]) ->
|
|
||||||
{ok, Socket} = gen_udp:open(22222, [binary]),
|
|
||||||
{ok, #state{socket = Socket}}.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Handling call messages
|
|
||||||
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
|
|
||||||
State :: #state{}) ->
|
|
||||||
{reply, Reply :: term(), NewState :: #state{}} |
|
|
||||||
{reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
|
||||||
handle_call(_Request, _From, State) ->
|
|
||||||
{reply, ok, State}.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Handling cast messages
|
|
||||||
-spec(handle_cast(Request :: term(), State :: #state{}) ->
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
|
||||||
handle_cast(_Info, State) ->
|
|
||||||
{noreply, State}.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Handling all non call/cast messages
|
|
||||||
-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
|
||||||
handle_info({udp, Sock, Ip, Port, <<1>>}, State = #state{socket = Sock}) ->
|
|
||||||
{ok, Content} = file:read_file("/tmp/files/test.dmg"),
|
|
||||||
send_file_content(Sock, Ip, Port, 1200, Content),
|
|
||||||
{noreply, State#state{}}.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc This function is called by a gen_server when it is about to
|
|
||||||
%% terminate. It should be the opposite of Module:init/1 and do any
|
|
||||||
%% necessary cleaning up. When it returns, the gen_server terminates
|
|
||||||
%% with Reason. The return value is ignored.
|
|
||||||
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
|
|
||||||
State :: #state{}) -> term()).
|
|
||||||
terminate(_Reason, _State = #state{}) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Convert process state when code is changed
|
|
||||||
-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
|
|
||||||
Extra :: term()) ->
|
|
||||||
{ok, NewState :: #state{}} | {error, Reason :: term()}).
|
|
||||||
code_change(_OldVsn, State = #state{}, _Extra) ->
|
|
||||||
{ok, State}.
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% Internal functions
|
|
||||||
%%%===================================================================
|
|
||||||
|
|
||||||
send_file_content(Sock, Ip, Port, Size, Content) when byte_size(Content) =< Size ->
|
|
||||||
gen_udp:send(Sock, Ip, Port, Content);
|
|
||||||
send_file_content(Sock, Ip, Port, Size, Content) ->
|
|
||||||
<<Part:Size/binary, Rest/binary>> = Content,
|
|
||||||
gen_udp:send(Sock, Ip, Port, Part),
|
|
||||||
send_file_content(Sock, Ip, Port, Size, Rest).
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author anlicheng
|
|
||||||
%%% @copyright (C) 2024, <COMPANY>
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 17. 4月 2024 10:35
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(sdlan_udp_wget).
|
|
||||||
-author("anlicheng").
|
|
||||||
|
|
||||||
-behaviour(gen_server).
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([start_link/0]).
|
|
||||||
-export([wget/0]).
|
|
||||||
|
|
||||||
%% gen_server callbacks
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
|
||||||
|
|
||||||
-record(state, {
|
|
||||||
socket,
|
|
||||||
bytes = 0
|
|
||||||
}).
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% API
|
|
||||||
%%%===================================================================
|
|
||||||
wget() ->
|
|
||||||
gen_server:call(?MODULE, wget).
|
|
||||||
|
|
||||||
%% @doc Spawns the server and registers the local name (unique)
|
|
||||||
-spec(start_link() ->
|
|
||||||
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
|
|
||||||
start_link() ->
|
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% gen_server callbacks
|
|
||||||
%%%===================================================================
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Initializes the server
|
|
||||||
-spec(init(Args :: term()) ->
|
|
||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term()} | ignore).
|
|
||||||
init([]) ->
|
|
||||||
{ok, Socket} = gen_udp:open(0, [binary, {active, true}]),
|
|
||||||
inet_udp:controlling_process(Socket, self()),
|
|
||||||
|
|
||||||
erlang:start_timer(5000, self(), qps_ticker),
|
|
||||||
|
|
||||||
{ok, #state{socket = Socket}}.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Handling call messages
|
|
||||||
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
|
|
||||||
State :: #state{}) ->
|
|
||||||
{reply, Reply :: term(), NewState :: #state{}} |
|
|
||||||
{reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
|
||||||
handle_call(wget, _From, State=#state{socket = Socket}) ->
|
|
||||||
gen_udp:send(Socket, "127.0.0.1", 22222, <<1>>),
|
|
||||||
{reply, ok, State}.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Handling cast messages
|
|
||||||
-spec(handle_cast(Request :: term(), State :: #state{}) ->
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
|
||||||
handle_cast(_Info, State) ->
|
|
||||||
{noreply, State}.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Handling all non call/cast messages
|
|
||||||
-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
|
||||||
handle_info({udp, Sock, _Ip, _Port, Data}, State = #state{socket = Sock, bytes = Bytes}) ->
|
|
||||||
{noreply, State#state{bytes = Bytes + byte_size(Data)}};
|
|
||||||
handle_info({timeout, _, qps_ticker}, State = #state{bytes = Bytes}) ->
|
|
||||||
logger:debug("[sdlan_udp_wget] qps is: ~p(M)", [Bytes / 1024 / 1024]),
|
|
||||||
erlang:start_timer(5000, self(), qps_ticker),
|
|
||||||
{noreply, State#state{bytes = 0}}.
|
|
||||||
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc This function is called by a gen_server when it is about to
|
|
||||||
%% terminate. It should be the opposite of Module:init/1 and do any
|
|
||||||
%% necessary cleaning up. When it returns, the gen_server terminates
|
|
||||||
%% with Reason. The return value is ignored.
|
|
||||||
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
|
|
||||||
State :: #state{}) -> term()).
|
|
||||||
terminate(_Reason, _State = #state{}) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% @doc Convert process state when code is changed
|
|
||||||
-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
|
|
||||||
Extra :: term()) ->
|
|
||||||
{ok, NewState :: #state{}} | {error, Reason :: term()}).
|
|
||||||
code_change(_OldVsn, State = #state{}, _Extra) ->
|
|
||||||
{ok, State}.
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% Internal functions
|
|
||||||
%%%===================================================================
|
|
||||||
@ -9,12 +9,14 @@
|
|||||||
-module(stun_client).
|
-module(stun_client).
|
||||||
-author("anlicheng").
|
-author("anlicheng").
|
||||||
-include("sdlan_pb.hrl").
|
-include("sdlan_pb.hrl").
|
||||||
|
-include("sdlan.hrl").
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
-export([register/1, debug_info/1]).
|
-export([register/1, debug_info/1]).
|
||||||
|
-export([test/0]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
|
||||||
@ -25,19 +27,25 @@
|
|||||||
-define(STUN_REGISTER, 3).
|
-define(STUN_REGISTER, 3).
|
||||||
%% 响应
|
%% 响应
|
||||||
-define(STUN_REGISTER_ACK, 4).
|
-define(STUN_REGISTER_ACK, 4).
|
||||||
|
|
||||||
-define(STUN_DATA, 5).
|
-define(STUN_DATA, 5).
|
||||||
|
|
||||||
|
-define(SUPER_HOST, "127.0.0.1").
|
||||||
|
-define(SUPER_PORT, 1265).
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
socket,
|
|
||||||
tun_socket,
|
tun_socket,
|
||||||
client_id :: binary(),
|
client_id :: binary(),
|
||||||
|
mac :: binary(),
|
||||||
network_id,
|
network_id,
|
||||||
net_addr,
|
ip,
|
||||||
mask_len,
|
mask_len,
|
||||||
aes_key,
|
aes_key,
|
||||||
cookie = 1,
|
cookie = 1,
|
||||||
|
|
||||||
|
session_token :: binary(),
|
||||||
|
|
||||||
|
private_key,
|
||||||
|
|
||||||
sessions = #{}
|
sessions = #{}
|
||||||
}).
|
}).
|
||||||
|
|
||||||
@ -45,6 +53,11 @@
|
|||||||
%%% API
|
%%% API
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
|
test() ->
|
||||||
|
{ok, Pid} = start_link(),
|
||||||
|
register(Pid),
|
||||||
|
ok.
|
||||||
|
|
||||||
register(Pid) when is_pid(Pid) ->
|
register(Pid) when is_pid(Pid) ->
|
||||||
gen_server:call(Pid, register).
|
gen_server:call(Pid, register).
|
||||||
|
|
||||||
@ -67,11 +80,10 @@ start_link() ->
|
|||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term()} | ignore).
|
{stop, Reason :: term()} | ignore).
|
||||||
init([]) ->
|
init([]) ->
|
||||||
{ok, Socket} = gen_tcp:connect("localhost", 18083, [binary, {packet, 2}, {active, true}]),
|
{ok, TunSocket} = gen_udp:open(0, [binary, {active, true}]),
|
||||||
inet_tcp:controlling_process(Socket, self()),
|
Ip = sdlan_ipaddr:ipv4_to_int({10, 211, 179, 1}),
|
||||||
{ok, TunSocket} = gen_udp:open(12345, [binary, {active, true}]),
|
|
||||||
|
|
||||||
{ok, #state{socket = Socket, tun_socket = TunSocket, client_id = <<"22222222222222222222222222222222">>}}.
|
{ok, #state{tun_socket = TunSocket, network_id = 8, mac = <<11, 12, 13, 14, 15, 16>>, ip = Ip, mask_len = 24, client_id = <<"22222222222222222222222222222222">>}}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc Handling call messages
|
%% @doc Handling call messages
|
||||||
@ -86,37 +98,26 @@ init([]) ->
|
|||||||
handle_call(debug_info, _From, State) ->
|
handle_call(debug_info, _From, State) ->
|
||||||
{reply, {ok, State}, State};
|
{reply, {ok, State}, State};
|
||||||
|
|
||||||
handle_call(register, _From, State = #state{socket = Socket, client_id = ClientId}) ->
|
handle_call(register, _From, State = #state{tun_socket = Socket, network_id = NId, client_id = ClientId, ip = Ip, mac = Mac, mask_len = MaskLen}) ->
|
||||||
Req = #{
|
{PublicKeyBin, PrivateKey} = sdlan_cipher:rsa_generate_key(),
|
||||||
<<"version">> => 1,
|
|
||||||
<<"client_id">> => ClientId,
|
|
||||||
<<"dev_addr">> => #{
|
|
||||||
<<"net_addr">> => 0,
|
|
||||||
<<"net_bit_len">> => 0
|
|
||||||
},
|
|
||||||
<<"token">> => <<"1234567890">>
|
|
||||||
},
|
|
||||||
|
|
||||||
Register = #sdl_register_super {
|
Register = #sdl_register_super {
|
||||||
version = 1,
|
pkt_id = 1,
|
||||||
installed_channel = <<"macos">>,
|
|
||||||
client_id = ClientId,
|
client_id = ClientId,
|
||||||
dev_addr = #sdl_dev_addr {
|
network_id = NId,
|
||||||
network_id = 0,
|
mac = Mac,
|
||||||
mac = <<11, 12, 13, 14, 15, 16>>,
|
ip = Ip,
|
||||||
net_addr = 0,
|
mask_len = MaskLen,
|
||||||
net_bit_len = 0
|
hostname = <<"mysql1">>,
|
||||||
},
|
pub_key = PublicKeyBin,
|
||||||
pub_key = <<>>,
|
access_token = <<"access_token1234">>
|
||||||
token = <<"1234567890">>
|
|
||||||
},
|
},
|
||||||
|
|
||||||
logger:debug("register is: ~p", [Register]),
|
logger:debug("register is: ~p", [Register]),
|
||||||
|
|
||||||
Packet = jiffy:encode(Req, [force_utf8]),
|
Pkt = sdlan_pb:encode_msg(Register),
|
||||||
ok = gen_tcp:send(Socket, <<1:32, 101, Packet/binary>>),
|
ok = gen_udp:send(Socket, ?SUPER_HOST, ?SUPER_PORT, <<?PACKET_REGISTER_SUPER, Pkt/binary>>),
|
||||||
|
|
||||||
{reply, ok, State}.
|
{reply, ok, State#state{private_key = PrivateKey}}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc Handling cast messages
|
%% @doc Handling cast messages
|
||||||
@ -133,56 +134,62 @@ handle_cast(_Request, State = #state{}) ->
|
|||||||
{noreply, NewState :: #state{}} |
|
{noreply, NewState :: #state{}} |
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
{stop, Reason :: term(), NewState :: #state{}}).
|
||||||
handle_info({tcp, Socket, <<1:32, 5, Data/binary>>}, State = #state{socket = Socket, tun_socket = TunSocket, client_id = ClientId, cookie = Cookie}) ->
|
handle_info({udp, _Socket, _ServerIp, _ServerPort, <<?PACKET_REGISTER_SUPER_ACK, Data/binary>>}, State = #state{private_key = PrivateKey}) ->
|
||||||
Response = jiffy:decode(Data, [return_maps]),
|
SuperAck = #sdl_register_super_ack{session_token = SessionToken, aes_key = EncAesKey} = sdlan_pb:decode_msg(Data, sdl_register_super_ack),
|
||||||
#{
|
|
||||||
<<"dev_addr">> := #{
|
|
||||||
<<"network_id">> := NetworkId,
|
|
||||||
<<"net_addr">> := NetAddr,
|
|
||||||
<<"net_bit_len">> := NetBitLen
|
|
||||||
},
|
|
||||||
<<"aes_key">> := AesKey,
|
|
||||||
<<"lifetime">> := Lifetime
|
|
||||||
} = Response,
|
|
||||||
|
|
||||||
logger:debug("[stun_client] get a register super response: ~p, alloc ip addr: ~p", [Response, sdlan_ipaddr:int_to_ipv4(NetAddr)]),
|
AesKey = sdlan_cipher:rsa_decrypt(EncAesKey, PrivateKey),
|
||||||
|
logger:debug("[stun_client] get a register super ack: ~p, aes_key: ~p", [SuperAck, AesKey]),
|
||||||
|
|
||||||
%% 开始注册自己的tun信息
|
erlang:start_timer(5000, self(), stun_request_ticker),
|
||||||
gen_udp:send(TunSocket, "localhost", 1265, <<1, Cookie:32, ClientId/binary, NetworkId:32, NetAddr:32>>),
|
|
||||||
|
|
||||||
{noreply, State#state{network_id = NetworkId, net_addr = NetAddr, mask_len = NetBitLen, aes_key = AesKey, cookie = Cookie + 1}};
|
{noreply, State#state{session_token = SessionToken}};
|
||||||
|
|
||||||
handle_info({udp, _, _, _, <<2, Cookie:32, Family, Port:16, Ip0, Ip1, Ip2, Ip3>>}, State = #state{}) ->
|
handle_info({udp, _Socket, _ServerIp, _ServerPort, <<?PACKET_REGISTER_SUPER_NAK, Data/binary>>}, State) ->
|
||||||
logger:debug("[stun_client] tun register ack, cookie: ~p, ack: ~p", [Cookie, {Family, Port, {Ip0, Ip1, Ip2, Ip3}}]),
|
SuperNak = #sdl_register_super_nak{} = sdlan_pb:decode_msg(Data, sdl_register_super_nak),
|
||||||
|
logger:debug("[stun_client] get a register super nak: ~p", [SuperNak]),
|
||||||
|
{noreply, State#state{}};
|
||||||
|
|
||||||
|
%% 错误的事件处理
|
||||||
|
handle_info({udp, _Socket, _ServerIp, _ServerPort, <<?PACKET_EVENT, ?PACKET_EVENT_REFRESH_AUTH, Data/binary>>}, State) ->
|
||||||
|
Event = #sdl_refresh_auth_event{} = sdlan_pb:decode_msg(Data, sdl_refresh_auth_event),
|
||||||
|
logger:debug("[stun_client] get a refresh_auth_event: ~p", [Event]),
|
||||||
|
{stop, refresh_auth, State};
|
||||||
|
|
||||||
|
handle_info({udp, _Socket, _ServerIp, _ServerPort, <<?PACKET_PEER_INFO, Data/binary>>}, State) ->
|
||||||
|
PeerInfo = #sdl_peer_info{} = sdlan_pb:decode_msg(Data, sdl_peer_info),
|
||||||
|
logger:debug("[stun_client] get peer info: ~p", [PeerInfo]),
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
|
||||||
handle_info({udp, _, Ip, Port, <<?STUN_REGISTER:8, NetworkId:32, SrcIp:32, DstIp:32>>}, State = #state{tun_socket = TunSocket, sessions = Sessions}) ->
|
handle_info({udp, _Socket, _ServerIp, _ServerPort, <<?PACKET_EVENT, EventType:8, Event/binary>>}, State) ->
|
||||||
Packet = <<?STUN_REGISTER_ACK, NetworkId:32, DstIp:32, SrcIp:32>>,
|
logger:debug("[stun_client] get event_type: ~p, event: ~p", [EventType, Event]),
|
||||||
logger:debug("[stun_client] will send stun reply: ~p, peer: ~p", [Packet, {Ip, Port}]),
|
|
||||||
ok = gen_udp:send(TunSocket, Ip, Port, Packet),
|
|
||||||
|
|
||||||
NSessions = maps:put(SrcIp, {Ip, Port}, Sessions),
|
|
||||||
{noreply, State#state{sessions = NSessions}};
|
|
||||||
|
|
||||||
handle_info({udp, _, Ip, Port, <<?STUN_REGISTER_ACK, NetworkId:32, SrcIp:32, DstIp:32>>}, State = #state{sessions = Sessions}) ->
|
|
||||||
logger:debug("[stun_client] stun_data: network_id: ~p, src: ~p, dst: ~p, register_ack!!!", [NetworkId, SrcIp, DstIp]),
|
|
||||||
NSessions = maps:put(SrcIp, {Ip, Port}, Sessions),
|
|
||||||
{noreply, State#state{sessions = NSessions}};
|
|
||||||
|
|
||||||
handle_info({udp, _, _Ip0, _Port0, <<?STUN_DATA, NetworkId:32, SrcIp:32, DstIp:32, TTL:8, Data/binary>>}, State = #state{tun_socket = TunSocket, sessions = Sessions}) ->
|
|
||||||
logger:debug("[stun_client] stun_data: network_id: ~p, src: ~p, dst: ~p, data!!!", [NetworkId, SrcIp, DstIp]),
|
|
||||||
case maps:is_key(SrcIp, Sessions) of
|
|
||||||
true ->
|
|
||||||
{Ip, Port} = maps:get(SrcIp, Sessions),
|
|
||||||
ok = gen_udp:send(TunSocket, Ip, Port, <<?STUN_DATA, NetworkId:32, DstIp:32, SrcIp:32, 255, Data/binary>>),
|
|
||||||
logger:debug("[stun_client] stun_data: network_id: ~p, src: ~p, dst: ~p, reply data!!!", [NetworkId, SrcIp, DstIp]);
|
|
||||||
false ->
|
|
||||||
logger:debug("[stun_client] stun_data: network_id: ~p, src: ~p, dst: ~p, no session", [NetworkId, SrcIp, DstIp])
|
|
||||||
end,
|
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
|
||||||
handle_info(Info, State = #state{}) ->
|
handle_info({timeout, _, stun_request_ticker},
|
||||||
logger:debug("[stun_client] get info: ~p", [Info]),
|
State = #state{tun_socket = TunSocket, network_id = NetworkId, client_id = ClientId, mac = Mac, ip = Ip, session_token = SessionToken}) ->
|
||||||
|
|
||||||
|
Packet = sdlan_pb:encode_msg(#sdl_stun_request{
|
||||||
|
client_id = ClientId,
|
||||||
|
network_id = 8,
|
||||||
|
mac = Mac,
|
||||||
|
ip = Ip,
|
||||||
|
nat_type = 0,
|
||||||
|
session_token = SessionToken
|
||||||
|
}),
|
||||||
|
logger:debug("timer ticker will send packet: ~p", [Packet]),
|
||||||
|
|
||||||
|
gen_udp:send(TunSocket, ?SUPER_HOST, ?SUPER_PORT, <<?PACKET_STUN_REQUEST, Packet/binary>>),
|
||||||
|
erlang:start_timer(5000, self(), stun_request_ticker),
|
||||||
|
|
||||||
|
QueryPacket = sdlan_pb:encode_msg(#sdl_query_info{
|
||||||
|
pkt_id = 10,
|
||||||
|
network_id = NetworkId,
|
||||||
|
src_mac = Mac,
|
||||||
|
dst_mac = Mac,
|
||||||
|
session_token = SessionToken
|
||||||
|
}),
|
||||||
|
gen_udp:send(TunSocket, ?SUPER_HOST, ?SUPER_PORT, <<?PACKET_QUERY_INFO, QueryPacket/binary>>),
|
||||||
|
logger:debug("[stun_client] will send query packet: ~p", [QueryPacket]),
|
||||||
|
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
|
|||||||
@ -3,14 +3,7 @@
|
|||||||
|
|
||||||
{http_server, [
|
{http_server, [
|
||||||
{port, 18082},
|
{port, 18082},
|
||||||
{acceptors, 500},
|
{acceptors, 1},
|
||||||
{max_connections, 10240},
|
|
||||||
{backlog, 10240}
|
|
||||||
]},
|
|
||||||
|
|
||||||
{tcp_server, [
|
|
||||||
{port, 18083},
|
|
||||||
{acceptors, 500},
|
|
||||||
{max_connections, 10240},
|
{max_connections, 10240},
|
||||||
{backlog, 10240}
|
{backlog, 10240}
|
||||||
]},
|
]},
|
||||||
@ -21,7 +14,7 @@
|
|||||||
%% stun类型探测相当于有个类型
|
%% stun类型探测相当于有个类型
|
||||||
{stun_servers, [
|
{stun_servers, [
|
||||||
{port, 1265},
|
{port, 1265},
|
||||||
{acceptor_nums, 5}
|
{acceptor_nums, 1}
|
||||||
]},
|
]},
|
||||||
|
|
||||||
{stun_port_assist, [
|
{stun_port_assist, [
|
||||||
@ -40,21 +33,6 @@
|
|||||||
]},
|
]},
|
||||||
|
|
||||||
{pools, [
|
{pools, [
|
||||||
%% mysql连接池配置
|
|
||||||
{mysql_sdlan,
|
|
||||||
[{size, 10}, {max_overflow, 20}, {worker_module, mysql}],
|
|
||||||
[
|
|
||||||
{host, {39, 98, 184, 67}},
|
|
||||||
{port, 3306},
|
|
||||||
{user, "sdlanuser"},
|
|
||||||
{connect_mode, lazy},
|
|
||||||
{keep_alive, true},
|
|
||||||
{password, "sdlan@J1c8WGu"},
|
|
||||||
{database, "sdlan"},
|
|
||||||
{queries, [<<"set names utf8">>]}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{dns_resolver_pool,
|
{dns_resolver_pool,
|
||||||
[{size, 20}, {max_overflow, 100}, {worker_module, dns_resolver}],
|
[{size, 20}, {max_overflow, 100}, {worker_module, dns_resolver}],
|
||||||
[]
|
[]
|
||||||
@ -85,15 +63,14 @@
|
|||||||
{handler, disk, logger_disk_log_h,
|
{handler, disk, logger_disk_log_h,
|
||||||
#{
|
#{
|
||||||
level => debug,
|
level => debug,
|
||||||
config => #{
|
config => #{
|
||||||
file => "log/debug.log",
|
file => "log/debug.log",
|
||||||
max_no_files => 10,
|
max_no_files => 10,
|
||||||
max_no_bytes => 524288000
|
max_no_bytes => 524288000
|
||||||
},
|
},
|
||||||
formatter => {logger_formatter, #{template => [time, " [", level, "] ", msg, "\n"]}}
|
formatter => {logger_formatter, #{template => [time, " [", level, "] ", msg, "\n"]}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]}
|
]}
|
||||||
]}
|
]}
|
||||||
].
|
].
|
||||||
|
|||||||
@ -8,20 +8,13 @@
|
|||||||
{backlog, 10240}
|
{backlog, 10240}
|
||||||
]},
|
]},
|
||||||
|
|
||||||
{tcp_server, [
|
|
||||||
{port, 18083},
|
|
||||||
{acceptors, 500},
|
|
||||||
{max_connections, 10240},
|
|
||||||
{backlog, 10240}
|
|
||||||
]},
|
|
||||||
|
|
||||||
%% 网络带宽, 单位为: kb
|
%% 网络带宽, 单位为: kb
|
||||||
{band_width, 2048},
|
{band_width, 2048},
|
||||||
|
|
||||||
%% stun类型探测相当于有个类型
|
%% stun类型探测相当于有个类型
|
||||||
{stun_servers, [
|
{stun_servers, [
|
||||||
{port, 1265},
|
{port, 1265},
|
||||||
{acceptor_nums, 1}
|
{acceptor_nums, 5}
|
||||||
]},
|
]},
|
||||||
|
|
||||||
{stun_port_assist, [
|
{stun_port_assist, [
|
||||||
@ -40,21 +33,6 @@
|
|||||||
]},
|
]},
|
||||||
|
|
||||||
{pools, [
|
{pools, [
|
||||||
%% mysql连接池配置
|
|
||||||
{mysql_sdlan,
|
|
||||||
[{size, 10}, {max_overflow, 20}, {worker_module, mysql}],
|
|
||||||
[
|
|
||||||
{host, {118, 178, 229, 213}},
|
|
||||||
{port, 3306},
|
|
||||||
{user, "sdlanuser"},
|
|
||||||
{connect_mode, lazy},
|
|
||||||
{keep_alive, true},
|
|
||||||
{password, "sdlan@J1c8WGu"},
|
|
||||||
{database, "sdlan"},
|
|
||||||
{queries, [<<"set names utf8">>]}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{dns_resolver_pool,
|
{dns_resolver_pool,
|
||||||
[{size, 20}, {max_overflow, 100}, {worker_module, dns_resolver}],
|
[{size, 20}, {max_overflow, 100}, {worker_module, dns_resolver}],
|
||||||
[]
|
[]
|
||||||
@ -87,14 +65,15 @@
|
|||||||
#{
|
#{
|
||||||
level => debug,
|
level => debug,
|
||||||
config => #{
|
config => #{
|
||||||
file => "log/debug.log",
|
file => "log/debug.log",
|
||||||
max_no_files => 10,
|
max_no_files => 10,
|
||||||
max_no_bytes => 524288000
|
max_no_bytes => 524288000
|
||||||
},
|
},
|
||||||
formatter => {logger_formatter, #{template => [time, " [", level, "] ", msg, "\n"]}}
|
formatter => {logger_formatter, #{template => [time, " [", level, "] ", msg, "\n"]}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]}
|
]}
|
||||||
]}
|
]}
|
||||||
|
|
||||||
].
|
].
|
||||||
|
|||||||
248
message.proto
248
message.proto
@ -1,5 +1,10 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
|
// 注意
|
||||||
|
// 1. network_id, ip地址等分配的逻辑已经迁移到后端https服务
|
||||||
|
// 2. 端的强制升级逻辑也迁移到了https服务
|
||||||
|
// 3. 假设一个udp包的最大有效负载为1400字节;因此虚拟网卡启动的时候mtu需要控制一下,因为每个包里面都有自己的元数据信息, 端上的mtu值先统一为:1250
|
||||||
|
|
||||||
// 基础公共类型定义
|
// 基础公共类型定义
|
||||||
|
|
||||||
message SDLV4Info {
|
message SDLV4Info {
|
||||||
@ -13,111 +18,111 @@ message SDLV6Info {
|
|||||||
bytes v6 = 2;
|
bytes v6 = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设备网络地址信息
|
|
||||||
message SDLDevAddr {
|
|
||||||
uint32 network_id = 1;
|
|
||||||
bytes mac = 2;
|
|
||||||
uint32 net_addr = 3;
|
|
||||||
uint32 net_bit_len = 4;
|
|
||||||
string network_domain = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tcp通讯消息
|
|
||||||
message SDLEmpty {
|
message SDLEmpty {
|
||||||
|
uint32 pkt_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 这里修改成了扁平的结构, 否则有些字段不好找放的位置
|
||||||
message SDLRegisterSuper {
|
message SDLRegisterSuper {
|
||||||
uint32 version = 1;
|
// 所有需要建立请求和响应对应关系的,都是通过4字节的pktId来对应
|
||||||
string installed_channel = 2;
|
uint32 pkt_id = 1;
|
||||||
string client_id = 3;
|
|
||||||
SDLDevAddr dev_addr = 4;
|
|
||||||
string pub_key = 5;
|
|
||||||
string token = 6;
|
|
||||||
string network_code = 7;
|
|
||||||
string hostname = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SDLRegisterSuperAck {
|
|
||||||
SDLDevAddr dev_addr = 1;
|
|
||||||
bytes aes_key = 2;
|
|
||||||
uint32 upgrade_type = 3;
|
|
||||||
optional string upgrade_prompt = 4;
|
|
||||||
optional string upgrade_address = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SDLRegisterSuperNak {
|
|
||||||
uint32 error_code = 1;
|
|
||||||
string error_message = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 网络地址查询
|
|
||||||
|
|
||||||
message SDLQueryInfo {
|
|
||||||
bytes dst_mac = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SDLPeerInfo {
|
|
||||||
bytes dst_mac = 1;
|
|
||||||
SDLV4Info v4_info = 2;
|
|
||||||
optional SDLV6Info v6_info = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 事件定义
|
|
||||||
|
|
||||||
message SDLNatChangedEvent {
|
|
||||||
bytes mac = 1;
|
|
||||||
uint32 ip = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SDLSendRegisterEvent {
|
|
||||||
bytes dst_mac = 1;
|
|
||||||
uint32 nat_ip = 2;
|
|
||||||
uint32 nat_port = 3;
|
|
||||||
uint32 nat_type = 4;
|
|
||||||
optional SDLV6Info v6_info = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SDLNetworkShutdownEvent {
|
|
||||||
string message = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 命令定义
|
|
||||||
|
|
||||||
message SDLChangeNetworkCommand {
|
|
||||||
SDLDevAddr dev_addr = 1;
|
|
||||||
bytes aes_key = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SDLCommandAck {
|
|
||||||
// status = true, 表示成功;status = false 表示失败,message是失败原因描述
|
|
||||||
bool status = 1;
|
|
||||||
optional string message = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SDLFlows {
|
|
||||||
// 服务器转发流量
|
|
||||||
uint32 forward_num = 1;
|
|
||||||
// p2p直接流量
|
|
||||||
uint32 p2p_num = 2;
|
|
||||||
// 接收的流量
|
|
||||||
uint32 inbound_num = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UDP通讯消息
|
|
||||||
|
|
||||||
message SDLStunRequest {
|
|
||||||
uint32 cookie = 1;
|
|
||||||
string client_id = 2;
|
string client_id = 2;
|
||||||
|
// 网络地址信息已经有https请求分配了
|
||||||
|
// 注册的时候需要带上(network_id, mac, ip, mask_len, hostname)
|
||||||
uint32 network_id = 3;
|
uint32 network_id = 3;
|
||||||
bytes mac = 4;
|
bytes mac = 4;
|
||||||
uint32 ip = 5;
|
uint32 ip = 5;
|
||||||
uint32 nat_type = 6;
|
uint32 mask_len = 6;
|
||||||
optional SDLV6Info v6_info = 7;
|
string hostname = 7;
|
||||||
|
|
||||||
|
string pub_key = 8;
|
||||||
|
// 客户端使用http协议请求后端,通过token或者账号密码登录时, 统一返回一个access_token;
|
||||||
|
// RegisterSuper的时候,验证凭证是否合法 (access_token)
|
||||||
|
string access_token = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SDLStunReply {
|
message SDLRegisterSuperAck {
|
||||||
uint32 cookie = 1;
|
uint32 pkt_id = 1;
|
||||||
|
bytes aes_key = 2;
|
||||||
|
// 验证通过后,返回session_token; 后续通讯的合法行需哟啊通过session_token来保证
|
||||||
|
// 在SDLQueryInfo,SDLStunRequest, SDLData, SDLArpRequest等需要服务器端介入的地方都增加了session_token的验证(端和端之间的SDLData不需要校验)
|
||||||
|
bytes session_token = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册失败时候的消息体
|
||||||
|
message SDLRegisterSuperNak {
|
||||||
|
uint32 pkt_id = 1;
|
||||||
|
uint32 error_code = 2;
|
||||||
|
string error_message = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网络地址查询
|
||||||
|
message SDLQueryInfo {
|
||||||
|
uint32 pkt_id = 1;
|
||||||
|
uint32 network_id = 2;
|
||||||
|
bytes src_mac = 3;
|
||||||
|
bytes dst_mac = 4;
|
||||||
|
bytes session_token = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SDLPeerInfo {
|
||||||
|
uint32 pkt_id = 1;
|
||||||
|
uint32 network_id = 2;
|
||||||
|
bytes dst_mac = 3;
|
||||||
|
SDLV4Info v4_info = 4;
|
||||||
|
optional SDLV6Info v6_info = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SDLEmptyPeerInfo {
|
||||||
|
uint32 pkt_id = 1;
|
||||||
|
uint32 network_id = 2;
|
||||||
|
bytes dst_mac = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件定义, 下面的事件是服务器主动推送的,不需要响应
|
||||||
|
|
||||||
|
message SDLNatChangedEvent {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
bytes mac = 2;
|
||||||
|
uint32 ip = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 被清理掉的Endpoints
|
||||||
|
// 协议改成udp后,服务端只能通过定时器来集中清理掉线的端(通过心跳机制SDLStunRequest)
|
||||||
|
message SDLDropMacsEvent {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
repeated bytes macs = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知端上必须重新校验
|
||||||
|
// 服务器端认为端已经掉线了,但是这个时候还在向服务端发送消息;服务端要求端上重新校验(SDLRegisterSuper)
|
||||||
|
message SDLRefreshAuthEvent {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SDLSendRegisterEvent {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
bytes dst_mac = 2;
|
||||||
|
uint32 nat_ip = 3;
|
||||||
|
uint32 nat_port = 4;
|
||||||
|
uint32 nat_type = 5;
|
||||||
|
optional SDLV6Info v6_info = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SDLNetworkShutdownEvent {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDP通讯消息
|
||||||
|
message SDLStunRequest {
|
||||||
|
string client_id = 1;
|
||||||
|
uint32 network_id = 2;
|
||||||
|
bytes mac = 3;
|
||||||
|
uint32 ip = 4;
|
||||||
|
uint32 nat_type = 5;
|
||||||
|
optional SDLV6Info v6_info = 6;
|
||||||
|
bytes session_token = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SDLData {
|
message SDLData {
|
||||||
@ -127,8 +132,14 @@ message SDLData {
|
|||||||
bool is_p2p = 4;
|
bool is_p2p = 4;
|
||||||
uint32 ttl = 5;
|
uint32 ttl = 5;
|
||||||
bytes data = 6;
|
bytes data = 6;
|
||||||
|
bytes session_token = 7;
|
||||||
|
// 端通过https登录的时候,服务端会分配该端对应的权限标识
|
||||||
|
// 后续的请求过程中需要带上这个值,对端通过这个值要判断对数据包是否放行
|
||||||
|
uint32 identity_id = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 这个是客户端之间的相互打洞的数据
|
||||||
|
|
||||||
message SDLRegister {
|
message SDLRegister {
|
||||||
uint32 network_id = 1;
|
uint32 network_id = 1;
|
||||||
bytes src_mac = 2;
|
bytes src_mac = 2;
|
||||||
@ -146,10 +157,53 @@ message SDLRegisterAck {
|
|||||||
message SDLStunProbe {
|
message SDLStunProbe {
|
||||||
uint32 cookie = 1;
|
uint32 cookie = 1;
|
||||||
uint32 attr = 2;
|
uint32 attr = 2;
|
||||||
|
// 增加step是为了方便端上判断,收到的请求和响应之间的映射关系;服务器端原样返回
|
||||||
|
uint32 step = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SDLStunProbeReply {
|
message SDLStunProbeReply {
|
||||||
uint32 cookie = 1;
|
uint32 cookie = 1;
|
||||||
uint32 port = 2;
|
// 增加step是为了方便端上判断,收到的请求和响应之间的映射关系;服务器端原样返回
|
||||||
uint32 ip = 3;
|
uint32 step = 2;
|
||||||
|
uint32 port = 3;
|
||||||
|
uint32 ip = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ARP查询相关
|
||||||
|
// 真实的arp请求是通过广播的形式获取到的,但是针对于macos这种tun的实现;是能够分析出arp请求包的;对于当前网络来说,服务端是知道mac对应的ip地址的,因此没有必要广播;直接通过服务器端返回
|
||||||
|
message SDLArpRequest {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
uint32 target_ip = 2;
|
||||||
|
bytes session_token = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SDLArpResponse {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
uint32 target_ip = 2;
|
||||||
|
bytes target_mac = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 权限请求查询相关
|
||||||
|
message SDLPolicyRequest {
|
||||||
|
string client_id = 1;
|
||||||
|
uint32 network_id = 2;
|
||||||
|
bytes mac = 3;
|
||||||
|
uint32 src_identity_id = 4;
|
||||||
|
uint32 dst_identity_id = 5;
|
||||||
|
uint32 version = 6;
|
||||||
|
bytes session_token = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SDLPolicyResponse {
|
||||||
|
uint32 network_id = 1;
|
||||||
|
uint32 src_identity_id = 2;
|
||||||
|
uint32 dst_identity_id = 3;
|
||||||
|
// 版本号,客户端需要比较版本号确定是否覆盖
|
||||||
|
uint32 version = 4;
|
||||||
|
// 总包数
|
||||||
|
uint32 total_num = 5;
|
||||||
|
// 当前分片
|
||||||
|
uint32 index = 6;
|
||||||
|
// 4+1+2 的稀疏序列化规则
|
||||||
|
bytes rules = 7;
|
||||||
}
|
}
|
||||||
64
policy.sql
Normal file
64
policy.sql
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
CREATE TABLE identity (
|
||||||
|
identity_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
network_id INT NOT NULL,
|
||||||
|
-- 业务侧来源
|
||||||
|
subject_type ENUM('token', 'instance', 'user', 'service') NOT NULL,
|
||||||
|
subject_id VARCHAR(128) NOT NULL,
|
||||||
|
|
||||||
|
created_at INT(10) NOT NULL DEFAULT 0,
|
||||||
|
expired_at INT(10) NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
UNIQUE KEY uk_subject (network_id, subject_type, subject_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- policy 表(权限集合,可复用)
|
||||||
|
CREATE TABLE policy (
|
||||||
|
policy_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
network_id INT NOT NULL,
|
||||||
|
name VARCHAR(64) NOT NULL,
|
||||||
|
description VARCHAR(255),
|
||||||
|
created_at INT(10) NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
-- identity_policy(多对多关系)
|
||||||
|
CREATE TABLE identity_policy (
|
||||||
|
identity_id INT NOT NULL,
|
||||||
|
policy_id INT NOT NULL,
|
||||||
|
PRIMARY KEY (identity_id, policy_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE rule (
|
||||||
|
rule_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
network_id INT NOT NULL,
|
||||||
|
-- 来源限制(可选)
|
||||||
|
src_policy_id INT NOT NULL,
|
||||||
|
-- 目标限制(可选,允许 NULL 表示 any)
|
||||||
|
dst_policy_id INT NULL,
|
||||||
|
-- 6=TCP, 17=UDP
|
||||||
|
proto TINYINT NOT NULL,
|
||||||
|
-- 0~65535
|
||||||
|
port INT NOT NULL,
|
||||||
|
action ENUM('allow', 'deny') NOT NULL,
|
||||||
|
created_at INT(10) NOT NULL DEFAULT 0,
|
||||||
|
INDEX idx_src (src_policy_id),
|
||||||
|
INDEX idx_dst (dst_policy_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 实际操作逻辑
|
||||||
|
-- 1. 通过Token获取user+password的方式找到对应的identity_id,每个端都会有一个对应的identity_id值
|
||||||
|
-- 2. 数据访问的时候SDLData结构会携带一个identity_id, 被访问端会先查找自身的cache是否有对应identity_id(src_identity_id)的规则
|
||||||
|
|
||||||
|
|
||||||
|
-- 难点
|
||||||
|
-- 通过src_identity_id, dst_identity_id 查找到对应的rules
|
||||||
|
|
||||||
|
-- 查找来源对应的rules
|
||||||
|
-- $src_policy_ids = select * from identity_policy where identity_id = $src_identity_id
|
||||||
|
-- $drt_policy_ids = select * from identity_policy where identity_id = $dst_identity_id
|
||||||
|
|
||||||
|
-- 来源 src_policy_id 可以是Any (UNION {0});目标 dst_policy_id不能是Any
|
||||||
|
-- select * from rule where src_policy_id in ($src_policy_ids UNION {0}) and dst_policy_id in $drt_policy_ids
|
||||||
|
|
||||||
|
-- 然后合并全部的rules
|
||||||
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
{erl_opts, [
|
{erl_opts, [
|
||||||
debug_info
|
debug_info
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{deps, [
|
{deps, [
|
||||||
{poolboy, ".*", {git, "https://github.com/devinus/poolboy.git", {tag, "1.5.1"}}},
|
{poolboy, ".*", {git, "https://github.com/devinus/poolboy.git", {tag, "1.5.1"}}},
|
||||||
{hackney, ".*", {git, "https://github.com/benoitc/hackney.git", {tag, "1.16.0"}}},
|
{hackney, ".*", {git, "https://github.com/benoitc/hackney.git", {tag, "1.16.0"}}},
|
||||||
@ -10,9 +11,10 @@
|
|||||||
{gpb, ".*", {git, "https://github.com/tomas-abrahamsson/gpb.git", {tag, "4.21.1"}}},
|
{gpb, ".*", {git, "https://github.com/tomas-abrahamsson/gpb.git", {tag, "4.21.1"}}},
|
||||||
{throttle, ".*", {git, "https://github.com/lambdaclass/throttle.git", {tag, "0.3.0"}}},
|
{throttle, ".*", {git, "https://github.com/lambdaclass/throttle.git", {tag, "0.3.0"}}},
|
||||||
{dns_erlang, ".*", {git, "https://github.com/dnsimple/dns_erlang.git", {tag, "v4.4.0"}}},
|
{dns_erlang, ".*", {git, "https://github.com/dnsimple/dns_erlang.git", {tag, "v4.4.0"}}},
|
||||||
{quicer, ".*", {git, "https://github.com/emqx/quic.git", {tag, "0.4.0"}}},
|
|
||||||
{pkt, ".*", {git, "https://github.com/msantos/pkt.git", {tag, "0.6.0"}}},
|
{pkt, ".*", {git, "https://github.com/msantos/pkt.git", {tag, "0.6.0"}}},
|
||||||
{sync, ".*", {git, "https://github.com/rustyio/sync.git", {branch, "master"}}}
|
{sync, ".*", {git, "https://github.com/rustyio/sync.git", {branch, "master"}}},
|
||||||
|
{parse_trans, ".*", {git, "https://github.com/uwiger/parse_trans", {tag, "3.0.0"}}},
|
||||||
|
{lager, ".*", {git,"https://github.com/erlang-lager/lager.git", {tag, "3.9.2"}}}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{relx, [{release, {sdlan, "0.1.0"},
|
{relx, [{release, {sdlan, "0.1.0"},
|
||||||
@ -44,5 +46,4 @@
|
|||||||
%% {mode, minimal}
|
%% {mode, minimal}
|
||||||
]
|
]
|
||||||
}]}]}.
|
}]}]}.
|
||||||
|
|
||||||
{rebar_packages_cdn, "https://hexpm.upyun.com"}.
|
{rebar_packages_cdn, "https://hexpm.upyun.com"}.
|
||||||
@ -54,15 +54,10 @@
|
|||||||
{git,"https://github.com/devinus/poolboy.git",
|
{git,"https://github.com/devinus/poolboy.git",
|
||||||
{ref,"3bb48a893ff5598f7c73731ac17545206d259fac"}},
|
{ref,"3bb48a893ff5598f7c73731ac17545206d259fac"}},
|
||||||
0},
|
0},
|
||||||
{<<"quicer">>,
|
|
||||||
{git,"https://github.com/emqx/quic.git",
|
|
||||||
{ref,"c2962fd732caa29d513de7c645ea307cf89aaa9c"}},
|
|
||||||
0},
|
|
||||||
{<<"ranch">>,
|
{<<"ranch">>,
|
||||||
{git,"https://github.com/ninenines/ranch",
|
{git,"https://github.com/ninenines/ranch",
|
||||||
{ref,"a692f44567034dacf5efcaa24a24183788594eb7"}},
|
{ref,"a692f44567034dacf5efcaa24a24183788594eb7"}},
|
||||||
1},
|
1},
|
||||||
{<<"snabbkaffe">>,{pkg,<<"snabbkaffe">>,<<"1.0.10">>},1},
|
|
||||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},1},
|
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},1},
|
||||||
{<<"sync">>,
|
{<<"sync">>,
|
||||||
{git,"https://github.com/rustyio/sync.git",
|
{git,"https://github.com/rustyio/sync.git",
|
||||||
@ -82,7 +77,6 @@
|
|||||||
{<<"idna">>, <<"1D038FB2E7668CE41FBF681D2C45902E52B3CB9E9C77B55334353B222C2EE50C">>},
|
{<<"idna">>, <<"1D038FB2E7668CE41FBF681D2C45902E52B3CB9E9C77B55334353B222C2EE50C">>},
|
||||||
{<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>},
|
{<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>},
|
||||||
{<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>},
|
{<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>},
|
||||||
{<<"snabbkaffe">>, <<"9BE2F54F61FC6862391B666B2B5B76C3FA53598E2989A17CEF1B48CF347A8A63">>},
|
|
||||||
{<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>},
|
{<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>},
|
||||||
{<<"unicode_util_compat">>, <<"8516502659002CEC19E244EBD90D312183064BE95025A319A6C7E89F4BCCD65B">>}]},
|
{<<"unicode_util_compat">>, <<"8516502659002CEC19E244EBD90D312183064BE95025A319A6C7E89F4BCCD65B">>}]},
|
||||||
{pkg_hash_ext,[
|
{pkg_hash_ext,[
|
||||||
@ -93,7 +87,6 @@
|
|||||||
{<<"idna">>, <<"A02C8A1C4FD601215BB0B0324C8A6986749F807CE35F25449EC9E69758708122">>},
|
{<<"idna">>, <<"A02C8A1C4FD601215BB0B0324C8A6986749F807CE35F25449EC9E69758708122">>},
|
||||||
{<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>},
|
{<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>},
|
||||||
{<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>},
|
{<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>},
|
||||||
{<<"snabbkaffe">>, <<"70A98DF36AE756908D55B5770891D443D63C903833E3E87D544036E13D4FAC26">>},
|
|
||||||
{<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>},
|
{<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>},
|
||||||
{<<"unicode_util_compat">>, <<"D48D002E15F5CC105A696CF2F1BBB3FC72B4B770A184D8420C8DB20DA2674B38">>}]}
|
{<<"unicode_util_compat">>, <<"D48D002E15F5CC105A696CF2F1BBB3FC72B4B770A184D8420C8DB20DA2674B38">>}]}
|
||||||
].
|
].
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user