Mikrotik Neighbor Discovery (MNDP)

Mikrotik Neighbor Discovery (MNDP) (C++/Qt)

For all that are interestet in discovering Mikrotik devices in their own programs here is an example how to do, written in C++/Qt

First we send out some UDP packages to request Mikrotik answers:

QByteArray datagram;
datagram.resize(4);
datagram[0] = 0x00;
datagram[1] = 0x00;
datagram[2] = 0x00;
datagram[3] = 0x00;

// Send a few packets to force Mikrotik Discovery
for(int i=0; i<3; i++)
··udpSocket->writeDatagram(datagram.data(), datagram.size(), QHostAddress::Broadcast, 5678);

After a while we get a UDP Packet back with the required information, we just need to parse it:

QByteArray datagram;
QHostAddress *address = new QHostAddress;
int datatoread = udpSocket->pendingDatagramSize();
char *datagramPtr = 0;
short len = 0;

QByteArray macAddr, identity, version, platform, uptime, softwareid, board, unpack, interface;

datagram.resize(datatoread);
udpSocket->readDatagram(datagram.data(), datatoread, address);

// MNDP packet has a bit size
if(datatoread < 18)
·· continue;

// Work with pointer
datagramPtr = datagram.data();

// Get MAC-address
len = 0;
len |= ((datagramPtr[6])&0xFF << 8) | ((datagramPtr[7])&0xFF);
macAddr.resize(len*2+5);
sprintf(macAddr.data(), “%02x:%02x:%02x:%02x:%02x:%02x”,
datagramPtr[8], datagramPtr[9], datagramPtr[10],
datagramPtr[11], datagramPtr[12], datagramPtr[13]);

// Get Identity
datagramPtr += 16;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
identity.resize(len);
strncpy(identity.data(), datagramPtr+2, len);

// Get Version
datagramPtr += len + 4;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
version.resize(len);
strncpy(version.data(), datagramPtr+2, len);

// Get Platform
datagramPtr += len + 4;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
platform.resize(len);
strncpy(platform.data(), datagramPtr+2, len);

// Get Uptime
datagramPtr += len + 4;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
uptime.resize(len);
strncpy(uptime.data(), datagramPtr+2, len);

// Get Software-ID
datagramPtr += len + 4;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
softwareid.resize(len);
strncpy(softwareid.data(), datagramPtr+2, len);

// Get Board
datagramPtr += len + 4;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
board.resize(len);
strncpy(board.data(), datagramPtr+2, len);

// Get Unpack
datagramPtr += len + 4;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
unpack.resize(len);
strncpy(unpack.data(), datagramPtr+2, len);

// Get Interface
datagramPtr += len + 4;
len = 0;
len |= ((datagramPtr[0])&0xFF << 8) | ((datagramPtr[1])&0xFF);
interface.resize(len);
strncpy(interface.data(), datagramPtr+2, len);

