Hello everyone,
I have an idea for this year's Summer of Code, so I figured I should share it
early with you guys. I'm hoping for a chance to work on Zenmap this summer.
I was doing some thinking/research about graphical network topology mapping (or
a lack thereof) and how cool it would be if Zenmap had that functionality, so I
came up with a simple algorithm and implemented it in Python. I've attached the
source code (disclaimer: it's a quick hack), so I'll continue with a brief
explanation of the basic ideas behind it.
First, a user supplies an IP address (or an IP range) which is our
"destination", and the prefix length (PLEN), which is used for standard
CIDR-style subnet scanning (performed later). All IP addresses are added to the
"to be processed" (TBP) list.
For every address ADDR in the TBP list, do the following:
* Run a traceroute to ADDR, and add any newly-discovered intermediate hosts
to the TBP list. Update the topology graph using traceroute's data to add new
nodes and construct edges between nodes.
* Run a nmap scan on ADDR/PLEN (standard CIDR-style designation) and add any
discovered hosts to the TBP list.
When there are no addresses left in the TBP list, the construction of the
topology graph is "finished". I say "finished" here because, of course, a user
can only see the portion of the net she scanned. What I thought would be cool
(but didn't implement it) is that a user should be able to manually expand the
constructed topology once the algorithm finishes its run (that's the TODO on
stinkfist.py:85). For example, she knows that there's a live host (or hosts) on
the local LAN that wasn't detected because PLEN was too big.
There are also some other ideas that can be implemented, for example if a node
has more than two edges connected to it, then it's probably a router. Also I
had some thoughts about the starting node - maybe it doesn't have to be the
localhost, so we can simulate being on some other machine and starting the scan
from there. The possibilities are endless... ;)
As for the code, I used (py)graphviz to represent the topology graph and plot
it to an SVG image. You can use "stinkfist.py dst TARGET PLEN" (note the "dst"
there) or "stinkfist.py dst TARGET" (PLEN = 29, default) to generate the
topology file for graphviz (in the form TARGET_PLEN.dot). You can then use
"draw.py FILENAME" to convert the .dot file into an .svg image (Firefox can
open it). Also attached is a sample scan of my ISP's subnet with PLEN = 29,
converted to a GIF image. I've blurred out the first two octets of IP addresses
for privacy reasons.
So guys, any input is welcome, and please let me know if I'm reinventing the
wheel here. I'm looking forward to discussing this with you.
Cheers,
Vladimir
p.s. I apologize if the name "stinkfist" sounds bad, it's just a working title.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import re
import pygraphviz
default_plen = 29 # default
topology = pygraphviz.AGraph()
startaddr = "localhost" # TODO get a real local IP (ifconfig, whatever)
def traceroute(dest):
addrlist = []
tr_out = os.popen("traceroute -I -n " + dest + " 2> /dev/null", "r")
for line in tr_out:
# find the first IP address in the current line
addr = re.findall(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', line)
if addr != []: addrlist.append(addr[0])
return addrlist
def scan_subnet(subnet):
addrlist = []
nmap_out = os.popen("nmap -sP " + subnet + " 2> /dev/null", "r")
for line in nmap_out:
addr = re.findall(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', line)
if addr != []: addrlist.append(addr[0])
return addrlist
def destination_based(dest, plen):
ip_list = [] # list of not-yet-processed nodes
result_list = [] # list of processed nodes
### 1. traceroute to the destination address
print "initial traceroute run:\n " + startaddr,
for addr in traceroute(dest):
print "-> " + addr,
ip_list.append(addr)
print
# add these nodes to the topology graph
if len(ip_list) > 0: topology.add_edge(startaddr, ip_list[0]) # cheat
for i in range(0, len(ip_list)-1):
topology.add_edge(ip_list[i], ip_list[i+1])
### 2.
for addr in ip_list:
result_list.append(addr)
### 2.1 traceroute to the current node, add any new nodes to ip_list
print "processing " + addr
lastaddr = startaddr
for newaddr in traceroute(addr):
if newaddr not in result_list and newaddr not in ip_list:
ip_list.append(newaddr)
# add the new node to the topology graph
topology.add_edge(lastaddr, newaddr)
lastaddr = newaddr
### 2.2 scan this host's subnet, using a given host width
for newaddr in scan_subnet(addr + "/" + str(plen)):
if newaddr not in result_list and newaddr not in ip_list:
ip_list.append(newaddr)
### 2.3
### 2.4
#ip_list.remove(addr)
# save the topology into a .dot file
tfile = dest + "_" + str(plen) + ".dot"
topology.write(tfile)
print "written topology into " + tfile
def incremental():
# TODO implement
pass
def single_node():
# TODO implement
pass
if __name__ == "__main__":
if sys.argv[1] == "dst":
if len(sys.argv) == 4:
destination_based(sys.argv[2], sys.argv[3])
else:
destination_based(sys.argv[2], default_plen)
elif sys.argv[1] == "inc":
incremental()
elif sys.argv[1] == "scn":
scan_subnet(sys.argv[2])
elif sys.argv[1] == "sgl":
single_node()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pygraphviz import *
import re
import sys
format = "svg"
layout = "dot"
#layout = "circo"
A = AGraph()
A.read(sys.argv[1])
A.layout(layout)
A.graph_attr["label"]=sys.argv[1]
A.node_attr["shape"] = "circle"
# put the format extension instead of the original extension and draw
A.draw(re.sub(r'\.\w+$', '', sys.argv[1]) + "." + format)
_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org
Received on Mar 11 2008