diff --git a/proto/sdlan.proto b/proto/sdlan.proto index 49f27b2..5121ee4 100644 --- a/proto/sdlan.proto +++ b/proto/sdlan.proto @@ -141,9 +141,7 @@ message SDLCommand { // 出口节点控制 message ExitNodeControl { int32 action = 1; // 必选:操作类型 - bytes node_mac = 2; // 必选:目标节点 MAC - uint32 node_ip = 3; // 必选:目标节点 IP - string remark = 4; // 可选:备注(方便日志/调试) + string remark = 2; // 可选:备注(方便日志/调试) } oneof command { diff --git a/src/http_handler/network_handler.erl b/src/http_handler/network_handler.erl index f7c0212..263488f 100644 --- a/src/http_handler/network_handler.erl +++ b/src/http_handler/network_handler.erl @@ -8,6 +8,7 @@ %%%------------------------------------------------------------------- -module(network_handler). -author("anlicheng"). +-include_lib("sdlan_pb.hrl"). %% API -export([handle_request/4]). @@ -35,17 +36,30 @@ handle_request("POST", "/network/delete", _, #{<<"id">> := NetworkId}) when Netw end end; -handle_request("POST", "/network/exit_node_control", _, #{<<"id">> := NetworkId}) when NetworkId > 0 -> +handle_request("POST", "/network/exit_node_control", _, #{<<"id">> := NetworkId, <<"action">> := Action, <<"dst_mac">> := DstMac, <<"remark">> := Remark, <<"timeout">> := Timeout}) when NetworkId > 0 -> case sdlan_network:get_pid(NetworkId) of undefined -> {ok, 200, sdlan_util:json_data(<<"network not found">>)}; NetworkPid when is_pid(NetworkPid) -> - case sdlan_network_sup:delete_network(NetworkId) of - ok -> - {ok, 200, sdlan_util:json_data(<<"success">>)}; + ReceiverPid = self(), + SubCommand = {exit_node, #'SDLCommand.ExitNodeControl'{ + action = Action, + remark = Remark + }}, + case sdlan_network:command(NetworkId, ReceiverPid, DstMac, SubCommand) of {error, Reason} -> - logger:debug("[network_handler] delete network: ~p, get error: ~p", [NetworkId, Reason]), - {ok, 200, sdlan_util:json_error(-1, <<"error">>)} + {ok, 200, sdlan_util:json_error(-1, Reason)}; + {ok, Ref} -> + case sdlan_network:wait_command_ack(Ref, Timeout * 1000) of + {ok, #'SDLCommandAck'{code = Code, message = Message, data = Data}} -> + {ok, 200, sdlan_util:json_data(#{ + <<"code">> => Code, + <<"message">> => Message, + <<"data">> => Data + })}; + {error, timeout} -> + {ok, 200, sdlan_util:json_error(-1, <<"任务执行超时"/utf8>>)} + end end end; diff --git a/src/sdlan_network.erl b/src/sdlan_network.erl index e10e82e..67139ae 100644 --- a/src/sdlan_network.erl +++ b/src/sdlan_network.erl @@ -22,6 +22,7 @@ -export([start_link/2]). -export([get_name/1, get_pid/1, lookup_pid/1, peer_info/3, unregister/3, debug_info/1, get_network_id/1, attach/6, arp_request/2]). -export([forward/5, update_hole/7, disable_client/2, get_channel/2]). +-export([command/4, wait_command_ack/2]). -export([test_event/1]). %% gen_server callbacks @@ -117,6 +118,20 @@ peer_info(Pid, SrcMac, DstMac) when is_pid(Pid), is_binary(SrcMac), is_binary(Ds arp_request(Pid, TargetIp) when is_pid(Pid), is_integer(TargetIp) -> gen_server:call(Pid, {arp_request, TargetIp}). +-spec command(Pid :: pid(), ReceiverPid :: pid(), DstMac :: binary(), {Tag :: atom(), SubCommand :: any()}) -> + {error, Reason :: binary()} | {ok, Ref :: reference()}. +command(Pid, ReceiverPid, DstMac, SubCommand) when is_pid(Pid), is_pid(ReceiverPid), is_binary(DstMac) -> + gen_server:call(Pid, {command, ReceiverPid, DstMac, SubCommand}). + +-spec wait_command_ack(Ref :: reference(), Timeout :: integer()) -> {error, timeout} | {ok, CommandAck :: #'SDLCommandAck'{}}. +wait_command_ack(Ref, Timeout) when is_reference(Ref), is_integer(Timeout) -> + receive + {quic_command_ack, Ref, CommandAck} -> + {ok, CommandAck} + after Timeout -> + {error, timeout} + end. + -spec forward(pid(), Sock :: any(), SrcMac :: binary(), DstMac :: binary(), Packet :: binary()) -> no_return(). forward(Pid, Sock, SrcMac, DstMac, Packet) when is_pid(Pid), is_binary(SrcMac), is_binary(DstMac), is_binary(Packet) -> gen_server:cast(Pid, {forward, Sock, SrcMac, DstMac, Packet}). @@ -291,6 +306,17 @@ handle_call({arp_request, TargetIp}, _From, State = #state{endpoints = Endpoints {reply, {ok, Mac}, State} end; +%% 发送命令 +handle_call({command, ReceiverPid, DstMac, SubCommand}, _From, State = #state{endpoints = Endpoints}) -> + case maps:find(DstMac, Endpoints) of + error -> + {reply, {error, <<"目标Node不在线"/utf8>>}}; + {ok, #endpoint{channel_pid = ChannelPid}} -> + Ref = make_ref(), + sdlan_quic_channel:command(ChannelPid, Ref, ReceiverPid, SubCommand), + {reply, {ok, Ref}, State} + end; + handle_call(debug_info, _From, State = #state{network_id = NetworkId, ipaddr = IpAddr, mask_len = MaskLen, owner_id = OwnerId, endpoints = Endpoints}) -> Reply = #{ <<"network_id">> => NetworkId,