Current section
Files
Jump to
Current section
Files
lib/rns/interface.ex
defmodule RNS.Interface do
@moduledoc """
This is the module for the interface struct.
The interface struct contains data about the current interface. The struct can be fetched using the interface id.
"""
require Logger
require Record
Record.defrecord(:interface,
id: nil,
pid: nil,
mode: :full,
ifac: nil,
virtual: false
)
@type t() :: record() | id() | pid()
@type record() :: tuple()
@type id() :: String.t()
@type mode() :: :full | :access_point | :gateway | :roaming | :boundary
@type ifac() :: binary() | :open
@doc "Get the id of an interface record."
@spec id(record()) :: id()
def id(interface), do: interface(interface, :id)
@doc "Get the pid of an interface record."
@spec pid(record()) :: pid()
def pid(interface), do: interface(interface, :pid)
@doc "Get the mode of an interface record."
@spec mode(record()) :: mode()
def mode(interface), do: interface(interface, :mode)
@doc "Get the IFAC key of an interface record."
@spec ifac(record()) :: ifac()
def ifac(interface), do: interface(interface, :ifac)
@doc "See if an interface (record) is virtual or not."
@spec virtual(record()) :: boolean()
def virtual(interface), do: interface(interface, :virtual)
@doc "Get the position of the id in an interface record."
@spec id_position() :: non_neg_integer()
def id_position(), do: interface(:id)
@doc "Get the position of the pid in an interface record."
@spec pid_position() :: non_neg_integer()
def pid_position(), do: interface(:pid)
@doc """
Create a new "interface" record.
The parameters mode and ifac are optional.
"""
# Yes, it's a map and not a keyword list because these are parameters for the parsed TOML config.
@spec new(id(), pid(), mode: mode(), virtual: boolean()) :: t()
def new(id, pid, opts \\ []) do
# TODO: IFAC, check Reticulum.py:645 for detailed explanation, then somewhere in Packet.py ig.
ifac = nil
interface(
id: id,
pid: pid,
virtual: Keyword.get(opts, :virtual, false),
mode: Keyword.get(opts, :mode, :full),
ifac: ifac
)
end
@doc "Send a packet through an interface."
@spec send(RNS.Packet.t(), t() | nil, t() | nil) ::
:ok | {:error, :dead_interface} | [:ok | {:error, :dead_interface}]
def send(data, interface \\ nil, source \\ nil)
# When the interface id is set to nil, send to all interfaces.
def send(data, nil, source) do
for interface <- RNS.InterfaceManager.all(virtuals: false) do
__MODULE__.send(data, interface(interface, :pid), source)
end
end
# Try to resolve the interface into a pid.
def send(packet, interface_id, source) when is_binary(interface_id) do
case RNS.InterfaceManager.find(interface_id) do
{:ok, interface} -> __MODULE__.send(packet, interface, source)
{:error, :not_found} -> {:error, :interface_not_found}
end
end
def send(packet, interface, source) when Record.is_record(interface, :interface) do
interface_pid = interface(interface, :pid)
__MODULE__.send(packet, interface_pid, source)
end
# Send the packet.
def send(packet, pid, source) when is_pid(pid) do
if alive?(pid) do
Kernel.send(pid, {:inbound, packet, source})
:ok
else
{:error, :dead_interface}
end
end
@doc "Check if an interface's process is alive."
@spec alive?(RNS.Interface.t() | nil) :: boolean()
def alive?(interface_id) when is_binary(interface_id) do
case RNS.InterfaceManager.find(interface_id) do
{:ok, interface} -> alive?(interface)
{:error, :not_found} -> false
end
end
def alive?(interface) when Record.is_record(interface, :interface) do
alive?(interface(interface, :pid))
end
def alive?(pid) when is_pid(pid), do: Process.alive?(pid)
def alive?(nil), do: false
end