MNDP (mndp v0.2.0)
View SourceAn Elixir implementation for the MikroTik Neighbor Discovery Protocol.
Discover devices
> mix mndp.discover
Press enter to end
| identity | mac | ip_v4 | ifname | version | uptime | seen |
| ----------- | ----------------- | ------------- | ------ | ------- | ------ | ------- |
| nerves-fe79 | CE:6B:2A:1C:3A:7F | 172.31.154.53 | usb0 | 0.1.0 | 53s | 16s ago |
The application is automatically started and listening and broadcasting on all available IPv4 network interfaces. You can restrict the interfaces via config. See MNDP.Options. To use it just add the dependency to your project.
def deps do
[
{:mndp, "~> 0.1.0"}
]
endTo get the last discovered devices you can use MNDP.Listener.list_discovered/0.
You can decode and encode from and to a binary directly.
Encoding:
iex> MNDP.new!("en0") |> MNDP.encode()
<<...>>Decoding:
iex> MNDP.decode(binary)
{:ok, %MNDP{}}
Summary
Types
representation of a hardware MAC address
field overrides for the MNDP struct generation
a MNDP packet struct
seconds since start
Functions
Decdes a binary to a MNDP struct.
Ecnodes an MNDP stuct to a binary
List all the discovered devices withing configured TTL. See MNDP.Options for informations about how to modify the default settings
Creates a new MNDP struct from an interface name or struct.
Same as MNDP.new/2 but raises on error
Will print the devices from list_discovered/0 via IO.puts
Subscribe the current process to new MNDP packets from the listener. It will send a message with the type {:mndp, MNDP.t()}
Types
@type mac() :: [byte()]
representation of a hardware MAC address
@type overrides() :: [ identity: String.t(), version: String.t(), platform: String.t(), uptime: (-> uptime()), software_id: String.t(), board: String.t() ]
field overrides for the MNDP struct generation
@type t() :: %MNDP{ board: String.t(), identity: String.t(), interface: String.t(), ip_v4: :inet.ip4_address(), ip_v6: :inet.ip6_address(), last_seen: DateTime.t(), mac: mac(), platform: String.t(), seq_no: non_neg_integer(), software_id: String.t(), ttl: integer(), type: integer(), unpack: :none | nil, uptime: uptime(), version: String.t() }
a MNDP packet struct
@type uptime() :: non_neg_integer()
seconds since start
Functions
Decdes a binary to a MNDP struct.
The structure of a MNDP packet looks like this
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TYPE | TTL | SEQ |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TLV TYPE | TLV LENGTH |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ TLV DATA +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- TYPE (8 bytes)
- TTL (8 bytes)
- SEQ (16 bytes)
- TLV TYPE (16 bytes)
- TLV LENGTH (16 bytes)
- TLV DATA (TLV LENGTH)
Info
The header fields are set like this in RouterOS 6 but changes in RouterOS 7. Currently it is not guaranteed to be correct
Examples
iex> MNDP.decode(<<_>>)
{:ok, %MNDP{}}
iex> MNDP.decode(<<"unknown">>)
{:error, :expected_tlv_format}
Ecnodes an MNDP stuct to a binary
Examples
iex> MNDP.encode(%MNDP{})
<<...>>
@spec list_discovered() :: [t()]
List all the discovered devices withing configured TTL. See MNDP.Options for informations about how to modify the default settings
@spec new(MNDP.Interface.t() | String.t(), overrides()) :: t() | {:error, atom()}
Creates a new MNDP struct from an interface name or struct.
Examples
iex> MNDP.new("en0")
{:ok, %MNDP{}}
iex> MNDP.new("unknown interface")
{:error, :interface_not_found}
Same as MNDP.new/2 but raises on error
@spec print_discovered(nil | MNDP.Render.t()) :: MNDP.Render.t()
Will print the devices from list_discovered/0 via IO.puts
@spec subscribe() :: :ok
Subscribe the current process to new MNDP packets from the listener. It will send a message with the type {:mndp, MNDP.t()}
Ususally you do this in a process like a GenServer and the message can be handled like this
def handle_info({:mndp, %MNDP{} = mndp}, state) do
IO.inspect(mndp)
{:noreply, state}
endIf you subscribe from an IEx shell you can flush the received messages with IEx.Helpers.flush/0. It is automatically imported and can be called with just flush()