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