diff --git a/tools/python/scanner.py b/tools/python/scanner.py
index a926a888d867ab6688ae19a40b593b979d4ad7a5..3c919825030bef7f44cf0caecfaf173fbae22bc7 100644
--- a/tools/python/scanner.py
+++ b/tools/python/scanner.py
@@ -9,6 +9,7 @@ import matplotlib.pyplot as plt
 import matplotlib.patches as mpatches
 from matplotlib.colors import colorConverter
 from matplotlib.collections import RegularPolyCollection
+from matplotlib.widgets import Button
 from mpl_toolkits.basemap import Basemap
 
 import GeoIP
@@ -16,15 +17,13 @@ import GeoIP
 done = 0
 all_nodes = PyNodeSet()
 
-gi = GeoIP.open("GeoLiteCity.dat", GeoIP.GEOIP_INDEX_CACHE | GeoIP.GEOIP_CHECK_CACHE)
-gi6 = GeoIP.open("GeoLiteCityv6.dat", GeoIP.GEOIP_INDEX_CACHE | GeoIP.GEOIP_CHECK_CACHE)
-
 plt.ion()
 plt.figaspect(2.)
 
 fig, axes = plt.subplots(2, 1)
 fig.set_size_inches(12,16,forward=True)
 fig.tight_layout()
+fig.canvas.set_window_title('OpenDHT scanner')
 
 mpx = axes[0]
 mpx.set_title("Node GeoIP")
@@ -43,38 +42,50 @@ ringx.set_aspect('equal', 'datalim')
 ringx.set_xlim(-2.,2.)
 ringx.set_ylim(-1.5,1.5)
 
+exitax = plt.axes([0.92, 0.95, 0.07, 0.04])
+exitbtn = Button(exitax, 'Exit')
+reloadax = plt.axes([0.92, 0.90, 0.07, 0.04])
+button = Button(reloadax, 'Reload')
+
+gi = GeoIP.open("GeoLiteCity.dat", GeoIP.GEOIP_INDEX_CACHE | GeoIP.GEOIP_CHECK_CACHE)
+gi6 = GeoIP.open("GeoLiteCityv6.dat", GeoIP.GEOIP_INDEX_CACHE | GeoIP.GEOIP_CHECK_CACHE)
+
 def gcb(v):
     return True
 
 r = PyDhtRunner()
 i = PyIdentity()
-i.generate()
+i.generate(bits = 1024)
 
 r.run(i, port=4112)
 r.bootstrap("bootstrap.ring.cx", "4222")
 
+all_lines = []
+
 plt.pause(2)
 
 def step(cur_h, cur_depth):
-    global done, all_nodes
+    global done, all_nodes, all_lines
     done += 1
     a = 2.*pi*cur_h.toFloat()
     b = a + 2.*pi/(2**(cur_depth))
     print("step", cur_h, cur_depth)
     arc = ringx.add_patch(mpatches.Wedge([0.,0,], 1., a*180/pi, b*180/pi, fill=True, color="blue", alpha=0.5))
     lines = ringx.plot([0, cos(a)], [0, sin(a)], 'k-', lw=1.2)
+    all_lines.extend(lines)
     r.get(cur_h, gcb, lambda d, nodes: nextstep(cur_h, cur_depth, d, nodes, arc=arc, lines=lines))
 
 def nextstep(cur_h, cur_depth, ok, nodes, arc=None, lines=[]):
     global done, all_nodes
     if arc:
         arc.remove()
+        del arc
     for l in lines:
         l.set_color('#444444')
     snodes = PyNodeSet()
     snodes.extend(nodes)
     all_nodes.extend(nodes)
-    depth = min(8, PyInfoHash.commonBits(snodes.first(), snodes.last())+6)
+    depth = min(6, PyInfoHash.commonBits(snodes.first(), snodes.last())+4)
     if cur_depth < depth:
         for b in range(cur_depth, depth):
             new_h = PyInfoHash(cur_h.toString());
@@ -82,61 +93,137 @@ def nextstep(cur_h, cur_depth, ok, nodes, arc=None, lines=[]):
             step(new_h, b+1);
     done -= 1
 
-# start first step
-start_h = PyInfoHash()
-start_h.setBit(159, 1)
-step(start_h, 0)
+run = True
+def exitcb(arg):
+    global run
+    run = False
+exitbtn.on_clicked(exitcb)
+
+def restart(arg):
+    global collection, all_lines, points
+    for l in all_lines:
+        l.remove()
+        del l
+    all_lines = []
+    if collection:
+        collection.remove()
+        del collection
+        collection = None
+    for p in points:
+        p.remove()
+        del p
+    points = []
+
+    print(arg)
+    start_h = PyInfoHash()
+    start_h.setBit(159, 1)
+    step(start_h, 0)
+    plt.draw()
 
 collection = None
