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);
Scraping MNDP packets from the network can be done with netcat: nc -ubl 255.255.255.255 5678 > receive.txt
//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";
}
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) <
C# lang solution for Mikrotik Dicovery
https://github.com/xmegz/MndpTray