3 MacGeiger WIFI AP detector
4 Copyright (C) 2014 rofl0r
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <pcap/pcap.h>
26 #include <arpa/inet.h>
33 #include "audio-backend.c"
38 #include "channel-switch.c"
41 #define MIN(x, y) ((x) < (y) ? (x) : (y))
45 #define MAX(x, y) ((x) > (y) ? (x) : (y))
48 #pragma RcB2 LINK "-lpcap" "-lpthread"
50 #include "../concol/console.h"
51 #include "../concol/console_keys.h"
52 #include "../concol/fonts/allfonts.h"
55 #define console_setcolor(A, B, C) do {} while(0)
60 static int usage(const char *argv0
) {
61 dprintf(2, "%s [-c channel] network-interface\n"
62 "i.e.: %s wlan0\n", argv0
, argv0
67 /* originally 256, but that would make the struct too big for the stack */
68 #define WPS_MAX_STR_LEN 64
74 char manufacturer
[WPS_MAX_STR_LEN
];
75 char model_name
[WPS_MAX_STR_LEN
];
76 char model_number
[WPS_MAX_STR_LEN
];
77 char device_name
[WPS_MAX_STR_LEN
];
78 char ssid
[WPS_MAX_STR_LEN
];
79 char uuid
[WPS_MAX_STR_LEN
];
80 char serial
[WPS_MAX_STR_LEN
];
81 char selected_registrar
[WPS_MAX_STR_LEN
];
82 char response_type
[WPS_MAX_STR_LEN
];
83 char primary_device_type
[WPS_MAX_STR_LEN
];
84 char config_methods
[WPS_MAX_STR_LEN
];
85 char rf_bands
[WPS_MAX_STR_LEN
];
86 char os_version
[WPS_MAX_STR_LEN
];
89 static void init_wps_data(struct wps_data
* wps
) {
93 wps
->manufacturer
[0] = 0;
94 wps
->model_name
[0] = 0;
95 wps
->model_number
[0] = 0;
96 wps
->device_name
[0] = 0;
100 wps
->selected_registrar
[0] = 0;
101 wps
->response_type
[0] = 0;
102 wps
->primary_device_type
[0] = 0;
103 wps
->config_methods
[0] = 0;
104 wps
->rf_bands
[0] = 0;
105 wps
->os_version
[0] = 0;
117 long long total_rssi
;
120 signed char last_rssi
;
121 signed char min_rssi
;
122 signed char max_rssi
;
123 unsigned char mac
[6];
124 struct ap_client
*next
;
128 struct wps_data
*wps
;
129 long long total_rssi
;
133 uint16_t beaconinterval
;
135 unsigned char mac
[6];
136 unsigned char channel
;
137 signed char last_rssi
;
138 signed char min_rssi
;
139 signed char max_rssi
;
141 struct ap_client
*clients
;
144 #define STATIC_ALLOC 1
146 static struct wlaninfo wlans_storage
[256];
147 static unsigned wlan_count
;
148 #define DYNA_NEW(X) wlans_storage
149 #define DYNA_GROW(X) (wlan_count++, wlans)
150 #define DYNA_COUNT(X) wlan_count
152 #include "../lib/include/dynarray.h"
155 static struct wlaninfo
*wlans
;
157 static pthread_mutex_t wlan_lock
= PTHREAD_MUTEX_INITIALIZER
;
158 #define lock() pthread_mutex_lock(&wlan_lock)
159 #define unlock() pthread_mutex_unlock(&wlan_lock)
161 static inline void get_wlan(size_t index
, struct wlaninfo
* out
) {
163 memcpy(out
, &wlans
[index
], sizeof(*out
));
167 static inline void write_wlan(size_t index
, struct wlaninfo
* in
) {
169 memcpy(&wlans
[index
], in
, sizeof(*in
));
174 static signed char min
, max
;
175 static unsigned char selection
, selected
;
176 static Console co
, *t
= &co
;
177 static int colorcount
;
179 static int get_wlan_by_essid(char* essid
) {
183 l
= DYNA_COUNT(wlans
);
185 if(!strcmp(essid
, wlans
[i
].essid
)) {
194 static int get_wlan_by_mac(unsigned char mac
[6]) {
198 l
= DYNA_COUNT(wlans
);
200 if(!memcmp(mac
, wlans
[i
].mac
, 6)) {
209 static int get_new_wlan(void) {
211 void* p
= DYNA_GROW(wlans
);
217 memset(&wlans
[DYNA_COUNT(wlans
)-1], 0, sizeof(wlans
[0]));
218 wlans
[DYNA_COUNT(wlans
)-1].min_rssi
= 127;
219 wlans
[DYNA_COUNT(wlans
)-1].max_rssi
= -127;
220 int res
= DYNA_COUNT(wlans
)-1;
225 static struct ap_client
*get_client(struct wlaninfo
*w
, unsigned char mac
[6]) {
227 for(c
= w
->clients
; c
; c
= c
->next
) {
228 if(!memcmp(mac
, c
->mac
, 6)) return c
;
233 static struct ap_client
*add_client(struct wlaninfo
*w
, unsigned char mac
[6]) {
234 struct ap_client
*c
= calloc(1, sizeof(*c
)), *it
;
235 memcpy(c
->mac
, mac
, 6);
238 if(!w
->clients
) w
->clients
= c
;
241 while(it
->next
) it
=it
->next
;
247 static long long getutime64(void);
249 static void set_client_rssi(struct wlaninfo
* w
, struct ap_client
*c
) {
251 c
->total_rssi
+= w
->last_rssi
;
252 c
->last_seen
= getutime64();
253 c
->last_rssi
= w
->last_rssi
;
254 c
->min_rssi
= MIN(c
->min_rssi
, c
->last_rssi
);
255 c
->max_rssi
= MAX(c
->max_rssi
, c
->last_rssi
);
258 static int set_rssi(struct wlaninfo
*w
, struct wps_data
* wps
) {
260 struct wlaninfo wtmp
, *d
= &wtmp
;
261 if(i
== -1) i
= get_wlan_by_mac(w
->mac
);
262 if(i
== -1) i
= get_new_wlan();
265 if(w
->essid
[0]) strcpy(d
->essid
, w
->essid
);
266 memcpy(d
->mac
, w
->mac
, 6);
267 d
->total_rssi
+= w
->last_rssi
;
269 d
->last_rssi
= w
->last_rssi
;
270 d
->channel
= w
->channel
;
271 d
->timestamp
= w
->timestamp
;
272 d
->beaconinterval
= w
->beaconinterval
;
273 d
->min_rssi
= MIN(d
->min_rssi
, d
->last_rssi
);
274 d
->max_rssi
= MAX(d
->max_rssi
, d
->last_rssi
);
275 d
->enctype
= w
->enctype
;
278 d
->wps
= malloc(sizeof *wps
);
279 if(d
->wps
) init_wps_data(d
->wps
);
282 if(!wps
->manufacturer
[0]) {
283 d
->wps
->version
= wps
->version
;
284 d
->wps
->state
= wps
->state
;
285 d
->wps
->locked
= wps
->locked
;
287 memcpy(d
->wps
, wps
, sizeof(*wps
));
300 #include "radiotap_flags.h"
302 static unsigned get_flags_off(unsigned flags
, unsigned start_off
) {
303 return rt_get_flag_offset(flags
, IEEE80211_RADIOTAP_FLAGS
, start_off
);
306 static unsigned get_dbm_off(unsigned flags
, unsigned start_off
) {
307 return rt_get_flag_offset(flags
, IEEE80211_RADIOTAP_DBM_ANTSIGNAL
, start_off
);
310 static unsigned get_chan_off(unsigned flags
, unsigned start_off
) {
311 return rt_get_flag_offset(flags
, IEEE80211_RADIOTAP_CHANNEL
, start_off
);
314 static unsigned channel_from_freq(unsigned freq
) {
315 return freq
==2484?14:(freq
-2407)/5;
319 uint16_t framecontrol
;
321 unsigned char receiver
[6];
322 unsigned char source
[6];
323 unsigned char bssid
[6];
324 uint16_t sequence_no
;
327 static unsigned char* find_tag(unsigned const char *tagdata
, unsigned tag
, unsigned bytes_left
) {
329 if(*tagdata
== tag
) return (unsigned char*)tagdata
;
330 unsigned tagsize
= tagdata
[1];
332 if(bytes_left
< 2+tagsize
) return 0;
333 bytes_left
-=2+tagsize
;
338 static long long timeval2utime(struct timeval
*t
) {
339 return (t
->tv_sec
* 1000LL * 1000LL) + t
->tv_usec
;
342 static long long getutime64(void) {
344 gettimeofday(&t
, NULL
);
345 return timeval2utime(&t
);
349 static int filebased
;
351 static const unsigned char* pcap_next_wrapper(pcap_t
*foo
, struct pcap_pkthdr
*h_out
) {
354 const unsigned char* ret
= 0;
355 struct pcap_pkthdr
*hdr_temp
;
356 int err
= pcap_next_ex(foo
, &hdr_temp
, &ret
);
358 /* skip malformed packets, like those emitted by busybox udhcpc */
359 struct ieee80211_radiotap_header
*rh
= (void*) ret
;
360 if(rh
->it_version
!= 0 || end_le16toh(rh
->it_len
) > hdr_temp
->len
)
365 if(ret
&& outfd
!= -1){
366 pcapfile_write_packet(outfd
, h_out
, ret
);
370 static long long pcap_file_start_time
, start_time
;
371 static unsigned char buf
[2][2048];
372 static struct pcap_pkthdr h
[2];
374 const unsigned char* ret
;
375 if(start_time
== 0 || getutime64() - start_time
>= timeval2utime(&h
[!actbuf
].ts
) - pcap_file_start_time
) {
376 ret
= pcap_next(foo
, h_out
);
379 assert(h
[actbuf
].len
<= sizeof buf
[actbuf
]);
380 memcpy(buf
[actbuf
], ret
, h
[actbuf
].len
);
384 start_time
= getutime64();
386 pcap_file_start_time
= timeval2utime(&h_out
->ts
);
397 static inline int myisascii(int x
) {
398 return x
>= ' ' && x
< 127;
401 static void dump_packet(const unsigned char* data
, size_t len
) {
402 static const char atab
[] = "0123456789abcdef";
403 char hex
[24*2+1], ascii
[24+1];
404 unsigned h
= 0, a
= 0;
409 hex
[h
++] = atab
[*data
>> 4];
410 hex
[h
++] = atab
[*data
& 0xf];
411 ascii
[a
++] = myisascii(*data
) ? *data
: '.';
416 printf("%s\t%s\n", hex
, ascii
);
418 if(fill
== '_') return; /* jump from filler */
439 void setminmax(int val
) {
443 snprintf(mmbuf
, sizeof mmbuf
, "min: %d, max: %d", min
, max
);
444 console_settitle(t
, mmbuf
);
447 static int get_next_ie(const unsigned char *data
, size_t len
, size_t *currpos
) {
448 if(*currpos
+ 2 >= len
) return 0;
449 *currpos
= *currpos
+ 2 + data
[*currpos
+ 1];
450 if(*currpos
>= len
) return 0;
454 static int get_next_wps_el(const unsigned char *data
, size_t len
, size_t *currpos
) {
455 if(*currpos
+ 4 >= len
) return 0;
457 memcpy(&el_len
, data
+ 2 + *currpos
, 2);
458 el_len
= end_be16toh(el_len
);
459 *currpos
= *currpos
+ 4 + el_len
;
460 if(*currpos
>= len
) return 0;
464 static void process_wps_tag(const unsigned char* tag
, size_t len
, struct wps_data
*wps
) {
465 unsigned const char *el
;
467 size_t el_iterator
= 0, wfa_iterator
, remain
;
468 uint16_t el_id
, el_len
;
472 el
= tag
+ el_iterator
;
473 remain
= len
- el_iterator
;
474 memcpy(&el_id
, el
, 2);
475 el_id
= end_be16toh(el_id
);
476 memcpy(&el_len
, el
+2, 2);
477 el_len
= end_be16toh(el_len
);
481 case 0x104A: /* WPS_VERSION */
484 case 0x1044: /* WPS_STATE */
487 case 0x1057: /* WPS_LOCKED */
490 case 0x1021: /* WPS_MANUFACTURER */
491 str
= wps
->manufacturer
;
493 case 0x1023: /*WPS_MODEL_NAME */
494 str
= wps
->model_name
;
497 str
= wps
->model_number
;
500 str
= wps
->device_name
;
513 str
= wps
->selected_registrar
;
517 str
= wps
->response_type
;
521 str
= wps
->primary_device_type
;
525 str
= wps
->config_methods
;
532 str
= wps
->os_version
;
534 case 0x1049: /* WPS_VENDOR_EXTENSION */
535 if(el_len
>= 5 && !memcmp(el
, "\x00\x37\x2A", 3)) { /* WFA_EXTENSION */
540 if(wfa_iterator
+2 <= el_len
&& el
[wfa_iterator
] == 0 /* WPS_VERSION2_ID */) {
541 wps
->version
= el
[2];
543 } while(get_next_ie(el
, el_len
, &wfa_iterator
));
550 max
= el_len
>= WPS_MAX_STR_LEN
/2 ? WPS_MAX_STR_LEN
/2 - 1 : el_len
;
552 sprintf(str
, "%02x", *el
);
558 max
= el_len
+ 1 >= WPS_MAX_STR_LEN
? WPS_MAX_STR_LEN
: el_len
+ 1;
559 snprintf(str
, max
, "%s", el
);
563 } while(get_next_wps_el(tag
, len
, &el_iterator
));
567 static void process_tags(const unsigned char* tagdata
, size_t tagdata_len
, struct wlaninfo
*temp
, struct wps_data
*wps
) {
568 unsigned const char *tag
;
570 /* iterate through tags */
571 size_t ie_iterator
= 0, remain
;
573 tag
= tagdata
+ ie_iterator
;
574 remain
= tagdata_len
- ie_iterator
;
576 case 0: /* essid tag */
577 if(tag
[1] <= remain
) {
578 memcpy(temp
->essid
, tag
+2, tag
[1]);
579 temp
->essid
[tag
[1]] = 0;
582 case 3: /* chan nr */
584 temp
->channel
= tag
[2];
586 case 0x30: /* RSN_TAG_NUMBER */
587 temp
->enctype
= ET_WPA2
;
589 case 0xDD: /* VENDOR_SPECIFIC_TAG*/
590 if(tag
[1] >= remain
) break;
592 !memcmp(tag
+2, "\x00\x50\xF2\x01\x01\x00", 6))
593 temp
->enctype
= ET_WPA
;
594 if(tag
[1] > 4 && !memcmp(tag
+2, "\x00\x50\xf2" /*micro$oft*/ "\x04" /*type WPS*/, 4))
595 process_wps_tag(tag
+2+4, tag
[1]-4, wps
);
599 } while(get_next_ie(tagdata
, tagdata_len
, &ie_iterator
));
602 static int process_frame(pcap_t
*foo
) {
603 struct pcap_pkthdr h
;
604 const unsigned char* data
= pcap_next_wrapper(foo
, &h
);
606 if(console_getbackendtype(t
) == cb_sdl
&& getenv("DEBUG")) dump_packet(data
, h
.len
);
608 uint32_t flags
, offset
, fchksum
;
610 if(!rt_get_presentflags(data
, h
.len
, &flags
, &offset
))
613 struct ieee80211_radiotap_header
*rh
= (void*) data
;
615 unsigned rtap_data
= offset
;
617 if(flags
& (1U << IEEE80211_RADIOTAP_FLAGS
)) {
618 unsigned flags_off
= get_flags_off(flags
, rtap_data
);
619 if(data
[flags_off
] & 0x10 /* IEEE80211_RADIOTAP_F_FCS */) {
620 /* TODO handle bad FCS IEEE80211_RADIOTAP_F_BADFCS 0x40 */
621 memcpy(&fchksum
, data
+ h
.len
- 4, 4);
622 fchksum
= end_le32toh(fchksum
);
627 struct wlaninfo temp
= {0};
629 if(!(flags
& (1U << IEEE80211_RADIOTAP_DBM_ANTSIGNAL
))) return -1;
630 unsigned dbmoff
= get_dbm_off(flags
, rtap_data
);
631 temp
.last_rssi
= ((signed char*)data
)[dbmoff
];
634 // if(!(flags & (1U << IEEE80211_RADIOTAP_CHANNEL))) return -1;
636 unsigned chanoff
= get_chan_off(flags
, rtap_data
);
637 memcpy(&freq
, data
+ chanoff
, 2);
638 temp
.channel
= channel_from_freq(freq
);
640 uint16_t framectl
, fctype
;
641 offset
= end_le16toh(rh
->it_len
);
642 memcpy(&framectl
, data
+offset
, 2);
643 framectl
= end_le16toh(framectl
);
644 struct dot11frame
* beacon
;
645 unsigned const char* tagdata
;
652 /* IEEE 802.11 packet type */
653 case 0x0080: /* beacon */
654 case 0x0050: /* probe response */
655 beacon
= (void*)(data
+offset
);
656 memcpy(&temp
.mac
,beacon
->source
,6);
657 offset
+= sizeof(*beacon
);
658 memcpy(&temp
.timestamp
,data
+offset
,8);
659 temp
.timestamp
= end_le64toh(temp
.timestamp
);
661 memcpy(&temp
.beaconinterval
, data
+offset
,2);
662 temp
.beaconinterval
= end_le16toh(temp
.beaconinterval
);
664 memcpy(&caps
, data
+offset
, 2);
665 caps
= end_le16toh(caps
);
666 if(caps
& 0x10 /* CAPABILITY_WEP */)
667 temp
.enctype
= ET_WEP
;
671 tagdata_len
= h
.len
-pos
;
673 process_tags(tagdata
, tagdata_len
, &temp
, &wps
);
674 setminmax(temp
.last_rssi
);
675 return set_rssi(&temp
, &wps
);
679 case 0x0040: /* probe request */
682 fctype
= framectl
& end_htole16(IEEE80211_FCTL_FTYPE
| IEEE80211_FCTL_STYPE
);
683 if(fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_DATA
) ||
684 fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_QOS_DATA
) ||
685 fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_NULLFUNC
) ||
686 fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_QOS_NULLFUNC
)
688 beacon
= (void*)(data
+offset
);
689 if(!memcmp(beacon
->receiver
, "\x01\x80\xc2", 3) ||
690 !memcmp(beacon
->receiver
, "\x01\x00\x0c\xcc\xcc\xcc", 6) ||
691 !memcmp(beacon
->receiver
, "\xff\xff\xff", 3))
694 int wifi_nr
= get_wlan_by_mac(beacon
->bssid
);
695 /* ignore packets sent to unknown APs */
696 if(wifi_nr
== -1) return -1;
697 unsigned char *client_mac
;
698 if(memcmp(beacon
->source
, beacon
->bssid
, 6))
700 client_mac
= beacon
->source
;
703 client_mac
= beacon
->receiver
;
705 struct wlaninfo apbuf
, *ap
=&apbuf
;
706 get_wlan(wifi_nr
, ap
);
707 struct ap_client
* c
= get_client(ap
, client_mac
);
708 if(!c
) c
= add_client(ap
, client_mac
);
709 write_wlan(wifi_nr
, ap
);
710 setminmax(temp
.last_rssi
);
711 if(client_mac
== beacon
->source
) {
712 set_client_rssi(&temp
, c
);
714 } else return wifi_nr
;
721 //while(htonl(*(flags++)) & (1U << IEEE80211_RADIOTAP_EXT)) next_chunk+=4;
722 //dprintf(2, "got data\n");
729 static int next_chan(int chan
) {
730 if(++chan
> 11) chan
= 1;
734 static int next_chan(int chan
) {
735 static char chanlist
[]={1,5,9,13,2,6,10,14,3,7,11,4,8,12};
737 for(i
= 0; i
< sizeof chanlist
&& chanlist
[i
] != chan
; i
++);
738 if(i
>=13) return chanlist
[0];
739 return chanlist
[++i
];
742 static int next_chan(int chan
) {
744 case 1: case 2: case 3: case 4: case 5:
746 case 6: case 7: case 8: case 9: case 10:
748 case 11: case 12: case 13:
749 /* uncomment next line if you leave in a country using chan 14 */
760 static struct {int w
, h
;} dim
;
762 #define BGCOL RGB(33, 66, 133)
763 #define COL_BLACK RGB(0,0,0)
764 #define COL_WHITE RGB(255,255,255)
765 #define COL_YELLOW RGB(255,255,0)
767 static void draw_bg() {
769 console_setcolor(t
, 0, BGCOL
);
770 for(y
=0; y
< dim
.h
; y
++) {
771 console_goto(t
, 0, y
);
772 for(x
= 0; x
< dim
.w
; x
++)
773 console_printchar(t
, ' ', 0);
777 static unsigned reduce_color(unsigned val
) {
779 if (colorcount
<= 8) {
782 static const unsigned tbl
[] = {0, 127, 255};
788 static int get_r(unsigned percent
) {
789 return reduce_color((50 - percent
/2) * 5);
791 static int get_g(unsigned percent
) {
792 return reduce_color(percent
/2 * 5);
794 static int get_a(unsigned age
) {
795 return reduce_color(5+((50 - age
)*5));
797 #define LINES_PER_NET 1
798 static void selection_move(int dir
) {
799 if((int)selection
+dir
< 0) dir
=0;
801 unsigned l
= DYNA_COUNT(wlans
);
803 if((int)selection
+dir
>= l
||
804 ((int)selection
+dir
)*LINES_PER_NET
+1 >= dim
.h
) dir
=0;
808 static volatile unsigned bms
;
809 static void set_bms(float percent
) {
810 float max
= 800, min
=50;
812 float rpercent
= range
/100.f
;
813 bms
= min
+ (100 - percent
) * rpercent
;
816 char *mac2str(unsigned char mac
[static 6], char buf
[static 18]) {
818 char hextab
[16] = "0123456789abcdef";
819 for(m
= 0, x
=0 ; m
<6; m
++, x
+=3) {
820 buf
[x
] = hextab
[mac
[m
]>>4];
821 buf
[x
+1] = hextab
[mac
[m
]&15];
828 static char* format_timestamp(uint64_t timestamp
, char *ts
) {
829 #define TSTP_SEC 1000000ULL /* 1 MHz clock -> 1 million ticks/sec */
830 #define TSTP_MIN (TSTP_SEC * 60ULL)
831 #define TSTP_HOUR (TSTP_MIN * 60ULL)
832 #define TSTP_DAY (TSTP_HOUR * 24ULL)
834 unsigned days
, hours
, mins
, secs
;
835 days
= timestamp
/ TSTP_DAY
;
836 rem
= timestamp
% TSTP_DAY
;
837 hours
= rem
/ TSTP_HOUR
;
839 mins
= rem
/ TSTP_MIN
;
841 secs
= rem
/ TSTP_SEC
;
842 sprintf(ts
, "%ud %02u:%02u:%02u", days
, hours
, mins
, secs
);
846 static const char* enctype_str(enum enctype et
) {
847 static const char enc_name
[][5] = {
853 if(et
> ET_MAX
) abort();
857 static char* sanitize_string(char *s
, char *new) {
858 size_t i
,j
, l
= strlen(s
), ls
=l
;
859 for(i
=0,j
=0;i
<ls
;i
++) {
860 if(s
[i
] < ' ' || s
[i
] > 127) {
861 sprintf(new + j
, "\\x%02x", s
[i
] & 0xff);
863 } else new[j
] = s
[i
];
870 static unsigned gray_shade_from_timestamp(long long last_seen
) {
871 long long now
= getutime64();
872 long long age_ms
= (now
- last_seen
)/1000;
873 age_ms
=MIN(5000, age_ms
)/100; /* seems we end up with a range 0-50 */
874 return get_a(age_ms
);
877 #define ESSID_PRINT_START 1
878 #define ESSID_PRINT_END 32+ESSID_PRINT_START
879 #define ESSID_PRINT_LEN (ESSID_PRINT_END - ESSID_PRINT_START)
881 static void dump_wlan_info(unsigned wlanidx
) {
882 struct wlaninfo wtmp
, *w
= &wtmp
;
883 get_wlan(wlanidx
, w
);
884 unsigned line
= 3, x
, col1
, col2
, col3
, col4
;
885 console_setcolor(t
, 0, BGCOL
);
886 console_setcolor(t
, 1, COL_WHITE
);
889 console_goto(t
, ++x
, line
);
891 console_printf(t
, "MAC %s", mac2str(w
->mac
, macbuf
));
895 console_goto(t
, ++x
, line
);
896 console_printf(t
, "CHAN %d", (int) w
->channel
);
900 console_goto(t
, ++x
, line
);
902 format_timestamp(w
->timestamp
, ts
);
903 console_printf(t
, "UP: %s", ts
);
907 console_goto(t
, ++x
, line
);
908 console_printf(t
, "BI %d ms", (int) w
->beaconinterval
);
913 console_goto(t
, ++x
, line
);
914 console_printf(t
, "AVG %.2f dBm", (double)w
->total_rssi
/(double)w
->count
);
918 console_goto(t
, ++x
, line
);
919 console_printf(t
, "CURR %d dBm", w
->last_rssi
);
923 console_goto(t
, ++x
, line
);
924 console_printf(t
, "MIN %d dBm", w
->min_rssi
);
928 console_goto(t
, ++x
, line
);
929 console_printf(t
, "MAX %d dBm", w
->max_rssi
);
934 console_goto(t
, ++x
, line
);
935 console_printf(t
, "%4s", enctype_str(w
->enctype
));
938 console_goto(t
, ++x
, line
);
939 if(w
->wps
) console_printf(t
, "WPS %d.%d", w
->wps
->version
>> 4, w
->wps
->version
& 15);
942 console_goto(t
, ++x
, line
);
943 if(w
->wps
) console_printf(t
, w
->wps
->locked
== 1 ? "LOCKED" : "-");
945 char sanbuf
[WPS_MAX_STR_LEN
*4+1];
948 console_goto(t
, ++x
, line
);
949 if(w
->wps
&& w
->wps
->manufacturer
[0]) {
950 sanitize_string(w
->wps
->manufacturer
, sanbuf
);
951 console_printf(t
, "%s", sanbuf
);
957 console_goto(t
, ++x
, line
);
958 if(w
->wps
&& w
->wps
->model_name
[0]) {
959 sanitize_string(w
->wps
->model_name
, sanbuf
);
960 console_printf(t
, "%s", sanbuf
);
964 console_goto(t
, ++x
, line
);
965 if(w
->wps
&& w
->wps
->model_number
[0]) {
966 sanitize_string(w
->wps
->model_number
, sanbuf
);
967 console_printf(t
, "%s", sanbuf
);
971 console_goto(t
, ++x
, line
);
972 if(w
->wps
&& w
->wps
->device_name
[0]) {
973 sanitize_string(w
->wps
->device_name
, sanbuf
);
974 console_printf(t
, "%s", sanbuf
);
978 console_goto(t
, ++x
, line
);
979 if(w
->wps
&& w
->wps
->serial
[0]) {
980 sanitize_string(w
->wps
->serial
, sanbuf
);
981 console_printf(t
, "%s", sanbuf
);
987 console_goto(t
, ++x
, line
++);
988 console_printf(t
, "CLIENT"
989 "%*s%s" "%*s%s" "%*s%s" "%*s%s",
997 for(c
= w
->clients
; c
; c
= c
->next
) {
999 unsigned a
= gray_shade_from_timestamp(c
->last_seen
);
1000 console_setcolor(t
, 1, RGB(a
,a
,a
));
1003 console_goto(t
, ++x
, line
);
1004 console_printf(t
, "%s", mac2str(c
->mac
, macbuf
));
1008 console_setcolor(t
, 1, COL_WHITE
);
1010 console_goto(t
, ++x
, line
);
1011 console_printf(t
, "%.2f dBm", (double)c
->total_rssi
/(double)c
->count
);
1014 console_goto(t
, ++x
, line
);
1015 console_printf(t
, "%d dBm", c
->last_rssi
);
1018 console_goto(t
, ++x
, line
);
1019 console_printf(t
, "%d dBm", c
->min_rssi
);
1022 console_goto(t
, ++x
, line
);
1023 console_printf(t
, "%d dBm", c
->max_rssi
);
1030 static void dump_wlan_at(unsigned wlanidx
, unsigned line
) {
1031 console_goto(t
, 0, line
);
1032 console_setcolor(t
, 0, BGCOL
);
1034 console_setcolor(t
, 1, COL_YELLOW
);
1036 if(wlanidx
== selection
) {
1037 console_printchar(t
, '>', 0);
1039 console_printchar(t
, ' ', 0);
1042 struct wlaninfo wtmp
, *w
= &wtmp
;
1043 get_wlan(wlanidx
, w
);
1045 unsigned a
= gray_shade_from_timestamp(w
->last_seen
);
1046 console_setcolor(t
, 1, RGB(a
,a
,a
));
1047 console_goto(t
, ESSID_PRINT_START
, line
);
1052 char essid_san
[32*4+1];
1053 sanitize_string(w
->essid
, essid_san
);
1054 console_printf(t
, "%*s", ESSID_PRINT_LEN
, essid_san
);
1056 console_printf(t
, "<hidden> %*s", ESSID_PRINT_LEN
-9, mac2str(w
->mac
, macbuf
));
1058 console_goto(t
, ESSID_PRINT_END
, line
);
1059 console_printchar(t
, ' ', 0);
1061 int scale
= max
- min
;
1062 int width
= dim
.w
- (ESSID_PRINT_LEN
+2);
1064 float widthpercent
= (float)width
/100.f
;
1065 float scalepercent
= (float)scale
/100.f
;
1066 float scaleup
= (float)width
/ (float)scale
;
1067 double avg
= (double)w
->total_rssi
/(double)w
->count
;
1068 float avg_percent
= (avg
- (float)min
) / scalepercent
;
1069 float curr_percent
= ((float)w
->last_rssi
- (float)min
) / scalepercent
;
1070 int avg_marker
= (avg
- (float)min
) * scaleup
;
1071 int curr_marker
= ((float)w
->last_rssi
- (float)min
) * scaleup
;
1073 for(x
= 0; x
< width
; x
++) {
1075 if(wlanidx
== selection
) step_color
= RGB(get_r(x
/widthpercent
),get_g(x
/widthpercent
),0);
1076 else step_color
= RGB(get_r(x
/widthpercent
),get_r(x
/widthpercent
),get_r(x
/widthpercent
));
1077 console_setcolor(t
, 0, step_color
);
1078 if(x
!= curr_marker
) console_setcolor(t
, 1, COL_BLACK
);
1079 else console_setcolor(t
, 1, COL_WHITE
);
1080 if(x
== avg_marker
) console_printchar(t
, 'I', 0);
1081 else if (x
== curr_marker
) console_printchar(t
, '|', 0);
1082 else if(x
== 0) console_printchar(t
, '[', 0);
1083 else if(x
== width
-1) console_printchar(t
, ']', 0);
1084 else console_printchar(t
, ' ', 0);
1088 static void dump_wlan(unsigned idx
) {
1089 if(idx
* LINES_PER_NET
+ 1 > dim
.h
|| (selected
&& selection
!= idx
)) return;
1090 dump_wlan_at(idx
, selected
? 1 : idx
* LINES_PER_NET
);
1091 if(selected
) dump_wlan_info(idx
);
1094 int this_wlan_scale_mode
= 1;
1095 static void calc_bms(unsigned wlanidx
) {
1096 long long now
= getutime64();
1097 struct wlaninfo wtmp
, *w
= &wtmp
;
1098 get_wlan(wlanidx
, w
);
1099 int my_min
= this_wlan_scale_mode
? w
->min_rssi
: min
;
1100 int my_max
= this_wlan_scale_mode
? w
->max_rssi
: max
;
1101 long long age_ms
= (now
- w
->last_seen
)/1000;
1102 age_ms
=MIN(5000, age_ms
)/100; /* seems we end up with a range 0-50 */
1103 int scale
= my_max
- my_min
;
1104 float scalepercent
= (float)scale
/100.f
;
1105 float curr_percent
= ((float)w
->last_rssi
- (float)my_min
) / scalepercent
;
1106 if(age_ms
< 15) set_bms(curr_percent
);
1110 static void dump(void) {
1113 l
= DYNA_COUNT(wlans
);
1115 //dprintf(1, "********************\n");
1122 static void initconcol() {
1126 if((p
= getenv("RES"))) {
1127 char *q
= strchr(p
, 'x');
1138 point reso
= {rw
, rh
};
1139 console_init_graphics(&co
, reso
, FONT
);
1140 console_getbounds(t
, &dim
.w
, &dim
.h
);
1141 colorcount
= console_getcolorcount(t
);
1143 (*console_setcolor
)(t
, 0, COL_WHITE
);
1144 (*console_setcolor
)(t
, 1, COL_BLACK
);
1149 static unsigned char blip
[] = {0x52, 0x51, 0x51, 0x51, 0xC4, 0x4C, 0xF4, 0xF4, 0xF3,0xEF};
1150 static unsigned blip_frame(int idx
) {
1151 idx
= idx
% (2*sizeof(blip
));
1152 if(idx
>=sizeof(blip
)) idx
=(2*sizeof(blip
))-idx
;
1156 static volatile float volume
= .5;
1158 static void generate_blip(unsigned char* data
, size_t bufsize
) {
1160 for(i
=0;i
<bufsize
;i
++) {
1161 float f
= blip_frame(i
) * volume
;
1166 static void volume_change(int dir
) {
1167 volume
+= dir
* 0.1;
1168 if(volume
< 0) volume
= 0;
1169 if(volume
> 1) volume
= 1;
1172 static void* blip_thread(void* arg
) {
1175 unsigned char buf
[100], silence
[1000];
1176 generate_blip(buf
, sizeof(buf
));
1177 memset(silence
, buf
[99], sizeof silence
);
1178 long long t
= getutime64();
1179 unsigned passed
= 0;
1180 float myvol
= volume
;
1182 if(myvol
!= volume
) {
1183 generate_blip(buf
, sizeof(buf
));
1186 if(bms
&& (getutime64() - t
)/1000 >= bms
) {
1187 audio_write(&ao
, buf
, sizeof buf
);
1190 audio_write(&ao
, silence
, sizeof silence
);
1197 static void* nop_thread(void *arg
) {
1201 static void* chanwalker_thread(void* arg
) {
1203 int channel
= 1, delay
= 800;
1205 if(filebased
) return 0;
1207 if((getutime64() - tm
)/1000 >= delay
) {
1208 int ret
= set_channel(itf
, channel
= next_chan(channel
));
1210 if(console_getbackendtype(t
) == cb_sdl
)
1211 dprintf(2, "oops couldnt switch to chan %d\n", channel
);
1219 #include <sys/ioctl.h>
1220 #include <sys/socket.h>
1221 /* set an interface up or down, depending on whether up is set.
1222 if checkonly is true, no change will be made and the result
1223 of the function can be interpreted as "isdownup".
1224 if the interface was already up/down, 2 is returned.
1225 if the interface was successfully upped/downed, 1 is returned.
1226 0 is only returned if checkonly is set and the interface was not
1227 in the queried state.
1228 -1 is returned on error. */
1229 static int ifdownup(const char *dev
, int up
, int checkonly
) {
1231 if((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) return -1;
1232 struct ifreq ifr
= {0};
1233 strcpy(ifr
.ifr_name
, dev
);
1234 if(ioctl(fd
, SIOCGIFFLAGS
, &ifr
) <0) goto done
;
1235 int isup
= ifr
.ifr_flags
& IFF_UP
;
1236 if((up
&& isup
) || (!up
&& !isup
)) ret
= 2;
1237 else if (checkonly
) ret
= 0;
1239 if(up
) ifr
.ifr_flags
|= IFF_UP
;
1240 else ifr
.ifr_flags
&= ~(IFF_UP
);
1241 ret
= (ioctl(fd
, SIOCSIFFLAGS
, &ifr
) >= 0);
1248 #include "wireless-lite.h"
1249 static int getiwmode(const char *dev
) {
1251 if((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) return -1;
1252 struct iwreq iwr
= {0};
1253 strcpy(iwr
.ifr_name
, dev
);
1254 if(ioctl(fd
, SIOCGIWMODE
, &iwr
) >=0) ret
= iwr
.u
.mode
;
1259 static int setiwmode(const char *dev
, int mode
) {
1261 if((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) return -1;
1262 struct iwreq iwr
= {.u
.mode
= mode
};
1263 strcpy(iwr
.ifr_name
, dev
);
1264 ret
= ioctl(fd
, SIOCSIWMODE
, &iwr
);
1269 static void* capture_thread(void*arg
) {
1272 int ret
= process_frame(foo
);
1273 long long tmp
= getutime64();
1276 wlans
[ret
].last_seen
= tmp
;
1283 static pthread_t bt
, wt
;
1284 static const char *itf
;
1286 static void set_selection(int on
) {
1289 pthread_join(wt
, 0);
1291 pthread_create(&bt
, 0, blip_thread
, 0);
1294 int ch
= wlans
[selection
].channel
;
1296 set_channel(itf
, ch
);
1299 pthread_create(&wt
, 0, chanwalker_thread
, (void*)itf
);
1300 pthread_join(bt
, 0);
1307 int main(int argc
,char**argv
) {
1308 wlans
= DYNA_NEW(struct wlaninfo
);
1311 while((c
= getopt(argc
, argv
, "c:")) != EOF
) switch(c
) {
1312 case 'c': fixed_chan
= atoi(optarg
); break;
1313 default: return usage(argv
[0]);
1315 if(!argv
[optind
]) return usage(argv
[0]);
1321 char errbuf
[PCAP_ERRBUF_SIZE
];
1323 if(strchr(itf
, '.') && access(itf
, R_OK
) == 0) {
1325 foo
= pcap_open_offline(itf
, errbuf
);
1327 foo
= pcap_create(itf
, errbuf
);
1329 snprintf(fnbuf
, sizeof fnbuf
, "tmp.%s.pcap", itf
);
1330 outfd
= open(fnbuf
, O_WRONLY
|O_CREAT
|O_TRUNC
,0660);
1332 pcapfile_write_header(outfd
);
1334 if(!foo
) { dprintf(2, "%s\n", errbuf
); return 1; }
1336 int ret
, wasdown
, orgmode
;
1338 if(filebased
) goto skip
;
1340 if((orgmode
= getiwmode(itf
)) != IW_MODE_MONITOR
) {
1341 if((ret
= ifdownup(itf
, 0, 0)) == -1) {
1343 perror("error setting up interface - maybe need to run as root.");
1345 wasdown
= (ret
== 2);
1346 if(setiwmode(itf
, IW_MODE_MONITOR
) == -1) goto iferr
;
1348 wasdown
= (ifdownup(itf
, 0, 1) == 2);
1350 if(ifdownup(itf
, 1, 0) == -1) goto iferr
;
1352 if(pcap_activate(foo
)) {
1353 dprintf(2, "pcap_activate failed: %s\n", pcap_geterr(foo
));
1361 signal(SIGINT
, sigh
);
1363 int channel
= fixed_chan
? fixed_chan
: 1;
1364 if(fixed_chan
) set_channel(itf
, fixed_chan
);
1367 void *(*cw_func
)(void*);
1368 if(filebased
|| fixed_chan
) cw_func
= nop_thread
;
1369 else cw_func
= chanwalker_thread
;
1370 pthread_create(&wt
, 0, cw_func
, (void*) itf
);
1371 pthread_create(&ct
, 0, capture_thread
, foo
);
1373 struct netgui_config netgui_cfg
;
1374 if(getenv("NETGUI")) {
1375 netgui_start(&netgui_cfg
, "0.0.0.0", 9876);
1379 long long tmp
= getutime64();
1380 if((tmp
-tm
) >= (1000000 / GUI_FPS
)) {
1385 if(selected
) calc_bms(selection
);
1386 int k
= console_getkey_nb(t
);
1389 case CK_CURSOR_RIGHT
: this_wlan_scale_mode
= !this_wlan_scale_mode
; break;
1390 case '+': case '0': volume_change(+1); break;
1391 case '-': case '9': volume_change(-1); break;
1392 case CK_CURSOR_DOWN
: selection_move(1);break;
1393 case CK_CURSOR_UP
: selection_move(-1);break;
1395 //selected = !selected;
1396 set_selection(!selected
);
1399 case CK_ESCAPE
: stop
= 1; break;
1404 pcap_breakloop(foo
); // this doesn't actually seem to work
1406 if(getenv("NETGUI")) {
1407 netgui_stop(&netgui_cfg
);
1412 pthread_join(bt
, 0);
1415 pthread_join(wt
, 0);
1418 // since our capture_thread uses blocking reads in order to keep CPU usage
1419 // minimal, we need to get the current read cancelled - and if no packets
1420 // arrive, this can take a *long* time. since pcap_breakloop() doesn't actually
1421 // seem to work, the only way i found to break out of the read is to actually
1422 // bring down the interface - so this must happen before we join the thread
1423 // and close the pcap handle.
1425 if(wasdown
|| orgmode
!= IW_MODE_MONITOR
) ifdownup(itf
, 0, 0);
1426 if(orgmode
!= IW_MODE_MONITOR
) setiwmode(itf
, orgmode
);
1427 if(!wasdown
&& orgmode
!= IW_MODE_MONITOR
) ifdownup(itf
, 1, 0);
1430 pthread_join(ct
, 0);
1434 if(outfd
!= -1) close(outfd
);