+points = []
 not_found = []
 
+def generate_set():
+    node_ipv4 = {}
+    node_ipv6 = {}
+    for n in all_nodes:
+        addr = b''.join(n.getNode().getAddr().split(b':')[0:-1]).decode()
+        if addr[0] == '[':
+            addr = addr[1:-1]
+            if addr in node_ipv6:
+                node_ipv6[addr][1] = 1
+            else:
+                node_ipv6[addr] = [n, 1]
+        else:
+            if addr in node_ipv4:
+                node_ipv4[addr][1] += 1
+            else:
+                node_ipv4[addr] = [n, 1]
+    return node_ipv4, node_ipv6
+
 def update_plot():
-    global done, m, collection, not_found
+    global done, m, collection, not_found, points
+    for p in points:
+        p.remove()
+        del p
+    points = []
     lats = []
     lons = []
     cities=[]
+    colors=[]
     not_found.clear()
-    for n in all_nodes:
-        addr = n.getNode().getAddr().decode().split(':')[0]
-        if addr[0] == '[':
-            res = gi6.record_by_name_v6(addr[1:-1])
-        else:
-            res = gi.record_by_name(addr)
+    ip4s, ip6s = generate_set()
+    ares = []
+    for addr, n in ip4s.items():
+        ares.append((addr, n[0].getNode(), gi.record_by_name(addr)))
+    for addr, n in ip6s.items():
+        ares.append((addr, n[0].getNode(), gi6.record_by_name_v6(addr)))
+    for r in ares:
+        res = r[2]
+        n = r[1]
         if res:
-            #pprint(res)
             lats.append(res['latitude'])
             lons.append(res['longitude'])
             cities.append(res['city'] if res['city'] else (str(int(res['latitude']))+'-'+str(int(res['longitude']))))
+            colors.append('red' if n.isExpired() else 'blue')
         else:
-            not_found.append(n)
+            not_found.append(r[0])
 
     x,y = m(lons,lats)
-    m.plot(x,y,'bo')
+    points.extend(m.plot(x,y,'bo'))
     for name, xpt, ypt in zip(cities, x, y):
-        mpx.text(xpt+50000, ypt+50000, name)
+        points.append(mpx.text(xpt+50000, ypt+50000, name))
     node_val = [n.getId().toFloat() for n in all_nodes]
     xys = [(cos(d*2*pi), sin(d*2*pi)) for d in node_val]
     if collection:
         collection.remove()
+        del collection
+        collection = None
     collection = ringx.add_collection(RegularPolyCollection(
-                fig.dpi, 6, sizes=(10,), facecolors=colorConverter.to_rgba('blue'),
+                fig.dpi, 6, sizes=(10,), facecolors=colors,
                 offsets = xys, transOffset = ringx.transData))
 
-while done > 0:
-    update_plot()
+if run:
+    # start first step
+    start_h = PyInfoHash()
+    start_h.setBit(159, 1)
+    step(start_h, 0)
+
+def d(arg):
+   pass
+
+while run:
+    while run and done > 0:
+        update_plot()
+        plt.draw()
+        plt.pause(.5)
+
+    if not run:
+        break
+
+    button.on_clicked(restart)
+
+    node_ip4s, node_ip6s = generate_set()
+
+    print(all_nodes.size(), " nodes found")
+    print(all_nodes)
+    print(len(not_found), " nodes not geolocalized")
+    for n in not_found:
+        print(n)
+    print('')
+    print(len(node_ip4s), " different IPv4s :")
+    for ip in node_ip4s.items():
+        print(ip[0] + " : " + str(ip[1][1]) + " nodes")
+    print('')
+    print(len(node_ip6s), " different IPv6s :")
+    for ip in node_ip6s.items():
+        print(ip[0] + " : " + str(ip[1][1]) + " nodes")
+
+    while run and done == 0:
+        plt.pause(.5)
+    button.on_clicked(d)
     plt.draw()
-    plt.pause(.25)
-plt.draw()
-
-print(all_nodes.size(), " nodes found")
-print(all_nodes)
-print(len(not_found), " nodes not geolocalized")
-for n in not_found:
-    print(n.getNode().getId().toString().decode(), n.getNode().getAddr().decode())
-
-plt.ioff()
-plt.show()
 
 all_nodes = []
 r.join()
-