-module(ls_iprange). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. -export([ create/8, delete/1, get/1, release/2, claim/1, list/2, list/3, stream/3 ]). -export([ name/2, uuid/2, network/2, netmask/2, gateway/2, set_metadata/2, tag/2, vlan/2, add_ip/2, remove_ip/2 ]). -export([ip_to_bin/1, ip_to_int/1]). %%%=================================================================== %%% Iprange Functions %%%=================================================================== %%-------------------------------------------------------------------- %% @doc Creates a new IP range on the server. All ips are passed as %% integer or binary. %% @end %%-------------------------------------------------------------------- -spec create(Iprange::binary(), Network::integer() | binary(), Gateway::integer() | binary(), Netmask::integer() | binary(), First::integer() | binary(), Last::integer() | binary(), Tag::binary(), Vlan::pos_integer()) -> {ok, UUID::fifo:iprange_id()} | duplicate | {'error', 'no_servers'}. create(Iprange, Network, Gateway, Netmask, First, Last, Tag, Vlan) when is_binary(Iprange), is_binary(Tag), is_integer(Network), %is_integer(Gateway), Network =:= (Gateway band Netmask), is_integer(Netmask), is_integer(First), % Network =:= (First band Netmask), is_integer(Last), % Network =:= (Last band Netmask), is_integer(Vlan), Vlan >= 0 -> send({iprange, create, Iprange, Network, Gateway, Netmask, First, Last, Tag, Vlan}); create(Iprange, Network, Gateway, Netmask, First, Last, Tag, Vlan) when is_binary(Iprange), is_binary(Tag), is_integer(Vlan), Vlan >= 0, is_binary(Network), is_binary(Gateway), is_binary(Netmask), is_binary(First), is_binary(Last) -> create(Iprange, ip_to_int(Network), ip_to_int(Gateway), ip_to_int(Netmask), ip_to_int(First), ip_to_int(Last), Tag, Vlan). %%-------------------------------------------------------------------- %% @doc Adds a specific IP to an iprange %% @end %%-------------------------------------------------------------------- -spec add_ip(Iprange::binary(), IP::integer() | binary()) -> ok | {'error', 'no_servers'}. add_ip(Iprange, IP) when is_binary(Iprange), is_binary(IP) -> add_ip(Iprange, ip_to_int(IP)); add_ip(Iprange, IP) when is_binary(Iprange), is_integer(IP) -> send({iprange, add_ip, Iprange, IP}). %%-------------------------------------------------------------------- %% @doc Removes a specific IP from an iprange %% @end %%-------------------------------------------------------------------- -spec remove_ip(Iprange::binary(), IP::integer() | binary()) -> ok | {'error', 'no_servers'}. remove_ip(Iprange, IP) when is_binary(Iprange), is_binary(IP) -> remove_ip(Iprange, ip_to_int(IP)); remove_ip(Iprange, IP) when is_binary(Iprange), is_integer(IP) -> send({iprange, remove_ip, Iprange, IP}). %%-------------------------------------------------------------------- %% @doc Deletes a iprange from the server. %% @end %%-------------------------------------------------------------------- -spec delete(Iprange::binary()) -> ok | not_found | {'error', 'no_servers'}. delete(Iprange) -> send({iprange, delete, Iprange}). %%-------------------------------------------------------------------- %% @doc Reads all the info of a iprange from the server. %% @end %%-------------------------------------------------------------------- -spec get(Iprange::binary()) -> not_found | {ok, fifo:iprange()} | {'error', 'no_servers'}. get(Iprange) -> send({iprange, get, Iprange}). %%-------------------------------------------------------------------- %% @doc Tries to release a claimed ip address from a range. %% @end %%-------------------------------------------------------------------- -spec release(Iprange::fifo:iprange_id(), Ip::integer()) -> ok | not_found | {'error', 'no_servers'}. release(Iprange, Ip) when is_binary(Iprange), is_integer(Ip) -> send({iprange, release, Iprange, ip_to_int(Ip)}); release(Iprange, Ip) when is_binary(Iprange) -> release(Iprange, ip_to_int(Ip)). %%-------------------------------------------------------------------- %% @doc Claims a ipaddress and returns the address and the relevant %% network information. %% @end %%-------------------------------------------------------------------- -spec claim(Iprange::fifo:iprange_id()) -> not_found | {ok, {Tag::binary(), IP::pos_integer(), Netmask::pos_integer(), Gateway::pos_integer(), VLan::non_neg_integer()}} | {error, failed} | {'error', 'no_servers'}. claim(Iprange) -> send({iprange, claim, Iprange}). -define(HS(F), F(DTRace, Val) -> send({iprange, F, DTRace, Val})). ?HS(name). ?HS(uuid). ?HS(network). ?HS(netmask). ?HS(gateway). ?HS(set_metadata). ?HS(tag). ?HS(vlan). %%-------------------------------------------------------------------- %% @doc Lists all ip ranges known to the system filtered by %% given matchers. %% @end %%-------------------------------------------------------------------- -spec list(Reqs::[fifo:matcher()], boolean()) -> {ok, [{Ranking::integer(), ID::fifo:iprange_id()}]} | {ok, [{Ranking::integer(), ID::fifo:iprange()}]} | {'error', 'no_servers'}. list(Reqs, Full) -> list(mdns, Reqs, Full). list(Sniffle, Reqs, Full) -> send(Sniffle, {iprange, list, Reqs, Full}). %%-------------------------------------------------------------------- %% @doc Streams the IPRANGE's in chunks. %% @end %%-------------------------------------------------------------------- -spec stream(Reqs::[fifo:matcher()], mdns_client_lib:stream_fun(), term()) -> {ok, [{Ranking::integer(), fifo:iprange_id()}]} | {ok, [{Ranking::integer(), fifo:iprange()}]} | {'error', 'no_servers'}. stream(Reqs, StreamFn, Acc0) -> case libsniffle_server:stream({iprange, stream, Reqs}, StreamFn, Acc0) of {reply, Reply} -> Reply; noreply -> ok; E -> E end. %%%=================================================================== %%% Utility functions %%%=================================================================== %%-------------------------------------------------------------------- %% @doc This function convers an integer to the human readable ip %% format as
AAA.BBB.CCC.DDD
. %% @end %%-------------------------------------------------------------------- -spec ip_to_bin(integer()) -> binary(). ip_to_bin(IP) -> ft_iprange:to_bin(IP). %%-------------------------------------------------------------------- %% @doc This function an human readable ip format %%
AAA.BBB.CCC.DDD
to an integer. %% @end %%-------------------------------------------------------------------- -spec ip_to_int(integer()|string()|binary()) -> integer(). ip_to_int(IP) when is_integer(IP) -> IP; ip_to_int(IP) -> ft_iprange:parse_bin(IP). %%%=================================================================== %%% Internal Functions %%%=================================================================== -spec send(MSG::fifo:sniffle_iprange_message()) -> ok | atom() | {ok, Reply::term()} | {error, no_servers}. send(Msg) -> send(mdns, Msg). send(Sniffle, Msg) -> case libsniffle_server:send(Sniffle, Msg) of {reply, Reply} -> Reply; noreply -> ok; E -> E end. %%%=================================================================== %%% Tests %%%=================================================================== -ifdef(TEST). ip_convert_test() -> IP1 = <<"192.168.0.0">>, ?assertEqual(ip_to_bin(ip_to_int(IP1)), IP1). ip_to_int_test() -> ?assertEqual(ip_to_int("255.255.255.255"), 16#FFFFFFFF), ?assertEqual(ip_to_int("0.255.255.255"), 16#00FFFFFF), ?assertEqual(ip_to_int("255.0.255.255"), 16#FF00FFFF), ?assertEqual(ip_to_int("255.255.0.255"), 16#FFFF00FF), ?assertEqual(ip_to_int("255.255.255.0"), 16#FFFFFF00), ?assertEqual(ip_to_int("255.255.255.240"), 16#FFFFFFF0). ip_to_bin_test() -> ?assertEqual(ip_to_bin(16#FFFFFFFF), <<"255.255.255.255">>), ?assertEqual(ip_to_bin(16#00FFFFFF), <<"0.255.255.255">>), ?assertEqual(ip_to_bin(16#FF00FFFF), <<"255.0.255.255">>), ?assertEqual(ip_to_bin(16#FFFF00FF), <<"255.255.0.255">>), ?assertEqual(ip_to_bin(16#FFFFFF00), <<"255.255.255.0">>), ?assertEqual(ip_to_bin(16#FFFFFFF0), <<"255.255.255.240">>). -endif.