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);
236 if(!w
->clients
) w
->clients
= c
;
239 while(it
->next
) it
=it
->next
;
245 static long long getutime64(void);
247 static void set_client_rssi(struct wlaninfo
* w
, struct ap_client
*c
) {
249 c
->total_rssi
+= w
->last_rssi
;
250 c
->last_seen
= getutime64();
251 c
->last_rssi
= w
->last_rssi
;
252 c
->min_rssi
= MIN(c
->min_rssi
, c
->last_rssi
);
253 c
->max_rssi
= MAX(c
->max_rssi
, c
->last_rssi
);
256 static int set_rssi(struct wlaninfo
*w
, struct wps_data
* wps
) {
258 struct wlaninfo wtmp
, *d
= &wtmp
;
259 if(i
== -1) i
= get_wlan_by_mac(w
->mac
);
260 if(i
== -1) i
= get_new_wlan();
263 if(w
->essid
[0]) strcpy(d
->essid
, w
->essid
);
264 memcpy(d
->mac
, w
->mac
, 6);
265 d
->total_rssi
+= w
->last_rssi
;
267 d
->last_rssi
= w
->last_rssi
;
268 d
->channel
= w
->channel
;
269 d
->timestamp
= w
->timestamp
;
270 d
->beaconinterval
= w
->beaconinterval
;
271 d
->min_rssi
= MIN(d
->min_rssi
, d
->last_rssi
);
272 d
->max_rssi
= MAX(d
->max_rssi
, d
->last_rssi
);
273 d
->enctype
= w
->enctype
;
276 d
->wps
= malloc(sizeof *wps
);
277 if(d
->wps
) init_wps_data(d
->wps
);
280 if(!wps
->manufacturer
[0]) {
281 d
->wps
->version
= wps
->version
;
282 d
->wps
->state
= wps
->state
;
283 d
->wps
->locked
= wps
->locked
;
285 memcpy(d
->wps
, wps
, sizeof(*wps
));
298 #include "radiotap_flags.h"
300 static unsigned get_flags_off(unsigned flags
, unsigned start_off
) {
301 return rt_get_flag_offset(flags
, IEEE80211_RADIOTAP_FLAGS
, start_off
);
304 static unsigned get_dbm_off(unsigned flags
, unsigned start_off
) {
305 return rt_get_flag_offset(flags
, IEEE80211_RADIOTAP_DBM_ANTSIGNAL
, start_off
);
308 static unsigned get_chan_off(unsigned flags
, unsigned start_off
) {
309 return rt_get_flag_offset(flags
, IEEE80211_RADIOTAP_CHANNEL
, start_off
);
312 static unsigned channel_from_freq(unsigned freq
) {
313 return freq
==2484?14:(freq
-2407)/5;
317 uint16_t framecontrol
;
319 unsigned char receiver
[6];
320 unsigned char source
[6];
321 unsigned char bssid
[6];
322 uint16_t sequence_no
;
325 static unsigned char* find_tag(unsigned const char *tagdata
, unsigned tag
, unsigned bytes_left
) {
327 if(*tagdata
== tag
) return (unsigned char*)tagdata
;
328 unsigned tagsize
= tagdata
[1];
330 if(bytes_left
< 2+tagsize
) return 0;
331 bytes_left
-=2+tagsize
;
336 static long long timeval2utime(struct timeval
*t
) {
337 return (t
->tv_sec
* 1000LL * 1000LL) + t
->tv_usec
;
340 static long long getutime64(void) {
342 gettimeofday(&t
, NULL
);
343 return timeval2utime(&t
);
347 static int filebased
;
349 static const unsigned char* pcap_next_wrapper(pcap_t
*foo
, struct pcap_pkthdr
*h_out
) {
352 const unsigned char* ret
= 0;
353 struct pcap_pkthdr
*hdr_temp
;
354 int err
= pcap_next_ex(foo
, &hdr_temp
, &ret
);
356 /* skip malformed packets, like those emitted by busybox udhcpc */
357 struct ieee80211_radiotap_header
*rh
= (void*) ret
;
358 if(rh
->it_version
!= 0 || end_le16toh(rh
->it_len
) > hdr_temp
->len
)
363 if(ret
&& outfd
!= -1){
364 pcapfile_write_packet(outfd
, h_out
, ret
);
368 static long long pcap_file_start_time
, start_time
;
369 static unsigned char buf
[2][2048];
370 static struct pcap_pkthdr h
[2];
372 const unsigned char* ret
;
373 if(start_time
== 0 || getutime64() - start_time
>= timeval2utime(&h
[!actbuf
].ts
) - pcap_file_start_time
) {
374 ret
= pcap_next(foo
, h_out
);
377 assert(h
[actbuf
].len
<= sizeof buf
[actbuf
]);
378 memcpy(buf
[actbuf
], ret
, h
[actbuf
].len
);
382 start_time
= getutime64();
384 pcap_file_start_time
= timeval2utime(&h_out
->ts
);
395 static inline int myisascii(int x
) {
396 return x
>= ' ' && x
< 127;
399 static void dump_packet(const unsigned char* data
, size_t len
) {
400 static const char atab
[] = "0123456789abcdef";
401 char hex
[24*2+1], ascii
[24+1];
402 unsigned h
= 0, a
= 0;
407 hex
[h
++] = atab
[*data
>> 4];
408 hex
[h
++] = atab
[*data
& 0xf];
409 ascii
[a
++] = myisascii(*data
) ? *data
: '.';
414 printf("%s\t%s\n", hex
, ascii
);
416 if(fill
== '_') return; /* jump from filler */
437 void setminmax(int val
) {
441 snprintf(mmbuf
, sizeof mmbuf
, "min: %d, max: %d", min
, max
);
442 console_settitle(t
, mmbuf
);
445 static int get_next_ie(const unsigned char *data
, size_t len
, size_t *currpos
) {
446 if(*currpos
+ 2 >= len
) return 0;
447 *currpos
= *currpos
+ 2 + data
[*currpos
+ 1];
448 if(*currpos
>= len
) return 0;
452 static int get_next_wps_el(const unsigned char *data
, size_t len
, size_t *currpos
) {
453 if(*currpos
+ 4 >= len
) return 0;
455 memcpy(&el_len
, data
+ 2 + *currpos
, 2);
456 el_len
= end_be16toh(el_len
);
457 *currpos
= *currpos
+ 4 + el_len
;
458 if(*currpos
>= len
) return 0;
462 static void process_wps_tag(const unsigned char* tag
, size_t len
, struct wps_data
*wps
) {
463 unsigned const char *el
;
465 size_t el_iterator
= 0, wfa_iterator
, remain
;
466 uint16_t el_id
, el_len
;
470 el
= tag
+ el_iterator
;
471 remain
= len
- el_iterator
;
472 memcpy(&el_id
, el
, 2);
473 el_id
= end_be16toh(el_id
);
474 memcpy(&el_len
, el
+2, 2);
475 el_len
= end_be16toh(el_len
);
479 case 0x104A: /* WPS_VERSION */
482 case 0x1044: /* WPS_STATE */
485 case 0x1057: /* WPS_LOCKED */
488 case 0x1021: /* WPS_MANUFACTURER */
489 str
= wps
->manufacturer
;
491 case 0x1023: /*WPS_MODEL_NAME */
492 str
= wps
->model_name
;
495 str
= wps
->model_number
;
498 str
= wps
->device_name
;
511 str
= wps
->selected_registrar
;
515 str
= wps
->response_type
;
519 str
= wps
->primary_device_type
;
523 str
= wps
->config_methods
;
530 str
= wps
->os_version
;
532 case 0x1049: /* WPS_VENDOR_EXTENSION */
533 if(el_len
>= 5 && !memcmp(el
, "\x00\x37\x2A", 3)) { /* WFA_EXTENSION */
538 if(wfa_iterator
+2 <= el_len
&& el
[wfa_iterator
] == 0 /* WPS_VERSION2_ID */) {
539 wps
->version
= el
[2];
541 } while(get_next_ie(el
, el_len
, &wfa_iterator
));
548 max
= el_len
>= WPS_MAX_STR_LEN
/2 ? WPS_MAX_STR_LEN
/2 - 1 : el_len
;
550 sprintf(str
, "%02x", *el
);
556 max
= el_len
+ 1 >= WPS_MAX_STR_LEN
? WPS_MAX_STR_LEN
: el_len
+ 1;
557 snprintf(str
, max
, "%s", el
);
561 } while(get_next_wps_el(tag
, len
, &el_iterator
));
565 static void process_tags(const unsigned char* tagdata
, size_t tagdata_len
, struct wlaninfo
*temp
, struct wps_data
*wps
) {
566 unsigned const char *tag
;
568 /* iterate through tags */
569 size_t ie_iterator
= 0, remain
;
571 tag
= tagdata
+ ie_iterator
;
572 remain
= tagdata_len
- ie_iterator
;
574 case 0: /* essid tag */
575 if(tag
[1] <= remain
) {
576 memcpy(temp
->essid
, tag
+2, tag
[1]);
577 temp
->essid
[tag
[1]] = 0;
580 case 3: /* chan nr */
582 temp
->channel
= tag
[2];
584 case 0x30: /* RSN_TAG_NUMBER */
585 temp
->enctype
= ET_WPA2
;
587 case 0xDD: /* VENDOR_SPECIFIC_TAG*/
588 if(tag
[1] >= remain
) break;
590 !memcmp(tag
+2, "\x00\x50\xF2\x01\x01\x00", 6))
591 temp
->enctype
= ET_WPA
;
592 if(tag
[1] > 4 && !memcmp(tag
+2, "\x00\x50\xf2" /*micro$oft*/ "\x04" /*type WPS*/, 4))
593 process_wps_tag(tag
+2+4, tag
[1]-4, wps
);
597 } while(get_next_ie(tagdata
, tagdata_len
, &ie_iterator
));
600 static int process_frame(pcap_t
*foo
) {
601 struct pcap_pkthdr h
;
602 const unsigned char* data
= pcap_next_wrapper(foo
, &h
);
604 if(console_getbackendtype(t
) == cb_sdl
&& getenv("DEBUG")) dump_packet(data
, h
.len
);
606 uint32_t flags
, offset
, fchksum
;
608 if(!rt_get_presentflags(data
, h
.len
, &flags
, &offset
))
611 struct ieee80211_radiotap_header
*rh
= (void*) data
;
613 unsigned rtap_data
= offset
;
615 if(flags
& (1U << IEEE80211_RADIOTAP_FLAGS
)) {
616 unsigned flags_off
= get_flags_off(flags
, rtap_data
);
617 if(data
[flags_off
] & 0x10 /* IEEE80211_RADIOTAP_F_FCS */) {
618 /* TODO handle bad FCS IEEE80211_RADIOTAP_F_BADFCS 0x40 */
619 memcpy(&fchksum
, data
+ h
.len
- 4, 4);
620 fchksum
= end_le32toh(fchksum
);
625 struct wlaninfo temp
= {0};
627 if(!(flags
& (1U << IEEE80211_RADIOTAP_DBM_ANTSIGNAL
))) return -1;
628 unsigned dbmoff
= get_dbm_off(flags
, rtap_data
);
629 temp
.last_rssi
= ((signed char*)data
)[dbmoff
];
632 // if(!(flags & (1U << IEEE80211_RADIOTAP_CHANNEL))) return -1;
634 unsigned chanoff
= get_chan_off(flags
, rtap_data
);
635 memcpy(&freq
, data
+ chanoff
, 2);
636 temp
.channel
= channel_from_freq(freq
);
638 uint16_t framectl
, fctype
;
639 offset
= end_le16toh(rh
->it_len
);
640 memcpy(&framectl
, data
+offset
, 2);
641 framectl
= end_le16toh(framectl
);
642 struct dot11frame
* beacon
;
643 unsigned const char* tagdata
;
650 /* IEEE 802.11 packet type */
651 case 0x0080: /* beacon */
652 case 0x0050: /* probe response */
653 beacon
= (void*)(data
+offset
);
654 memcpy(&temp
.mac
,beacon
->source
,6);
655 offset
+= sizeof(*beacon
);
656 memcpy(&temp
.timestamp
,data
+offset
,8);
657 temp
.timestamp
= end_le64toh(temp
.timestamp
);
659 memcpy(&temp
.beaconinterval
, data
+offset
,2);
660 temp
.beaconinterval
= end_le16toh(temp
.beaconinterval
);
662 memcpy(&caps
, data
+offset
, 2);
663 caps
= end_le16toh(caps
);
664 if(caps
& 0x10 /* CAPABILITY_WEP */)
665 temp
.enctype
= ET_WEP
;
669 tagdata_len
= h
.len
-pos
;
671 process_tags(tagdata
, tagdata_len
, &temp
, &wps
);
672 setminmax(temp
.last_rssi
);
673 return set_rssi(&temp
, &wps
);
677 case 0x0040: /* probe request */
680 fctype
= framectl
& end_htole16(IEEE80211_FCTL_FTYPE
| IEEE80211_FCTL_STYPE
);
681 if(fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_DATA
) ||
682 fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_QOS_DATA
) ||
683 fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_NULLFUNC
) ||
684 fctype
== end_htole16(IEEE80211_FTYPE_DATA
| IEEE80211_STYPE_QOS_NULLFUNC
)
686 beacon
= (void*)(data
+offset
);
687 if(!memcmp(beacon
->receiver
, "\x01\x80\xc2", 3) ||
688 !memcmp(beacon
->receiver
, "\x01\x00\x0c\xcc\xcc\xcc", 6) ||
689 !memcmp(beacon
->receiver
, "\xff\xff\xff", 3))
692 int wifi_nr
= get_wlan_by_mac(beacon
->bssid
);
693 /* ignore packets sent to unknown APs */
694 if(wifi_nr
== -1) return -1;
695 unsigned char *client_mac
;
696 if(memcmp(beacon
->source
, beacon
->bssid
, 6))
698 client_mac
= beacon
->source
;
701 client_mac
= beacon
->receiver
;
703 struct wlaninfo apbuf
, *ap
=&apbuf
;
704 get_wlan(wifi_nr
, ap
);
705 struct ap_client
* c
= get_client(ap
, client_mac
);
706 if(!c
) c
= add_client(ap
, client_mac
);
707 write_wlan(wifi_nr
, ap
);
708 setminmax(temp
.last_rssi
);
709 if(client_mac
== beacon
->source
) {
710 set_client_rssi(&temp
, c
);
712 } else return wifi_nr
;
719 //while(htonl(*(flags++)) & (1U << IEEE80211_RADIOTAP_EXT)) next_chunk+=4;
720 //dprintf(2, "got data\n");
727 static int next_chan(int chan
) {
728 if(++chan
> 11) chan
= 1;
732 static int next_chan(int chan
) {
733 static char chanlist
[]={1,5,9,13,2,6,10,14,3,7,11,4,8,12};
735 for(i
= 0; i
< sizeof chanlist
&& chanlist
[i
] != chan
; i
++);
736 if(i
>=13) return chanlist
[0];
737 return chanlist
[++i
];
740 static int next_chan(int chan
) {
742 case 1: case 2: case 3: case 4: case 5:
744 case 6: case 7: case 8: case 9: case 10:
746 case 11: case 12: case 13:
747 /* uncomment next line if you leave in a country using chan 14 */
758 static struct {int w
, h
;} dim
;
760 #define BGCOL RGB(33, 66, 133)
761 #define COL_BLACK RGB(0,0,0)
762 #define COL_WHITE RGB(255,255,255)
763 #define COL_YELLOW RGB(255,255,0)
765 static void draw_bg() {
767 console_setcolor(t
, 0, BGCOL
);
768 for(y
=0; y
< dim
.h
; y
++) {
769 console_goto(t
, 0, y
);
770 for(x
= 0; x
< dim
.w
; x
++)
771 console_printchar(t
, ' ', 0);
775 static unsigned reduce_color(unsigned val
) {
777 if (colorcount
<= 8) {
780 static const unsigned tbl
[] = {0, 127, 255};
786 static int get_r(unsigned percent
) {
787 return reduce_color((50 - percent
/2) * 5);
789 static int get_g(unsigned percent
) {
790 return reduce_color(percent
/2 * 5);
792 static int get_a(unsigned age
) {
793 return reduce_color(5+((50 - age
)*5));
795 #define LINES_PER_NET 1
796 static void selection_move(int dir
) {
797 if((int)selection
+dir
< 0) dir
=0;
799 unsigned l
= DYNA_COUNT(wlans
);
801 if((int)selection
+dir
>= l
||
802 ((int)selection
+dir
)*LINES_PER_NET
+1 >= dim
.h
) dir
=0;
806 static volatile unsigned bms
;
807 static void set_bms(float percent
) {
808 float max
= 800, min
=50;
810 float rpercent
= range
/100.f
;
811 bms
= min
+ (100 - percent
) * rpercent
;
814 char *mac2str(unsigned char mac
[static 6], char buf
[static 18]) {
816 char hextab
[16] = "0123456789abcdef";
817 for(m
= 0, x
=0 ; m
<6; m
++, x
+=3) {
818 buf
[x
] = hextab
[mac
[m
]>>4];
819 buf
[x
+1] = hextab
[mac
[m
]&15];
826 static char* format_timestamp(uint64_t timestamp
, char *ts
) {
827 #define TSTP_SEC 1000000ULL /* 1 MHz clock -> 1 million ticks/sec */
828 #define TSTP_MIN (TSTP_SEC * 60ULL)
829 #define TSTP_HOUR (TSTP_MIN * 60ULL)
830 #define TSTP_DAY (TSTP_HOUR * 24ULL)
832 unsigned days
, hours
, mins
, secs
;
833 days
= timestamp
/ TSTP_DAY
;
834 rem
= timestamp
% TSTP_DAY
;
835 hours
= rem
/ TSTP_HOUR
;
837 mins
= rem
/ TSTP_MIN
;
839 secs
= rem
/ TSTP_SEC
;
840 sprintf(ts
, "%ud %02u:%02u:%02u", days
, hours
, mins
, secs
);
844 static const char* enctype_str(enum enctype et
) {
845 static const char enc_name
[][5] = {
851 if(et
> ET_MAX
) abort();
855 static char* sanitize_string(char *s
, char *new) {
856 size_t i
,j
, l
= strlen(s
), ls
=l
;
857 for(i
=0,j
=0;i
<ls
;i
++) {
858 if(s
[i
] < ' ' || s
[i
] > 127) {
859 sprintf(new + j
, "\\x%02x", s
[i
] & 0xff);
861 } else new[j
] = s
[i
];
868 #define ESSID_PRINT_START 1
869 #define ESSID_PRINT_END 32+ESSID_PRINT_START
870 #define ESSID_PRINT_LEN (ESSID_PRINT_END - ESSID_PRINT_START)
872 static void dump_wlan_info(unsigned wlanidx
) {
873 struct wlaninfo wtmp
, *w
= &wtmp
;
874 get_wlan(wlanidx
, w
);
875 unsigned line
= 3, x
, col1
, col2
, col3
, col4
;
876 console_setcolor(t
, 0, BGCOL
);
877 console_setcolor(t
, 1, COL_WHITE
);
880 console_goto(t
, ++x
, line
);
882 console_printf(t
, "MAC %s", mac2str(w
->mac
, macbuf
));
886 console_goto(t
, ++x
, line
);
887 console_printf(t
, "CHAN %d", (int) w
->channel
);
891 console_goto(t
, ++x
, line
);
893 format_timestamp(w
->timestamp
, ts
);
894 console_printf(t
, "UP: %s", ts
);
898 console_goto(t
, ++x
, line
);
899 console_printf(t
, "BI %d ms", (int) w
->beaconinterval
);
904 console_goto(t
, ++x
, line
);
905 console_printf(t
, "AVG %.2f dBm", (double)w
->total_rssi
/(double)w
->count
);
909 console_goto(t
, ++x
, line
);
910 console_printf(t
, "CURR %d dBm", w
->last_rssi
);
914 console_goto(t
, ++x
, line
);
915 console_printf(t
, "MIN %d dBm", w
->min_rssi
);
919 console_goto(t
, ++x
, line
);
920 console_printf(t
, "MAX %d dBm", w
->max_rssi
);
925 console_goto(t
, ++x
, line
);
926 console_printf(t
, "%4s", enctype_str(w
->enctype
));
929 console_goto(t
, ++x
, line
);
930 if(w
->wps
) console_printf(t
, "WPS %d.%d", w
->wps
->version
>> 4, w
->wps
->version
& 15);
933 console_goto(t
, ++x
, line
);
934 if(w
->wps
) console_printf(t
, w
->wps
->locked
== 1 ? "LOCKED" : "-");
936 char sanbuf
[WPS_MAX_STR_LEN
*4+1];
939 console_goto(t
, ++x
, line
);
940 if(w
->wps
&& w
->wps
->manufacturer
[0]) {
941 sanitize_string(w
->wps
->manufacturer
, sanbuf
);
942 console_printf(t
, "%s", sanbuf
);
948 console_goto(t
, ++x
, line
);
949 if(w
->wps
&& w
->wps
->model_name
[0]) {
950 sanitize_string(w
->wps
->model_name
, sanbuf
);
951 console_printf(t
, "%s", sanbuf
);
955 console_goto(t
, ++x
, line
);
956 if(w
->wps
&& w
->wps
->model_number
[0]) {
957 sanitize_string(w
->wps
->model_number
, sanbuf
);
958 console_printf(t
, "%s", sanbuf
);
962 console_goto(t
, ++x
, line
);
963 if(w
->wps
&& w
->wps
->device_name
[0]) {
964 sanitize_string(w
->wps
->device_name
, sanbuf
);
965 console_printf(t
, "%s", sanbuf
);
969 console_goto(t
, ++x
, line
);
970 if(w
->wps
&& w
->wps
->serial
[0]) {
971 sanitize_string(w
->wps
->serial
, sanbuf
);
972 console_printf(t
, "%s", sanbuf
);
977 for(c
= w
->clients
; c
; c
= c
->next
) {
979 console_goto(t
, ++x
, line
);
980 console_printf(t
, "client %s", mac2str(c
->mac
, macbuf
));
985 static void dump_wlan_at(unsigned wlanidx
, unsigned line
) {
986 console_goto(t
, 0, line
);
987 console_setcolor(t
, 0, BGCOL
);
989 console_setcolor(t
, 1, COL_YELLOW
);
991 if(wlanidx
== selection
) {
992 console_printchar(t
, '>', 0);
994 console_printchar(t
, ' ', 0);
997 struct wlaninfo wtmp
, *w
= &wtmp
;
998 get_wlan(wlanidx
, w
);
1000 long long now
= getutime64();
1001 long long age_ms
= (now
- w
->last_seen
)/1000;
1002 age_ms
=MIN(5000, age_ms
)/100; /* seems we end up with a range 0-50 */
1003 unsigned a
= get_a(age_ms
);
1005 console_setcolor(t
, 1, RGB(a
,a
,a
));
1006 console_goto(t
, ESSID_PRINT_START
, line
);
1011 char essid_san
[32*4+1];
1012 sanitize_string(w
->essid
, essid_san
);
1013 console_printf(t
, "%*s", ESSID_PRINT_LEN
, essid_san
);
1015 console_printf(t
, "<hidden> %*s", ESSID_PRINT_LEN
-9, mac2str(w
->mac
, macbuf
));
1017 console_goto(t
, ESSID_PRINT_END
, line
);
1018 console_printchar(t
, ' ', 0);
1020 int scale
= max
- min
;
1021 int width
= dim
.w
- (ESSID_PRINT_LEN
+2);
1023 float widthpercent
= (float)width
/100.f
;
1024 float scalepercent
= (float)scale
/100.f
;
1025 float scaleup
= (float)width
/ (float)scale
;
1026 double avg
= (double)w
->total_rssi
/(double)w
->count
;
1027 float avg_percent
= (avg
- (float)min
) / scalepercent
;
1028 float curr_percent
= ((float)w
->last_rssi
- (float)min
) / scalepercent
;
1029 int avg_marker
= (avg
- (float)min
) * scaleup
;
1030 int curr_marker
= ((float)w
->last_rssi
- (float)min
) * scaleup
;
1032 for(x
= 0; x
< width
; x
++) {
1034 if(wlanidx
== selection
) step_color
= RGB(get_r(x
/widthpercent
),get_g(x
/widthpercent
),0);
1035 else step_color
= RGB(get_r(x
/widthpercent
),get_r(x
/widthpercent
),get_r(x
/widthpercent
));
1036 console_setcolor(t
, 0, step_color
);
1037 if(x
!= curr_marker
) console_setcolor(t
, 1, COL_BLACK
);
1038 else console_setcolor(t
, 1, COL_WHITE
);
1039 if(x
== avg_marker
) console_printchar(t
, 'I', 0);
1040 else if (x
== curr_marker
) console_printchar(t
, '|', 0);
1041 else if(x
== 0) console_printchar(t
, '[', 0);
1042 else if(x
== width
-1) console_printchar(t
, ']', 0);
1043 else console_printchar(t
, ' ', 0);
1047 static void dump_wlan(unsigned idx
) {
1048 if(idx
* LINES_PER_NET
+ 1 > dim
.h
|| (selected
&& selection
!= idx
)) return;
1049 dump_wlan_at(idx
, selected
? 1 : idx
* LINES_PER_NET
);
1050 if(selected
) dump_wlan_info(idx
);
1053 int this_wlan_scale_mode
= 1;
1054 static void calc_bms(unsigned wlanidx
) {
1055 long long now
= getutime64();
1056 struct wlaninfo wtmp
, *w
= &wtmp
;
1057 get_wlan(wlanidx
, w
);
1058 int my_min
= this_wlan_scale_mode
? w
->min_rssi
: min
;
1059 int my_max
= this_wlan_scale_mode
? w
->max_rssi
: max
;
1060 long long age_ms
= (now
- w
->last_seen
)/1000;
1061 age_ms
=MIN(5000, age_ms
)/100; /* seems we end up with a range 0-50 */
1062 int scale
= my_max
- my_min
;
1063 float scalepercent
= (float)scale
/100.f
;
1064 float curr_percent
= ((float)w
->last_rssi
- (float)my_min
) / scalepercent
;
1065 if(age_ms
< 15) set_bms(curr_percent
);
1069 static void dump(void) {
1072 l
= DYNA_COUNT(wlans
);
1074 //dprintf(1, "********************\n");
1081 static void initconcol() {
1085 if((p
= getenv("RES"))) {
1086 char *q
= strchr(p
, 'x');
1097 point reso
= {rw
, rh
};
1098 console_init_graphics(&co
, reso
, FONT
);
1099 console_getbounds(t
, &dim
.w
, &dim
.h
);
1100 colorcount
= console_getcolorcount(t
);
1102 (*console_setcolor
)(t
, 0, COL_WHITE
);
1103 (*console_setcolor
)(t
, 1, COL_BLACK
);
1108 static unsigned char blip
[] = {0x52, 0x51, 0x51, 0x51, 0xC4, 0x4C, 0xF4, 0xF4, 0xF3,0xEF};
1109 static unsigned blip_frame(int idx
) {
1110 idx
= idx
% (2*sizeof(blip
));
1111 if(idx
>=sizeof(blip
)) idx
=(2*sizeof(blip
))-idx
;
1115 static volatile float volume
= .5;
1117 static void generate_blip(unsigned char* data
, size_t bufsize
) {
1119 for(i
=0;i
<bufsize
;i
++) {
1120 float f
= blip_frame(i
) * volume
;
1125 static void volume_change(int dir
) {
1126 volume
+= dir
* 0.1;
1127 if(volume
< 0) volume
= 0;
1128 if(volume
> 1) volume
= 1;
1131 static void* blip_thread(void* arg
) {
1134 unsigned char buf
[100], silence
[1000];
1135 generate_blip(buf
, sizeof(buf
));
1136 memset(silence
, buf
[99], sizeof silence
);
1137 long long t
= getutime64();
1138 unsigned passed
= 0;
1139 float myvol
= volume
;
1141 if(myvol
!= volume
) {
1142 generate_blip(buf
, sizeof(buf
));
1145 if(bms
&& (getutime64() - t
)/1000 >= bms
) {
1146 audio_write(&ao
, buf
, sizeof buf
);
1149 audio_write(&ao
, silence
, sizeof silence
);
1156 static void* nop_thread(void *arg
) {
1160 static void* chanwalker_thread(void* arg
) {
1162 int channel
= 1, delay
= 800;
1164 if(filebased
) return 0;
1166 if((getutime64() - tm
)/1000 >= delay
) {
1167 int ret
= set_channel(itf
, channel
= next_chan(channel
));
1169 if(console_getbackendtype(t
) == cb_sdl
)
1170 dprintf(2, "oops couldnt switch to chan %d\n", channel
);
1178 #include <sys/ioctl.h>
1179 #include <sys/socket.h>
1180 /* set an interface up or down, depending on whether up is set.
1181 if checkonly is true, no change will be made and the result
1182 of the function can be interpreted as "isdownup".
1183 if the interface was already up/down, 2 is returned.
1184 if the interface was successfully upped/downed, 1 is returned.
1185 0 is only returned if checkonly is set and the interface was not
1186 in the queried state.
1187 -1 is returned on error. */
1188 static int ifdownup(const char *dev
, int up
, int checkonly
) {
1190 if((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) return -1;
1191 struct ifreq ifr
= {0};
1192 strcpy(ifr
.ifr_name
, dev
);
1193 if(ioctl(fd
, SIOCGIFFLAGS
, &ifr
) <0) goto done
;
1194 int isup
= ifr
.ifr_flags
& IFF_UP
;
1195 if((up
&& isup
) || (!up
&& !isup
)) ret
= 2;
1196 else if (checkonly
) ret
= 0;
1198 if(up
) ifr
.ifr_flags
|= IFF_UP
;
1199 else ifr
.ifr_flags
&= ~(IFF_UP
);
1200 ret
= (ioctl(fd
, SIOCSIFFLAGS
, &ifr
) >= 0);
1207 #include "wireless-lite.h"
1208 static int getiwmode(const char *dev
) {
1210 if((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) return -1;
1211 struct iwreq iwr
= {0};
1212 strcpy(iwr
.ifr_name
, dev
);
1213 if(ioctl(fd
, SIOCGIWMODE
, &iwr
) >=0) ret
= iwr
.u
.mode
;
1218 static int setiwmode(const char *dev
, int mode
) {
1220 if((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) return -1;
1221 struct iwreq iwr
= {.u
.mode
= mode
};
1222 strcpy(iwr
.ifr_name
, dev
);
1223 ret
= ioctl(fd
, SIOCSIWMODE
, &iwr
);
1228 static void* capture_thread(void*arg
) {
1231 int ret
= process_frame(foo
);
1232 long long tmp
= getutime64();
1235 wlans
[ret
].last_seen
= tmp
;
1242 static pthread_t bt
, wt
;
1243 static const char *itf
;
1245 static void set_selection(int on
) {
1248 pthread_join(wt
, 0);
1250 pthread_create(&bt
, 0, blip_thread
, 0);
1253 int ch
= wlans
[selection
].channel
;
1255 set_channel(itf
, ch
);
1258 pthread_create(&wt
, 0, chanwalker_thread
, (void*)itf
);
1259 pthread_join(bt
, 0);
1266 int main(int argc
,char**argv
) {
1267 wlans
= DYNA_NEW(struct wlaninfo
);
1270 while((c
= getopt(argc
, argv
, "c:")) != EOF
) switch(c
) {
1271 case 'c': fixed_chan
= atoi(optarg
); break;
1272 default: return usage(argv
[0]);
1274 if(!argv
[optind
]) return usage(argv
[0]);
1280 char errbuf
[PCAP_ERRBUF_SIZE
];
1282 if(strchr(itf
, '.') && access(itf
, R_OK
) == 0) {
1284 foo
= pcap_open_offline(itf
, errbuf
);
1286 foo
= pcap_create(itf
, errbuf
);
1288 snprintf(fnbuf
, sizeof fnbuf
, "tmp.%s.pcap", itf
);
1289 outfd
= open(fnbuf
, O_WRONLY
|O_CREAT
|O_TRUNC
,0660);
1291 pcapfile_write_header(outfd
);
1293 if(!foo
) { dprintf(2, "%s\n", errbuf
); return 1; }
1295 int ret
, wasdown
, orgmode
;
1297 if(filebased
) goto skip
;
1299 if((orgmode
= getiwmode(itf
)) != IW_MODE_MONITOR
) {
1300 if((ret
= ifdownup(itf
, 0, 0)) == -1) {
1302 perror("error setting up interface - maybe need to run as root.");
1304 wasdown
= (ret
== 2);
1305 if(setiwmode(itf
, IW_MODE_MONITOR
) == -1) goto iferr
;
1307 wasdown
= (ifdownup(itf
, 0, 1) == 2);
1309 if(ifdownup(itf
, 1, 0) == -1) goto iferr
;
1311 if(pcap_activate(foo
)) {
1312 dprintf(2, "pcap_activate failed: %s\n", pcap_geterr(foo
));
1320 signal(SIGINT
, sigh
);
1322 int channel
= fixed_chan
? fixed_chan
: 1;
1323 if(fixed_chan
) set_channel(itf
, fixed_chan
);
1326 void *(*cw_func
)(void*);
1327 if(filebased
|| fixed_chan
) cw_func
= nop_thread
;
1328 else cw_func
= chanwalker_thread
;
1329 pthread_create(&wt
, 0, cw_func
, (void*) itf
);
1330 pthread_create(&ct
, 0, capture_thread
, foo
);
1332 struct netgui_config netgui_cfg
;
1333 if(getenv("NETGUI")) {
1334 netgui_start(&netgui_cfg
, "0.0.0.0", 9876);
1338 long long tmp
= getutime64();
1339 if((tmp
-tm
) >= (1000000 / GUI_FPS
)) {
1344 if(selected
) calc_bms(selection
);
1345 int k
= console_getkey_nb(t
);
1348 case CK_CURSOR_RIGHT
: this_wlan_scale_mode
= !this_wlan_scale_mode
; break;
1349 case '+': case '0': volume_change(+1); break;
1350 case '-': case '9': volume_change(-1); break;
1351 case CK_CURSOR_DOWN
: selection_move(1);break;
1352 case CK_CURSOR_UP
: selection_move(-1);break;
1354 //selected = !selected;
1355 set_selection(!selected
);
1358 case CK_ESCAPE
: stop
= 1; break;
1363 pcap_breakloop(foo
); // this doesn't actually seem to work
1365 if(getenv("NETGUI")) {
1366 netgui_stop(&netgui_cfg
);
1371 pthread_join(bt
, 0);
1374 pthread_join(wt
, 0);
1377 // since our capture_thread uses blocking reads in order to keep CPU usage
1378 // minimal, we need to get the current read cancelled - and if no packets
1379 // arrive, this can take a *long* time. since pcap_breakloop() doesn't actually
1380 // seem to work, the only way i found to break out of the read is to actually
1381 // bring down the interface - so this must happen before we join the thread
1382 // and close the pcap handle.
1384 if(wasdown
|| orgmode
!= IW_MODE_MONITOR
) ifdownup(itf
, 0, 0);
1385 if(orgmode
!= IW_MODE_MONITOR
) setiwmode(itf
, orgmode
);
1386 if(!wasdown
&& orgmode
!= IW_MODE_MONITOR
) ifdownup(itf
, 1, 0);
1389 pthread_join(ct
, 0);
1393 if(outfd
!= -1) close(outfd
);