The OpenLANscape ProjectThe LANout Algorithm
Author: Maciek Kolbusz Overview
LANout is the algorithm which creates a 3-dimentional network topology layout. The name comes from the combination of LAN and layout.
It's designed for SNMP enabled LANs, as MIBs are good sources of informations needed to recreate physical topology of LAN.
The matter of network discovery and scanning won't be covered by this article. There's another project called
WiR3D which uses this algorithm together with PING/SNMP scanner to provide a complete toolkit for LAN topology mapping.
Input Objects
Subnet - a logical subnet identified by integer ID. ![]() Output Objects
Wirea - wired/wireless network area; a set of subnets with belonging nodes and relations between them. class WiREA { public: WiREA(); ~WiREA(); void openDB( char* ); void draw(); private: int Root; // root node id std::map< int, int > Nodes; // id, type std::map< int, std::list The process
Add all nodes to Nodes, so each node is represented by a pair of intergers (id, type). void WiREA::openDB( char *name ) { Database db( name ); if ( db.Connected() ) { Query q( db ); q.get_result( "SELECT idNode, nodeType FROM Nodes" ); while ( q.fetch_row() ) { int id = q.getval(); int type = q.getval(); Nodes[ id ] = type; if ( type == 0 ) Root = id; } q.free_result();
Add all subnets to Subnets. Each element of Subnets is a pair of subnet ID and a list of child nodes. Note that every node with more than one network interface is considered as a router and is present in as many subnets as many interfaces it has. q.get_result( "SELECT idSubnet FROM Subnets" ); while( q.fetch_row() ) { int netID = q.getval(); std::list< int > temp; std::stringstream stmt; stmt << "SELECT idNode FROM Nodes " << "JOIN Interfaces ON Nodes.idNode = Interfaces.belongsToNode " << "WHERE Interfaces.belongsToSubnet = " << netID; Query q2( db ); q2.get_result( stmt.str() ); while( q2.fetch_row() ) { temp.push_back( q2.getval() ); } q2.free_result(); Subnets[ netID ] = temp; } q.free_result();
Add all FDB entries to FDBs. Each element of FDBs is a pair of switch ID and a list of connected nodes. Node IDs are found by MAC addresses of their interfaces. Node is directly connected to a switch, if the assigned port number appears in FDB only once, so after FDB is purged from entries with non-unique port numbers directly connected nodes (or more precisely: MAC addresses of their interfaces) are left. for ( std::map< int, int >::iterator i = Nodes.begin(); i != Nodes.end(); i++ ) { if( i->second == 1 ) { // node is a switch std::multimap< int, int > port_map; std::multimap< int, int >::iterator j = port_map.begin(); std::stringstream stmt; stmt << "SELECT Nodes.idNode, FDBs.portNum FROM FDBs " << "JOIN Interfaces ON FDBs.MACaddress = Interfaces.MACaddress " << "JOIN Nodes ON Interfaces.belongsToNode = Nodes.idNode " << "WHERE FDBs.belongsTo = " << i->first << " ORDER BY FDBs.portNum"; q.get_result( stmt.str() ); while( q.fetch_row() ) { port_map.insert( port_map.end(), std::pair< int, int >(q.getval(), q.getval()) ); } j = port_map.begin(); while( j != port_map.end() ) { if( port_map.count(j->first) > 1 ) port_map.erase(j->first); else j++; } // save the results std::list< int > node_list; for( j = port_map.begin(); j != port_map.end(); j++ ) { node_list.push_back( j->second ); } FDBs[ i->first ] = node_list; q.free_result(); port_map.clear(); node_list.clear(); } }
Create virtual hubs if: int hc = 0; // hub counter for ( std::map< int, std::list
Assign neighbours to routers: for ( std::map< int, std::list
Having all of the above ready, the layout can be drawn: void WiREA::draw() { // Create Drawn map for ( std::map< int, int >::iterator i = Nodes.begin(); i != Nodes.end(); i++ ) { Drawn[ i->first ] = 0; } drawNode( 0 ); drawNeighbours( Root ); } void WiREA::drawNeighbours( int node ) { Drawn[ node ] = 1; if ( Nodes[ node ] == 0 or Nodes[ node ] == 2 ) { int j = 0; for ( std::list< int >::iterator i = Neighbours[ node ].begin(); i != Neighbours[ node ].end(); i++ ) { if ( Drawn[ *i ] == 0 ) { glPushMatrix(); glRotatef( j * 20.0, 0.0, 1.0, 0.0 ); glColor3f( 0.0, 1.0, 0.0 ); glBegin( GL_LINES ); glVertex3f ( 0.0, 0.0, 0.0 ); glVertex3f ( 2.0, 0.0, 0.0 ); glEnd(); glTranslatef( 2.0, 0.0, 0.0 ); drawNode( Nodes[ *i ] ); drawNeighbours( *i ); glPopMatrix(); j++; } } } else if ( Nodes[ node ] == 1 or Nodes[ node ] == 4 ) { int j = 0; for ( std::list< int >::iterator i = FDBs[ node ].begin(); i != FDBs[ node ].end(); i++ ) { if ( Drawn[ *i ] == 0 ) { glPushMatrix(); glRotatef( j * 20.0, 0.0, 1.0, 0.0 ); glColor3f( 0.0, 1.0, 0.0 ); glBegin( GL_LINES ); glVertex3f ( 0.0, 0.0, 0.0 ); glVertex3f ( 2.0, 1.0 * ( j / 18 ), 0.0 ); glEnd(); glTranslatef( 2.0, 1.0 * ( j / 18 ), 0.0 ); drawNode( Nodes[ *i ] ); drawNeighbours( *i ); glPopMatrix(); j++; } } j = 1; for ( std::list< int >::iterator i = Stacks[ Bridges[node] ].begin(); i != Stacks[ Bridges[node] ].end(); i++ ) { if ( Drawn[ *i ] == 0 ) { glPushMatrix(); glColor3f( 0.0, 1.0, 0.0 ); glBegin( GL_LINES ); glVertex3f ( 0.0, 0.0, 0.0 ); glVertex3f ( 0.0, j / 18 + 2.0, 0.0 ); glEnd(); glTranslatef( 0.0, j / 18 + 2.0, 0.0 ); drawNode( Nodes[ *i ] ); drawNeighbours( *i ); glPopMatrix(); j++; } } } } |