From 6001ec227c32d1c67792accf0a4713aa4bf60bfa Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Thu, 16 Apr 2026 11:16:22 +0800 Subject: [PATCH] add ipv6 support --- config/sys-dev.config | 5 + config/sys-prod.config | 5 + src/ipv6_assistor/ipv6_assistor_server.erl | 119 ++++++++++++++++++ .../ipv6_assistor_server_sup.erl | 35 ++++++ src/sdlan_sup.erl | 10 +- 5 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 src/ipv6_assistor/ipv6_assistor_server.erl create mode 100644 src/ipv6_assistor/ipv6_assistor_server_sup.erl diff --git a/config/sys-dev.config b/config/sys-dev.config index 57dec49..2e75e21 100644 --- a/config/sys-dev.config +++ b/config/sys-dev.config @@ -38,6 +38,11 @@ {port, 1366} ]}, + {ipv6_assistor, [ + {port, 1367}, + {acceptor_nums, 5} + ]}, + {pools, [ %% mysql连接池配置 {mysql_sdlan, diff --git a/config/sys-prod.config b/config/sys-prod.config index 697b3d2..294fa97 100644 --- a/config/sys-prod.config +++ b/config/sys-prod.config @@ -38,6 +38,11 @@ {port, 1366} ]}, + {ipv6_assistor, [ + {port, 1367}, + {acceptor_nums, 5} + ]}, + {pools, [ %% mysql连接池配置 {mysql_sdlan, diff --git a/src/ipv6_assistor/ipv6_assistor_server.erl b/src/ipv6_assistor/ipv6_assistor_server.erl new file mode 100644 index 0000000..81763e6 --- /dev/null +++ b/src/ipv6_assistor/ipv6_assistor_server.erl @@ -0,0 +1,119 @@ +%%%------------------------------------------------------------------- +%%% @doc IPv6 UDP listener for connectivity checks. +%%% @end +%%%------------------------------------------------------------------- +-module(ipv6_assistor_server). + +-include("sdlan.hrl"). +-include("sdlan_pb.hrl"). + +-behaviour(gen_server). + +%% API +-export([start_link/2]). +-export([get_name/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-record(state, { + socket +}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +-spec get_name(Id :: integer()) -> atom(). +get_name(Id) when is_integer(Id) -> + list_to_atom("ipv6_assistor_server:" ++ integer_to_list(Id)). + +-spec start_link(Name :: atom(), Port :: integer()) -> + {ok, Pid :: pid()} | ignore | {error, Reason :: term()}. +start_link(Name, Port) when is_atom(Name), is_integer(Port) -> + gen_server:start_link({local, Name}, ?MODULE, [Port], []). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +-spec init(Args :: term()) -> + {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | + {stop, Reason :: term()} | ignore. +init([Port]) -> + erlang:process_flag(priority, high), + Opts = [ + binary, + inet6, + {reuseaddr, true}, + {reuseport, true}, + {active, true}, + {recbuf, 5 * 1024 * 1024}, + {sndbuf, 5 * 1024 * 1024} + ], + {ok, Socket} = gen_udp:open(Port, Opts), + inet_udp:controlling_process(Socket, self()), + + logger:debug("[ipv6_assistor_server] start at port: ~p", [Port]), + {ok, #state{socket = Socket}}. + +-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 = #state{}) -> + {reply, ok, State}. + +-spec handle_cast(Request :: term(), State :: #state{}) -> + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), NewState :: #state{}}. +handle_cast(_Request, State) -> + {noreply, State}. + +-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, <>}, State = #state{socket = Sock}) -> + case catch sdlan_pb:decode_msg(Body, 'SDLRegister') of + #'SDLRegister'{network_id = NetworkId, src_mac = SrcMac, dst_mac = DstMac} -> + Reply = sdlan_pb:encode_msg(#'SDLRegisterAck'{ + network_id = NetworkId, + src_mac = SrcMac, + dst_mac = DstMac + }), + ok = gen_udp:send(Sock, Ip, Port, <>), + logger:debug("[ipv6_assistor_server] reply register_ack, network_id: ~p, src_mac: ~p, dst_mac: ~p", [ + NetworkId, + sdlan_util:format_mac(SrcMac), + sdlan_util:format_mac(DstMac) + ]); + Reason -> + logger:warning("[ipv6_assistor_server] decode register packet error: ~p", [Reason]) + end, + {noreply, State}; +handle_info({udp, Sock, Ip, Port, <>}, State = #state{socket = Sock}) -> + ok = gen_udp:send(Sock, Ip, Port, <>), + {noreply, State}; +handle_info({udp, _Sock, Ip, Port, Packet}, State) -> + logger:debug("[ipv6_assistor_server] ignore packet from ~p:~p, size: ~p", [Ip, Port, byte_size(Packet)]), + {noreply, State}; +handle_info(Info, State) -> + logger:debug("[ipv6_assistor_server] ignore message: ~p", [Info]), + {noreply, State}. + +-spec terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), + State :: #state{}) -> term(). +terminate(_Reason, _State = #state{}) -> + ok. + +-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}. diff --git a/src/ipv6_assistor/ipv6_assistor_server_sup.erl b/src/ipv6_assistor/ipv6_assistor_server_sup.erl new file mode 100644 index 0000000..a037e00 --- /dev/null +++ b/src/ipv6_assistor/ipv6_assistor_server_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc ipv6_assistor top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(ipv6_assistor_server_sup). + +-behaviour(supervisor). + +-export([start_link/0]). +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +init([]) -> + SupFlags = #{strategy => one_for_one, intensity => 1000, period => 3600}, + {ok, Props} = application:get_env(sdlan, ipv6_assistor), + Port = proplists:get_value(port, Props, 1367), + AcceptorNum = proplists:get_value(acceptor_nums, Props, 5), + + Specs = lists:map(fun(Id) -> + Name = ipv6_assistor_server:get_name(Id), + #{ + id => Name, + start => {ipv6_assistor_server, start_link, [Name, Port]}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => ['ipv6_assistor_server'] + } + end, lists:seq(1, AcceptorNum)), + {ok, {SupFlags, Specs}}. diff --git a/src/sdlan_sup.erl b/src/sdlan_sup.erl index 2134202..9c43d19 100644 --- a/src/sdlan_sup.erl +++ b/src/sdlan_sup.erl @@ -38,6 +38,14 @@ init([]) -> type => supervisor, modules => ['dns_server_sup'] }, + #{ + id => ipv6_assistor_server_sup, + start => {ipv6_assistor_server_sup, start_link, []}, + restart => permanent, + shutdown => 2000, + type => supervisor, + modules => ['ipv6_assistor_server_sup'] + }, #{ id => sdlan_network_coordinator, start => {sdlan_network_coordinator, start_link, []}, @@ -109,4 +117,4 @@ pools() -> {ok, Pools} = application:get_env(sdlan, pools), lists:map(fun({Name, PoolArgs, WorkerArgs}) -> poolboy:child_spec(Name, [{name, {local, Name}}|PoolArgs], WorkerArgs) - end, Pools). \ No newline at end of file + end, Pools).