View Source MNDP (mndp v0.1.2)

An Elixir implementation for the MikroTik Neighbor Discovery Protocol.

Discover devices

> mix mndp.discover

Press any key to end

┌───────┬──────┬────────────────┬─────────┬─────────────┬─────────┬───────────┐
│VERSION│UPTIME│MAC             │LAST SEEN│IPV4         │INTERFACE│IDENTITY   │
├───────┼──────┼────────────────┼─────────┼─────────────┼─────────┼───────────┤
│0.1.0  │4396  │6:95:72:B0:80:BB│0s ago   │172.31.199.73│usb0     │nerves-2a0c│
└───────┴──────┴────────────────┴─────────┴─────────────┴─────────┴───────────┘

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"}
  ]
end

To 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

t()

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

@spec decode(binary()) :: {:ok, t()} | {:error, atom()}

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}
@spec encode(t()) :: binary()

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

Link to this function

new(interface_or_ifname, overrides \\ [])

View Source
@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} 
Link to this function

new!(ifname, overrides \\ [])

View Source
@spec new!(String.t(), overrides()) :: t()

Same as MNDP.new/2 but raises on error

@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}
  end

If 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()