1
/* IWTAN context process and browse functions.*/
4
Copyright (C) 2008 Marco Cornolti
6
IWTAN (IWTAN: Wireless Topology ANalyzer) is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
8
IWTAN is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10
You should have received a copy of the GNU General Public License along with IWTAN; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17
#include <netinet/ether.h>
21
#include "iwtan_data.h"
23
/* ++++++++++++++ Data refreshing +++++++++++++++++++ */
26
Initialize a clear IWTAN context. Returns 0 on success or !=0 on error.
28
int iwtan_context_initialize(iwtan_context* context){
29
if (pthread_mutex_init(&(context->mutex), NULL)) return -1;
30
if (context==NULL) return -1;
33
context->ap_ass = calloc(context->allAss, sizeof(iwtan_association));
34
context->st_ass = calloc(context->allAss, sizeof(iwtan_association));
37
context->ap_list = calloc(context->allAP, sizeof(iwtan_ap*));
42
Destroy an IWTAN context. No other threads should use the context when this method is called. Returns 0 on success or !=0 on failure.
44
int iwtan_context_destroy(iwtan_context* context){
45
if (pthread_mutex_lock(&(context->mutex))) return -1;
48
for (i=0;i<context->assN;i++)
49
iwtan_free_station(((iwtan_association*)context->st_ass + i)->station);
51
for (i=0;i<context->apN;i++)
52
iwtan_free_AP((iwtan_ap*)(*(context->ap_list + i)));
53
free(context->ap_ass);
54
free(context->st_ass);
55
free(context->ap_list);
56
if (pthread_mutex_unlock(&(context->mutex))) return -1;
57
if (pthread_mutex_destroy(&(context->mutex))) return -1;
61
/* +++++++++++++++++++++++ DATA BROWSING FUNCTIONS ++++++++++++++++++++++++++ */
64
Get a linked list of wi_stations_el containing a copy of the stations currently part of the BSS with the given id.
65
All the stations properties are copied so that they may be used thread-safely.
66
The function iwtan_free_stations_ll() should be called when the list and the stations are no longer needed.
67
Returns NULL if no associations were found, otherwise a reference to the first element of the list.
68
Function is thread-safe.*/
69
iwtan_station_el* iwtan_get_by_AP(mac_address* bssId, iwtan_context* context){
71
pthread_mutex_lock(&(context->mutex));
72
iwtan_station_el* ret;
74
int firstPos = _iwtan_first_bsearch_by_AP(bssId, context);
79
unsigned int currentAssI = firstPos;
80
iwtan_association* currentAss = context->ap_ass + currentAssI;
82
iwtan_station_el* firstListEl=calloc(1, sizeof(iwtan_station_el));
83
firstListEl->station = _iwtan_copy_station(currentAss->station);
85
currentAss = context->ap_ass + currentAssI;
87
iwtan_station_el* currListEl = firstListEl;
88
while (currentAssI < context->assN && !iwtan_cmp_mac(currentAss->ap->bssId, bssId)){
89
currListEl->next = malloc(sizeof(iwtan_station_el));
90
currListEl = currListEl->next;
91
currListEl->station = _iwtan_copy_station(currentAss->station);
92
currListEl->next = NULL;
94
currentAss = context->ap_ass + currentAssI;
98
pthread_mutex_unlock(&(context->mutex));
103
Get a copy of the access point currently associated to the station with given MAC address.
104
All the AP properties are copied so that they may be used thread-safely.
105
The function iwtan_free_AP() should be called when the AP is no longer needed.
106
Returns NULL if no associations were found, otherwise a reference to the Access Point.
107
Function is thread-safe.*/
108
iwtan_ap* iwtan_get_by_station(mac_address* stMac, iwtan_context* context){
110
pthread_mutex_lock(&(context->mutex));
112
iwtan_association* ass = context->st_ass + _iwtan_bsearch_by_station(stMac, context);
113
ap = (ass==NULL) ? NULL : _iwtan_copy_AP(ass->ap);
114
pthread_mutex_unlock(&(context->mutex));
119
Get a list of (elements referring copies of) all the stations currently present in the database ordered by their MAC address.
120
All the stations properties are copied so that they may be used thread-safely.
121
The function iwtan_free_stations_ll() should be called when the list and the stations are no longer needed.
122
Returns the first element of the list.
123
Function is thread-safe.*/
124
iwtan_station_el* iwtan_get_all_stations(iwtan_context* context){
125
pthread_mutex_lock(&(context->mutex));
126
iwtan_station_el* ret = NULL;
127
if (context->assN != 0){
129
iwtan_station_el* curr = malloc(sizeof(iwtan_station_el));
130
curr->station = _iwtan_copy_station(context->ap_ass->station);
133
for (i=1; i<context->assN; i++){
134
curr->next = malloc(sizeof(iwtan_station_el));
136
curr->station = _iwtan_copy_station(((iwtan_association*)(context->ap_ass + i))->station);
141
pthread_mutex_unlock(&(context->mutex));
146
Get a list of (elements referring copies of) all the Access points currently present in the database ordered by their MAC address.
147
All the APs properties are copied so that they may be used thread-safely.
148
The function iwtan_free_ap_ll() should be called when the list and the stations are no longer needed.
149
Returns the first element of the list.
150
Function is thread-safe.
152
iwtan_ap_el* iwtan_get_all_APs(iwtan_context* context){
153
pthread_mutex_lock(&(context->mutex));
154
iwtan_ap_el* ret = NULL;
155
if (context->apN != 0){
157
iwtan_ap_el* curr = malloc(sizeof(iwtan_ap_el));
158
curr->ap = _iwtan_copy_AP(*(context->ap_list + 0));
161
for (i=1; i<context->apN; i++){
162
curr->next = malloc(sizeof(iwtan_ap_el));
164
curr->ap = _iwtan_copy_AP(*(context->ap_list + i));
169
pthread_mutex_unlock(&(context->mutex));
174
Get an array of (copies of) all the associations between APs and Stations currently present in the database, ordered by the stations MAC address.
175
All the elements properties are copied so that they may be used thread-safely.
176
The function iwtan_free_ass_array() should be called when the array of associations are no longer needed.
177
Returns a pointer to the first element of the array and writes in elementsN the number of element (array size).
178
Function is thread-safe.
180
iwtan_association* iwtan_get_all_associations(iwtan_context* context, unsigned int* elementsN){
181
pthread_mutex_lock(&(context->mutex));
182
*elementsN = context->assN;
183
iwtan_association* associations = calloc(context->assN, sizeof(iwtan_association));
185
iwtan_association* current;
186
iwtan_association* currentNew;
187
for (i=0; i<context->assN; i++){
188
current = context->st_ass + i;
189
currentNew = associations + i;
191
currentNew->station = _iwtan_copy_station(current->station);
192
currentNew->ap = _iwtan_copy_AP(current->ap);
194
pthread_mutex_unlock(&(context->mutex));
199
Get an AP with the given essid.
200
All the AP properties are copied so that they may be used thread-safely.
201
The function iwtan_free_AP() should be called when the AP is no longer needed.
202
Returns a pointer to the copied AP or NULL if an AP with the given MAC address was not found.
203
Function is thread-safe.
205
iwtan_ap* iwtan_get_by_essid(char* essid, iwtan_context* context){
206
if (!essid) return NULL;
207
iwtan_ap* ret = NULL;
210
pthread_mutex_lock(&(context->mutex));
211
while (i < context->apN){
212
current = *(context->ap_list + i);
214
if (!strcmp(current->essid, essid)) {
215
ret=_iwtan_copy_AP(current);
220
pthread_mutex_unlock(&(context->mutex));
225
/* +++++++++++++++++++++++++++ UTILITY FUNCTIONS ++++++++++++++++++++++++++++ */
228
Free an access point and all its elements. Returns 0 only on success.
230
int iwtan_free_AP(iwtan_ap* ap){
233
free(ap->description);
239
Free a station and all its elements. Returns 0 only on success.
241
int iwtan_free_station(iwtan_station* st){
250
Free a linked list of iwtan_station_el elements (such as the one created by iwtan_get_by_AP). Free both the elements and the list.
252
int iwtan_free_stations_ll(iwtan_station_el* freeEl){
253
iwtan_station_el* next;
254
while (freeEl != NULL){
256
iwtan_free_station(freeEl->station);
264
Free a linked list of iwtan_ap_el elements (such as the one created by iwtan_get_all_aps). Free the elements and the list.
266
int iwtan_free_AP_ll(iwtan_ap_el* freeEl){
268
while (freeEl != NULL){
270
iwtan_free_AP(freeEl->ap);
278
Free all the elements of an array containing associations with the number of elements given by argument. Also frees the array.
280
int iwtan_free_ass_array(iwtan_association* ass, int size){
282
iwtan_association* curr;
283
for (i=0; i<size; i++){
285
iwtan_free_station(curr->station);
286
iwtan_free_AP(curr->ap);
293
Compare two mac addresses. Returns 0 if the two given addresses are the same, >0 if mac1>mac2, <0 otherwise. Not thread safe.
295
int iwtan_cmp_mac(mac_address* mac1, mac_address* mac2){
297
while (i < ETH_ALEN && mac1->ether_addr_octet[i]== mac2->ether_addr_octet[i]) i++;
301
return mac1->ether_addr_octet[i] - mac2->ether_addr_octet[i];
305
Convert an IPv4 address to a string in the standard notation.
306
The string will be stored in the address pointed by result, that must be of size 16 at least.
307
Returns a pointer to the string or NULL on errors.
309
char* iwtan_ip4toa(ip4_address ip4, char* result){
310
if (!ip4) return NULL;
311
sprintf(result, "%d.%d.%d.%d", *(ip4), *(ip4+1), *(ip4+2), *(ip4+3));
315
/* Convert an IPv6 address to a string in the standard notation.
316
The string will be stored in the address pointed by result, that must be of size 40 at least.
317
Returns a pointer to the string or NULL on errors.
319
char* iwtan_ip6toa(ip6_address ip6, char* result){
320
if (!ip6) return NULL;
323
for(group=0; group<8; group++){
324
if (*(ip6+group*2)!=0 || *(ip6+group*2+1)!=0){
325
sprintf(str, "%.2x%.2x\0", *(ip6+group*2), *(ip6+group*2+1));
337
/*+++++++++++++++++++ INTERNAL UTILITY FUNCTIONS +++++++++++++++++++++++++++++*/
340
Add an association between a wireless station and an access point.
341
If the station mac address is already present in the database, the association gets refreshed with the new associated AP.
342
If the AP was not present in the database, a new one is created.
343
Returns 0 if the new association has been made, !=0 on errors.
345
int _iwtan_add_association(mac_address* bssId, mac_address* station_mac, iwtan_context* context){
346
if (bssId == NULL || station_mac == NULL) return 1;
348
/* Search for previousely existing associations for the station MAC address*/
349
iwtan_station* station;
350
int prevPos = _iwtan_bsearch_by_station(station_mac, context);
352
/*If the station is not present, we have to create it*/
353
station = calloc(1, sizeof(iwtan_station));
354
_iwtan_init_station(station_mac, station);
356
iwtan_ap** ap_ref = _iwtan_bsearch_AP(bssId, context);
359
/*If the access point is not present in the database, we have to create the new access point.*/
360
ap = _iwtan_add_new_AP(bssId, context);
364
_iwtan_add_new_association(station, ap, context);
365
ap->lastSeen = time(NULL);
369
/*If the station is already present, we have to re-associate it to the new access point*/
370
iwtan_association* ass = context->st_ass + prevPos;
371
ass->station->lastSeen = time(NULL);
372
iwtan_ap* oldAP = ass->ap;
373
oldAP->associations--;
374
iwtan_ap** newAP_ref = _iwtan_bsearch_AP(bssId, context);
377
if (newAP_ref == NULL){
378
/*If the access point is not present in the database, we have to create the new access point.*/
379
newAP = _iwtan_add_new_AP(bssId, context);
384
newAP->lastSeen = time(NULL);
385
newAP->associations++;
386
ass->ap = newAP; //in any case edit the st_ass
388
/*Find the association in ap_ass to the old access point and copy to it.*/
389
int APPosI = _iwtan_first_bsearch_by_AP(oldAP->bssId, context);
390
while (iwtan_cmp_mac(station_mac, ((iwtan_association*)(context->ap_ass + APPosI))->station->mac)) APPosI++;
391
*(context->ap_ass + APPosI) = *ass;
394
/*In any case, reorder the lists.*/
395
qsort(context->ap_ass, context->assN, sizeof(*(context->ap_ass)), (int(*)(const void *, const void *))_iwtan_cmp_assoc_by_AP);
396
qsort(context->st_ass, context->assN, sizeof(*(context->st_ass)), (int(*)(const void *, const void *))_iwtan_cmp_assoc_by_station);
402
Remove an access point from the list. Returns !=0 if and only if the given AP was not found or it was not empty (its associated stations was not 0). Not thread safe.
404
int _iwtan_remove_AP (iwtan_ap* ap, iwtan_context* context){
407
if (ap->associations != 0) return -1;
408
while((i < context->apN) && (ap != ((iwtan_association*)(context->ap_ass + i))->ap)) i++;
409
if (i != context->apN){
411
while (i<context->apN){
412
*(context->ap_ass + i -1) = *(context->ap_ass + i);
422
Add an access point with the given BSS Id to the list of access points. The AP should not be present in the list before calling this function.
423
The list gets ordered by the access points MAC address.
425
Returns a pointer to the Access Point
427
iwtan_ap* _iwtan_add_new_AP(mac_address* bssId, iwtan_context* context){
428
if (context->apN == context->allAP){
430
context->ap_list = realloc(context->ap_list, context->allAP * sizeof(iwtan_ap*));
432
iwtan_ap* newAP = calloc(1, sizeof(iwtan_ap));
433
_iwtan_init_AP(bssId, newAP);
434
*(context->ap_list + context->apN) = newAP;
436
qsort(context->ap_list, context->apN, sizeof(iwtan_ap*), (int(*)(const void *, const void *))_iwtan_cmp_AP);
441
Add an association between a station and an AP to both ap_ass and st_ass.
442
Note that no other corrispondences for that station should exist before.
443
The list is not ordered and must be ordered after calling this function.
444
The context mutex must be locked before calling this function (i.e. this function is not thread-safe).
445
Returns 0 only on success.
447
int _iwtan_add_new_association(iwtan_station* station, iwtan_ap* ap, iwtan_context* context){
448
/* To preserve memory, if the allocated space is three times bigger than the contained elements, it get reduced to a third.*/
449
if (context->allAss > 2*context->assN+10){
450
context->allAss = context->allAss/2 + 1;
451
context->ap_ass = realloc(context->ap_ass, context->allAss * sizeof(iwtan_association));
452
context->st_ass = realloc(context->st_ass, context->allAss * sizeof(iwtan_association));
454
/* If there is not enought allocated memory, the space is doubled.*/
455
else if (context->allAss == context->assN){
456
context->allAss *= 2;
457
context->ap_ass = realloc(context->ap_ass, context->allAss * sizeof(iwtan_association));
458
context->st_ass = realloc(context->st_ass, context->allAss * sizeof(iwtan_association));
461
iwtan_association* newApAss = context->ap_ass + context->assN;
462
newApAss->station = station;
464
iwtan_association* newStAss = context->st_ass + context->assN;
465
newStAss->station = station;
473
Update the data of an Access Point with those passed by argument.
474
Arguments are considered (and data is updated) only if their values are significant.
475
For pointer-arguments, the old data is freed and replaced with the argument only if the old and new pointer are not the same.
476
ap: the AP to update.
477
wepped: 1 => wepped, 0 => not wepped, -1 => not significant.
478
dataRate: the data rate in Mbps. 0 => not significant.
479
antenna: the antenna number. 0 => non significant.
480
frequency: the frequency in MHz. 0 => non significant.
481
type: the AP type (see iwtan_ap). 0 => non significant.
482
signal: the signal strenght. 0 => non significant.
483
mac: a pointer to the AP MAC address. NULL => non significant.
484
bssId: a pointer to the AP BSS id. NULL => non significant.
485
essid: a pointer to the AP essid string. NULL => non significant.
486
description: a pointer to the AP description set by the user. NULL => non significant.
487
lastSeen: the time this access point was seen for the last time. NULL => non significant.
489
int _iwtan_update_AP (iwtan_ap* ap, short wepped, unsigned int dataRate, unsigned short antenna, unsigned int frequency, short type, short signal, mac_address* bssId, char* essid, char* description, time_t lastSeen){
490
if (wepped != -1) ap->WEPped = wepped;
491
if (dataRate != 0) ap->dataRate = dataRate;
492
if (antenna != 0) ap->antenna = antenna;
493
if (frequency != 0) ap->frequency = frequency;
494
if (type !=0) ap->type = type;
495
if (signal != 0) ap->signal = signal;
496
if (bssId && (bssId != ap->bssId)) {
500
if (essid && (essid != ap->essid)) {
504
if (description && (description != ap->description)){
505
free(ap->description);
506
ap->description = description;
509
ap->lastSeen = lastSeen;
513
Update the data of a Station with those passed by argument.
514
Arguments are considered (and data is updated) only if their values are significant.
515
For pointer-arguments, the old data is freed and replaced with the argument only if the old and new pointer are not the same.
516
st: the station to update.
517
ip4: a pointer to the station IPv4. NULL => non significant.
518
mac: a pointer to the station MAC address. NULL => non significant.
519
ip6: a pointer to the station IPv6. NULL => non significant.
520
lastSeen: the time this station was seen for the last time. 0 => non significant.
522
int _iwtan_update_station (iwtan_station* st, ip4_address ip4, mac_address* mac, ip6_address ip6, time_t lastSeen){
523
if (ip4 && (ip4 != st->ip4)){
524
free(st->ip4); //TODO: may be optimized...
527
if (mac && (mac != st->mac)) {
531
if (ip6 && (ip6 != st->ip6)) {
536
st->lastSeen = lastSeen;
541
Makes a binary search and returns the position in st_ass of the association whose Station MAC address is the one given by argument.
542
Returns -1 if such an element is not found. The associations list must be ordered by the Station MAC address.
544
int _iwtan_bsearch_by_station (mac_address* st_mac, iwtan_context* con){
545
iwtan_association hypApAss; //an hypotetical association whose Station has the given mac address
547
hypApAss.station = &hypSt;
550
iwtan_association* el = bsearch(&hypApAss, con->st_ass, con->assN, sizeof(iwtan_association), (int(*)(const void*, const void*))_iwtan_cmp_assoc_by_station);
551
if (el==NULL) return -1;
553
return el - con->st_ass;
557
Search for a station with the given MAC address. Returns a pointer to the station or NULL if not found.
559
iwtan_station* _iwtan_bsearch_station(mac_address* st_mac, iwtan_context* con){
560
iwtan_association* ass = NULL;
561
unsigned int idx = _iwtan_bsearch_by_station(st_mac, con);
562
if (idx != -1) ass = con->st_ass + idx;
563
return ass ? ass->station : NULL;
568
Search for an access point in the list and return its index. The context mutex must be locked before calling this function. Returns a pointer to the address where the Access Point is allocated.
570
iwtan_ap** _iwtan_bsearch_AP(mac_address* bssId, iwtan_context* context){
572
const iwtan_ap* hypAP_ref = &hypAP;
574
return bsearch(&hypAP_ref, context->ap_list, context->apN, sizeof(iwtan_ap*), (int(*)(const void*, const void*))_iwtan_cmp_AP);
578
Makes a binary search and returns the position of the first association whose Access Point MAC address is the one given by argument.
579
Returns -1 if such an element is not found.
580
The associations list must be ordered by the Access Point MAC address.
581
The context mutex must be locked before calling this function.
583
int _iwtan_first_bsearch_by_AP(mac_address* bssId, iwtan_context* con){
584
iwtan_association hypApAss; //an hypotetical association whose AP has the given mac address
586
hypApAss.ap = &hypAP;
589
iwtan_association* el = bsearch(&hypApAss, con->ap_ass, con->assN, sizeof(iwtan_association), (int(*)(const void*, const void*))_iwtan_cmp_assoc_by_AP);
590
if (el==NULL) return -1;
592
unsigned int i = el - con->ap_ass; //last certian element
593
iwtan_association* assNext;
595
assNext = con->ap_ass+i-1;
596
if (iwtan_cmp_mac(assNext->ap->bssId, bssId)) return i;
603
Initialize an empty AP with the given MAC address
605
int _iwtan_init_AP(mac_address* bssId, iwtan_ap* ap){
608
ap->lastSeen = time(NULL);
613
Initialize a station with the given mac address
615
int _iwtan_init_station(mac_address* st_mac, iwtan_station* station){
616
station->mac = st_mac;
617
station->lastSeen = time(NULL);
622
Compare two associations by their Access Point mac address.
624
int _iwtan_cmp_assoc_by_AP(const iwtan_association* ass1, const iwtan_association* ass2){
625
return iwtan_cmp_mac(ass1->ap->bssId, ass2->ap->bssId);
629
Compare two associations by their station mac address
631
int _iwtan_cmp_assoc_by_station(const iwtan_association* ass1, const iwtan_association* ass2){
632
return iwtan_cmp_mac(ass1->station->mac, ass2->station->mac);
636
Compare two access points by their BSS Id.
638
int _iwtan_cmp_AP(const iwtan_ap** ap1_ref, const iwtan_ap** ap2_ref){
639
const iwtan_ap* ap1 = *ap1_ref;
640
const iwtan_ap* ap2 = *ap2_ref;
641
return iwtan_cmp_mac(ap1->bssId, ap2->bssId);
645
Returns a copy of the station given by argument, with all its elements copied.
647
iwtan_station* _iwtan_copy_station (iwtan_station* st){
648
iwtan_station* newStation = calloc(1, sizeof(iwtan_station));
649
newStation->ip4 = _iwtan_copy_ip4(st->ip4);
650
newStation->ip6 = _iwtan_copy_ip6(st->ip6);
651
newStation->mac = _iwtan_copy_mac(st->mac);
652
newStation->lastSeen = st->lastSeen;
657
Returns a copy of the Access Point given by argument, with all its elements copied.
659
iwtan_ap* _iwtan_copy_AP (iwtan_ap* ap){
660
iwtan_ap* newAP = calloc(1, sizeof(iwtan_ap));
664
newAP->essid = calloc(strlen(ap->essid) + 1, sizeof(char));
665
strcpy(newAP->essid, ap->essid);
669
if (ap->description){
670
newAP->description = calloc(strlen(ap->description) + 1, sizeof(char));
671
strcpy(newAP->description, ap->description);}
673
newAP->bssId = _iwtan_copy_mac(ap->bssId);
679
Returns a copy of the IPv4 address given by argument.
681
ip4_address _iwtan_copy_ip4(ip4_address ip4){
682
if (!ip4) return NULL;
683
ip4_address newIP4 = calloc(IWTAN_IP4_LEN, sizeof(uint8_t));
685
for(i=0;i<IWTAN_IP4_LEN; i++)
686
*(newIP4+i) = *(ip4+i);
691
Returns a copy of the IPv6 address given by argument.
693
ip6_address _iwtan_copy_ip6(ip6_address ip6){
694
if (!ip6) return NULL;
695
ip6_address newIP6 = calloc(IWTAN_IP6_LEN, sizeof(uint8_t));
697
for(i=0;i<IWTAN_IP6_LEN; i++)
698
*(newIP6 + i) = *(ip6 + i);
703
Returns a copy of the MAC address given by argument.
705
mac_address* _iwtan_copy_mac(mac_address* mac){
706
if (!mac) return NULL;
707
mac_address* newMac = malloc(sizeof(mac_address));
709
for(i=0;i<sizeof(mac_address); i++)
710
newMac->ether_addr_octet[i] = mac->ether_addr_octet[i];
715
Copy a newly allocated MAC address with the bytes pointed by array.
717
mac_address* _iwtan_copy_mac_by_array(uint8_t* array){
718
if (!array) return NULL;
719
mac_address* newMac = malloc(sizeof(mac_address));
721
for(i=0;i<sizeof(mac_address); i++)
722
newMac->ether_addr_octet[i] = *(array+i);