support arp request

This commit is contained in:
anlicheng 2026-03-04 21:23:01 +08:00
parent b009a324bd
commit 92a5998868
5 changed files with 89 additions and 61 deletions

View File

@ -60,6 +60,10 @@
%%
-define(PACKET_WELCOME, 16#4F).
%% ARP查询
-define(PACKET_ARP_REQUEST, 16#50).
-define(PACKET_ARP_RESPONSE, 16#51).
%%
-define(PACKET_STUN_DATA, 16#FF).

View File

@ -159,7 +159,7 @@ handle_event(internal, {frame, <<?PACKET_REGISTER_SUPER, Body/binary>>}, initial
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),
IpAddr = sdlan_util:int_to_ipv4(Ip),
Params = #{
<<"network_id">> => NetworkId,
<<"client_id">> => ClientId,
@ -251,6 +251,28 @@ handle_event(internal, {frame, <<?PACKET_QUERY_INFO, Body/binary>>}, registered,
keep_state_and_data
end;
%% arp查询
handle_event(internal, {frame, <<?PACKET_ARP_REQUEST, Body/binary>>}, registered, #state{stream = Stream, network_id = NetworkId, network_pid = NetworkPid, mac = SrcMac}) when is_pid(NetworkPid) ->
#sdl_arp_request{pkt_id = PktId, target_ip = TargetIp} = sdlan_pb:decode_msg(Body, sdl_arp_request),
case sdlan_network:arp_request(NetworkPid, TargetIp) of
error ->
logger:debug("[sdlan_channel] network: ~p, arp_request target_ip: ~p, mac not found", [NetworkId, TargetIp]),
EmptyResponse = sdlan_pb:encode_msg(#sdl_empty{
pkt_id = PktId
}),
quic_send(Stream, <<?PACKET_ARP_RESPONSE, EmptyResponse/binary>>),
keep_state_and_data;
{ok, Mac} ->
logger:debug("[sdlan_channel] network: ~p, arp_request target_ip: ~p, mac: ~p", [NetworkId, sdlan_util:int_to_ipv4(TargetIp), sdlan_util:format_mac(Mac)]),
PeerInfo = sdlan_pb:encode_msg(#sdl_arp_response{
pkt_id = PktId,
target_ip = TargetIp,
target_mac = Mac
}),
quic_send(Stream, <<?PACKET_PEER_INFO, PeerInfo/binary>>),
keep_state_and_data
end;
handle_event(internal, {frame, <<?PACKET_POLICY_REQUEST, Body/binary>>}, registered, #state{stream = Stream, network_pid = NetworkPid}) when is_pid(NetworkPid) ->
maybe
PolicyRequest = catch sdlan_pb:decode_msg(Body, sdl_policy_request),

View File

@ -1,55 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2024, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 27. 3 2024 17:43
%%%-------------------------------------------------------------------
-module(sdlan_ipaddr).
-author("anlicheng").
%% API
-export([ipv4_to_int/1, int_to_ipv4/1, ips/2, format_ip/1]).
-export([ipv6_bytes_to_binary/1]).
format_ip(Ip) when is_integer(Ip) ->
int_to_ipv4(Ip);
format_ip(Ip) ->
Ip.
-spec ipv4_to_int(Ip :: integer() | binary() | inet:ip4_address()) -> integer().
ipv4_to_int(Ip) when is_integer(Ip) ->
Ip;
ipv4_to_int({Ip0, Ip1, Ip2, Ip3}) ->
<<Ip:32>> = <<Ip0, Ip1, Ip2, Ip3>>,
Ip;
ipv4_to_int(Ip) when is_binary(Ip) ->
Parts0 = binary:split(Ip, <<".">>, [global]),
Parts = lists:map(fun binary_to_integer/1, Parts0),
<<IpInt:32>> = iolist_to_binary(Parts),
IpInt.
-spec int_to_ipv4(Ip :: integer()) -> binary().
int_to_ipv4(Ip) when is_integer(Ip) ->
<<Ip0, Ip1, Ip2, Ip3>> = <<Ip:32>>,
<<(integer_to_binary(Ip0))/binary, $., (integer_to_binary(Ip1))/binary, $., (integer_to_binary(Ip2))/binary, $., (integer_to_binary(Ip3))/binary>>.
-spec ips(NetAddr :: binary(), MaskLen :: integer()) -> [Ip :: integer()].
ips(NetAddr, MaskLen) when is_binary(NetAddr), is_integer(MaskLen) ->
Mask = 16#FFFFFFFF bsr MaskLen,
Net0 = ipv4_to_int(NetAddr),
%% : "192.168.1.101",
L = 32 - MaskLen,
Net = (Net0 bsr L) bsl L,
lists:map(fun(V) -> Net + V end, lists:seq(1, Mask - 1)).
-spec ipv6_bytes_to_binary(Bytes :: binary()) -> Bin :: binary().
ipv6_bytes_to_binary(<<A:16, B:16, C:16, D:16, E:16, F:16, G:16, H:16>>) ->
Segments = [integer_to_list(X, 16) || X <- [A, B, C, D, E, F, G, H]],
% 4
Padded = [string:pad(S, 4, leading, $0) || S <- Segments],
% IPv6地址格式
iolist_to_binary(lists:flatten(string:join(Padded, ":")));
ipv6_bytes_to_binary(_) ->
<<"">>.

View File

@ -20,7 +20,7 @@
%% API
-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]).
-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([test_event/1]).
@ -109,6 +109,10 @@ unregister(Pid, ClientId, Mac) when is_pid(Pid), is_binary(ClientId), is_binary(
peer_info(Pid, SrcMac, DstMac) when is_pid(Pid), is_binary(SrcMac), is_binary(DstMac) ->
gen_server:call(Pid, {peer_info, SrcMac, DstMac}).
-spec arp_request(Pid :: pid(), TargetIp :: integer()) -> error | {ok, Mac :: binary()}.
arp_request(Pid, TargetIp) when is_pid(Pid), is_integer(TargetIp) ->
gen_server:call(Pid, {arp_request, TargetIp}).
-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}).
@ -183,7 +187,7 @@ handle_call({attach, ChannelPid, ClientId, Mac, Ip, Hostname}, _From,
State = #state{network_id = NetworkId, domain = Domain, endpoints = Endpoints, aes_key = AesKey}) ->
%% ip地址的时候mac地址为唯一基准
logger:debug("[sdlan_network] alloc_ip, network_id: ~p, client_id: ~p, mac: ~p, ip_addr: ~p",
[NetworkId, ClientId, sdlan_util:format_mac(Mac), sdlan_ipaddr:int_to_ipv4(Ip)]),
[NetworkId, ClientId, sdlan_util:format_mac(Mac), sdlan_util:int_to_ipv4(Ip)]),
%% ->ip的映射关系
sdlan_hostname_regedit:insert(Hostname, Domain, Ip),
@ -246,7 +250,7 @@ handle_call({peer_info, SrcMac, DstMac}, _From, State = #state{endpoints = Endpo
{ok, #endpoint{hole = #hole{peer = {SrcNatIp, SrcNatPort}, nat_type = SrcNatType}, v6_info = SrcV6Info}} ?= maps:find(SrcMac, Endpoints),
RegisterEvent = sdlan_pb:encode_msg(#sdl_send_register_event {
dst_mac = SrcMac,
nat_ip = sdlan_ipaddr:ipv4_to_int(SrcNatIp),
nat_ip = sdlan_util:ipv4_to_int(SrcNatIp),
nat_type = SrcNatType,
nat_port = SrcNatPort,
v6_info = SrcV6Info
@ -258,6 +262,15 @@ handle_call({peer_info, SrcMac, DstMac}, _From, State = #state{endpoints = Endpo
{reply, error, State}
end;
%% arp查询
handle_call({arp_request, TargetIp}, _From, State = #state{endpoints = Endpoints}) ->
case search_endpoint(fun(_, #endpoint{ip = Ip0}) -> Ip0 =:= TargetIp end, Endpoints) of
error ->
{reply, error, State};
{ok, Mac, _} ->
{reply, {ok, Mac}, 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,
@ -467,7 +480,7 @@ format_endpoint({Mac, #endpoint{client_id = ClientId, ip = Ip, hole = #hole{peer
#{
client_id => ClientId,
mac => sdlan_util:format_mac(Mac),
ip => sdlan_ipaddr:int_to_ipv4(Ip),
ip => sdlan_util:int_to_ipv4(Ip),
hole_map => HoleMap,
v6_info => V6InfoMap
}.

View File

@ -13,6 +13,8 @@
-export([rand_byte/1, md5/1, format_mac/1, assert_call/2]).
-export([json_data/1, json_error/2]).
-export([is_broadcast_mac/1, is_multicast_mac/1]).
-export([ipv4_to_int/1, int_to_ipv4/1, ips/2, format_ip/1]).
-export([ipv6_bytes_to_binary/1]).
-spec format_mac(Mac :: binary()) -> binary().
format_mac(Mac) when is_binary(Mac) ->
@ -72,3 +74,45 @@ is_broadcast_mac(Mac) when is_binary(Mac) ->
-spec is_multicast_mac(Mac :: binary()) -> boolean().
is_multicast_mac(Mac) when is_binary(Mac) ->
binary:part(Mac, 0, 3) =:= <<16#01,16#00,16#5E>>.
format_ip(Ip) when is_integer(Ip) ->
int_to_ipv4(Ip);
format_ip(Ip) ->
Ip.
-spec ipv4_to_int(Ip :: integer() | binary() | inet:ip4_address()) -> integer().
ipv4_to_int(Ip) when is_integer(Ip) ->
Ip;
ipv4_to_int({Ip0, Ip1, Ip2, Ip3}) ->
<<Ip:32>> = <<Ip0, Ip1, Ip2, Ip3>>,
Ip;
ipv4_to_int(Ip) when is_binary(Ip) ->
Parts0 = binary:split(Ip, <<".">>, [global]),
Parts = lists:map(fun binary_to_integer/1, Parts0),
<<IpInt:32>> = iolist_to_binary(Parts),
IpInt.
-spec int_to_ipv4(Ip :: integer()) -> binary().
int_to_ipv4(Ip) when is_integer(Ip) ->
<<Ip0, Ip1, Ip2, Ip3>> = <<Ip:32>>,
<<(integer_to_binary(Ip0))/binary, $., (integer_to_binary(Ip1))/binary, $., (integer_to_binary(Ip2))/binary, $., (integer_to_binary(Ip3))/binary>>.
-spec ips(NetAddr :: binary(), MaskLen :: integer()) -> [Ip :: integer()].
ips(NetAddr, MaskLen) when is_binary(NetAddr), is_integer(MaskLen) ->
Mask = 16#FFFFFFFF bsr MaskLen,
Net0 = ipv4_to_int(NetAddr),
%% : "192.168.1.101",
L = 32 - MaskLen,
Net = (Net0 bsr L) bsl L,
lists:map(fun(V) -> Net + V end, lists:seq(1, Mask - 1)).
-spec ipv6_bytes_to_binary(Bytes :: binary()) -> Bin :: binary().
ipv6_bytes_to_binary(<<A:16, B:16, C:16, D:16, E:16, F:16, G:16, H:16>>) ->
Segments = [integer_to_list(X, 16) || X <- [A, B, C, D, E, F, G, H]],
% 4
Padded = [string:pad(S, 4, leading, $0) || S <- Segments],
% IPv6地址格式
iolist_to_binary(lists:flatten(string:join(Padded, ":")));
ipv6_bytes_to_binary(_) ->
<<"">>.