4 thoughts on “Mikrotik Neighbor Discovery (MNDP)

  1. Will

    Scraping MNDP packets from the network can be done with netcat: nc -ubl 255.255.255.255 5678 > receive.txt

    Reply
  2. Will

    //the comment below/above was truncated. Here’s the end of the file.

    $unpack = ord(substr($packet,$unpackStart+2,$unpackLen));

    $interfaceStart = $unpackStart + $unpackLen + 4;
    $interfaceLen = (((ord($packet[$interfaceStart]) & 0xFF) << 8 ) | ord($packet[$interfaceStart+1]) & 0xFF);
    $interface = substr($packet,$interfaceStart+2,$interfaceLen);

    echo "Mac Address: $mac ($macLen)\r\n";
    echo "Identity: $identity ($identityLen)\r\n";
    echo "Version: $version ($versionLen)\r\n";
    echo "Platform: $platform ($platformLen)\r\n";
    echo "Uptime: $uptime ($uptimeLen)\r\n";
    echo "Software: $software ($softwareLen)\r\n";
    echo "Board: $board ($boardLen)\r\n";
    echo "Unpack: $unpack ($unpackLen)\r\n";
    echo "Interface: $interface ($interfaceLen)\r\n";

    }

    Reply
  3. Will

    This page and the link above are the only searchable public definitions of the Mikrotik Network Discovery Protocol.
    Therefore, here’s some working PHP code that demonstrates both unpacking an incoming update from a mikrotik and creating a valid MNDP udp advertisement for future reference. This runs on php7.3 on a raspberry pi.

    #!/usr/bin/php
    receive.txt

    $packet = file_get_contents(‘./receive.txt’);
    unPackMndp($packet);

    //build a packet and write it to a local file.
    file_put_contents(‘./output.txt’,packMndp());

    //use socat to send that file as a UDP packet. neither PHP nor Netcat like sending UDP broadcast packets.
    exec(‘/usr/bin/socat FILE:./output.txt UDP-DATAGRAM:255.255.255.255:5678,broadcast’);

    //unPackMndp(file_get_contents(‘./output.txt’));

    function packMndp()
    {
    $identity = getHostname();

    $uptime = @file_get_contents( “/proc/uptime”);
    $uptime = explode(” “,$uptime);
    $uptime = intval($uptime[0]);

    $platform = “Platform”;
    $version = “Version”;
    $board = exec(“uname -m”);

    //these fields are not shown in the mikrotik UI.
    $software = “Software”;
    $interface = “Interface Name”;

    //The following code builds our packet payload byte-by-byte.

    // first four bytes copied from a valid MNDP packet
    $packet = chr(0);
    $packet .= chr(0);
    $packet .= chr(111);
    $packet .= chr(108);
    $packet .= chr(0);
    $packet .= chr(1);

    //mac length
    $packet .= chr((6 & 0xFF00) >> 8);
    $packet .= chr(6 & 0xFF);

    // a valid mac address. This is not the address displayed by mikrotiks; they use the mac address from the UDP header
    $packet .= chr(212);
    $packet .= chr(202);
    $packet .= chr(109);
    $packet .= chr(145);
    $packet .= chr(101);
    $packet .= chr(105);

    //padding
    $packet .= chr(0);
    $packet .= chr(5);

    //identity
    $packet .= chr((strlen($identity) & 0xFF00) >> 8);
    $packet .= chr(strlen($identity) & 0xFF);
    $packet .= $identity;
    //padding
    $packet .= chr(0);
    $packet .= chr(7);

    //version
    $packet .= chr((strlen($version) & 0xFF00) >> 8);
    $packet .= chr(strlen($version) & 0xFF);
    $packet .= $version;
    //padding
    $packet .= chr(0);
    $packet .= chr(8);

    //platform
    $packet .= chr((strlen($platform) & 0xFF00) >> 8);
    $packet .= chr(strlen($platform) & 0xFF);
    $packet .= $platform;
    //padding
    $packet .= chr(0);
    $packet .= chr(10);

    //uptime
    $packet .= chr((4 & 0xFF00) >> 8);
    $packet .= chr(4 & 0xFF);
    $packet .= pack(“V”,$uptime); //uptime in seconds as an unsigned long (32 bit, little endian byte order)
    //padding
    $packet .= chr(0);
    $packet .= chr(11);

    //software
    $packet .= chr((strlen($software) & 0xFF00) >> 8);
    $packet .= chr(strlen($software) & 0xFF);
    $packet .= $software;
    //padding
    $packet .= chr(0);
    $packet .= chr(12);

    //board
    $packet .= chr((strlen($board) & 0xFF00) >> 8);
    $packet .= chr(strlen($board) & 0xFF);
    $packet .= $board;
    //padding
    $packet .= chr(0);
    $packet .= chr(14);

    //unpack
    $packet .= chr((1 & 0xFF00) >> 8);
    $packet .= chr(1 & 0xFF);
    $packet .= chr(0);
    //padding
    $packet .= chr(0);
    $packet .= chr(16);

    //interface
    $packet .= chr((strlen($interface) & 0xFF00) >> 8);
    $packet .= chr(strlen($interface) & 0xFF);
    $packet .= $interface;

    return($packet);
    }

    //Mac address,identity,version,platform,uptime,softwareId,board,unpack,interface
    function unPackMndp($packet)
    {
    $macLen = (((ord($packet[6]) & 0xFF) << 8 ) | ord($packet[7]) & 0xFF);
    $mac = sprintf("%02x:%02x:%02x:%02x:%02x:%02x", ord($packet[8]) , ord($packet[9]) , ord($packet[10]) , ord($packet[11]) , ord($packet[12]) , ord($packet[13]));

    $identityLen = (((ord($packet[16]) & 0xFF) << 8 ) | ord($packet[17]) & 0xFF);
    $identity = substr($packet,18,$identityLen);

    $versionStart = 16 + $identityLen + 4;
    $versionLen = (((ord($packet[$versionStart]) & 0xFF) << 8 ) | ord($packet[$versionStart+1]) & 0xFF);
    $version = substr($packet,$versionStart+2,$versionLen);

    $platformStart = $versionStart + $versionLen + 4;
    $platformLen = (((ord($packet[$platformStart]) & 0xFF) << 8 ) | ord($packet[$platformStart+1]) & 0xFF);
    $platform = substr($packet,$platformStart+2,$platformLen);

    $uptimeStart = $platformStart + $platformLen + 4;
    $uptimeLen = (((ord($packet[$uptimeStart]) & 0xFF) << 8 ) | ord($packet[$uptimeStart+1]) & 0xFF);
    $uptime = unpack("VUptime",substr($packet,$uptimeStart+2,$uptimeLen))['Uptime'];

    $softwareStart = $uptimeStart + $uptimeLen + 4;
    $softwareLen = (((ord($packet[$softwareStart]) & 0xFF) << 8 ) | ord($packet[$softwareStart+1]) & 0xFF);
    $software = substr($packet,$softwareStart+2,$softwareLen);

    $boardStart = $softwareStart + $softwareLen + 4;
    $boardLen = (((ord($packet[$boardStart]) & 0xFF) << 8 ) | ord($packet[$boardStart+1]) & 0xFF);
    $board = substr($packet,$boardStart+2,$boardLen);

    $unpackStart = $boardStart + $boardLen + 4;
    $unpackLen = (((ord($packet[$unpackStart]) & 0xFF) << 8 ) | ord($packet[$unpackStart+1]) & 0xFF);
    $unpack = ord(substr($packet,$unpackStart+2,$unpackLen));

    $interfaceStart = $unpackStart + $unpackLen + 4;
    $interfaceLen = (((ord($packet[$interfaceStart]) & 0xFF) <

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *