source: src/django_gheat/gheat/management/commands/import_kismet.py@ 9561

Last change on this file since 9561 was 9561, checked in by rick, 13 years ago

gwSplit netxml and gpsxml into to different calls, as you first like to import
_all_ netxml before importing _all_ gpsxml, as the gps probes might reference a
unknown accesspoint.

  • Property svn:executable set to *
File size: 7.2 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Script for importing .gpsxml and .netxml files (Kismet output)
5#
6# Rick van der Zwet <info@rickvanderzwet.nl>
7#
8from django.core.management.base import BaseCommand,CommandError
9from django.db.utils import IntegrityError
10from optparse import OptionParser, make_option
11from gheat.models import *
12from lxml import etree
13import datetime
14import gzip
15import os
16import sys
17import logging
18
19from collections import defaultdict
20
21from import_droidstumbler import bulk_sql
22
23logger = logging.getLogger(__name__)
24logger.setLevel(logging.INFO)
25
26# Open files for reading
27def open_file(file):
28 if file.endswith('.gz'):
29 return gzip.open(file,'rb')
30 else:
31 return open(file,'rb')
32
33
34
35def import_kismet_netxml(netxml_file):
36 netxml_doc = etree.parse(open_file(netxml_file))
37
38 counters = { 'ap_added' : 0, 'ap_total' : 0, 'ap_failed' : 0, 'ap_ignored' : 0}
39
40 # Prepare new accespoints and measurements
41 wnetworks = netxml_doc.findall('wireless-network')
42
43 # Temponary holders
44 ap_pool = {}
45
46 # Create all accesspoints and for caching validation purposes store them
47 # locally as well
48 for wnetwork in wnetworks:
49 counters['ap_total'] += 1
50 bssid = wnetwork.find('BSSID').text
51 # Only store access points
52 ap_type = wnetwork.attrib['type']
53 if ap_type in ['infrastructure', 'data']:
54 encryption = (wnetwork.find('SSID/encryption') != None)
55 ssid_node = wnetwork.find('SSID/essid[@cloaked="false"]')
56 ssid = ssid_node.text if ssid_node != None else 'hidden'
57
58 ap_pool[bssid] = (ssid, encryption)
59 elif ap_type in ['probe', 'ad-hoc']:
60 counters['ap_ignored'] += 1
61 continue
62 else:
63 logger.error('Unknown type %s - %s',bssid, wnetwork.attrib['type'])
64
65
66 # Determine which entries we need to add
67 bssid_list_present = Accespoint.objects.filter(mac__in=ap_pool.keys()).values_list('mac', flat=True)
68 bssid_list_insert = set(ap_pool.keys()) - set(bssid_list_present)
69
70 # Create a bulk import list and import
71 if bssid_list_insert:
72 sql_values = []
73 for bssid in bssid_list_insert:
74 ssid, encryption = ap_pool[bssid]
75 # Special trick in SSID ts avoid escaping in later stage
76 item = str((bssid,ssid.replace('%','%%'),encryption))
77 sql_values.append(item)
78 counters['ap_added'] = bulk_sql('gheat_accespoint (`mac`, `ssid`, `encryptie`)',sql_values)
79
80 return counters
81
82
83
84def import_kismet_gpsxml(gpsxml_file, meetrondje):
85 gpsxml_doc = etree.parse(open_file(gpsxml_file))
86
87 #Various statistics
88 counters = {'meting_added' : 0, 'meting_total' : 0, 'meting_failed' : 0, 'meting_ignored' :0}
89
90 bssid_failed = defaultdict(int)
91
92 # Prepare new accespoints and measurements
93 points = gpsxml_doc.findall('gps-point')
94
95 # Temponary holders
96 meting_pool = defaultdict(list)
97
98 for point in points:
99 counters['meting_total'] += 1
100 #XXX: This needs to be either the 'bssid' or the 'source',
101 #XXX: accesspoint from or too data.
102 bssid = point.attrib['bssid']
103 # XXX: Filter this in the beginning with XPath, but etree does not support
104 # that (yet).
105 if bssid in ['GP:SD:TR:AC:KL:OG','00:00:00:00:00:00']:
106 counters['meting_ignored'] =+ 1
107 continue
108 # XXX: Signal need properly be a relation of signal_dbm and noice_dbm
109 try:
110 level = point.attrib['signal_dbm']
111 except KeyError:
112 logger.debug("Point '%s' does not have signal strengh" % point)
113 counters['meting_failed'] += 1
114 continue
115 # We store all values found, avg or max will be done later on
116 key = (bssid, point.attrib['lat'], point.attrib['lon'])
117 signaal=100 + int(level)
118 meting_pool[key].append(signaal)
119
120 # Build mapping for meting import
121 mac2id = {}
122 for mac,id in Accespoint.objects.filter(mac__in=meting_pool.keys()).values_list('mac','id'):
123 mac2id[mac] = int(id)
124
125 sql_values = []
126 for (bssid,lat,lon),signals in meting_pool.iteritems():
127 if not mac2id.has_key(bssid):
128 counters['meting_failed'] += len(signals)
129 bssid_failed[bssid] += len(signals)
130 continue
131 item = str((int(meetrondje.id),mac2id[bssid],float(lat),float(lon),max(signaal)))
132 sql_values.append(item)
133
134 for bssid,count in sorted(bssid_failed.items(),
135 key=lambda item: item[1], reverse=True):
136 logger.debug("Missing BSSID %s found %3s times", bssid, count)
137
138 if sql_values:
139 counters['meting_added'] = bulk_sql('gheat_meting (`meetrondje_id`, `accespoint_id`, `lat`, `lng`, `signaal`)',sql_values)
140 return counters
141
142
143class Command(BaseCommand):
144 args = '<gpsxml|netxml>[.gz] [gpsxml2[.gz] gpsxml3[.gz] ...]'
145 option_list = BaseCommand.option_list + (
146 make_option('-k', '--kaart', dest='kaart', default='onbekend', help="Kaart gebruikt"),
147 make_option('-m', '--meetrondje', dest='meetrondje', default=None),
148 make_option('-g', '--gebruiker', dest='gebruiker', default='username',help='Naam van de persoon die de meting uitgevoerd heeft'),
149 make_option('-e', '--email', dest='email', default='foo@bar.org',help='Email van de persoon die de meting uitgevoerd heeft'),
150 make_option('-d', '--datum', dest='datum', default=None, help="Provide date \
151 in following format: '%Y%m%d-%H-%M-%S-1', by default it will be generated from \
152 the filename"),
153 )
154
155 def handle(self, *args, **options):
156 if len(args) == 0:
157 self.print_help(sys.argv[0],sys.argv[1])
158 raise CommandError("Not all arguments are provided")
159
160 for xml_file in args:
161 if not os.path.isfile(xml_file):
162 raise CommandError("xml file '%s' does not exists" % xml_file)
163
164
165 for xml_file in args:
166 logger.info("Processing '%s'" % xml_file)
167 if 'netxml' in xml_file:
168 counters = import_kismet_netxml(xml_file)
169 logger.info("summary accespoints: total:%(ap_total)-6s added:%(ap_added)-6s failed:%(ap_failed)-6s ignored:%(ap_ignored)-6s" % counters)
170 elif 'gpsxml' in xml_file:
171 if options['datum'] == None:
172 datum = os.path.basename(xml_file).lstrip('Kismet-').rstrip('.gz').rstrip('.gpsxml').rstrip('.netxml')
173 else:
174 datum = options['datum']
175 try:
176 # Kismet-20110805-15-37-30-1
177 datum = datetime.datetime.strptime(datum,'%Y%m%d-%H-%M-%S-1')
178 except ValueError:
179 raise CommandError("Invalid date '%s'" % options['datum'])
180
181 # Meetrondje from filename if needed
182 if options['meetrondje'] == None:
183 meetrondje = os.path.basename(xml_file).rstrip('.gz').rstrip('.gpsxml')
184 else:
185 meetrondje = options['meetrondje']
186
187 # Create meetrondje object
188 g, created = Gebruiker.objects.get_or_create(naam=options['gebruiker'] , email=options['email'])
189 a, created = Apparatuur.objects.get_or_create(kaart=options['kaart'])
190 mr, created = MeetRondje.objects.get_or_create(datum=datum , naam=meetrondje , gebruiker=g , apparatuur=a)
191 logger.info('Meetrondje: %s @ %s' % (meetrondje, datum))
192 if not created:
193 logger.error("Meetrondje '%s' already imported" % mr)
194 sys.exit(1)
195 counters = import_kismet_gpsxml(xml_file, mr)
196 logger.info("summary metingen : total:%(meting_total)-6s added:%(meting_added)-6s failed:%(meting_failed)-6s ignored:%(meting_ignored)-6s" % counters)
197 else:
198 raise CommandError("xml file '%s' format not recognized" % xml_file)
Note: See TracBrowser for help on using the repository browser.