1160994Ssam/*-
2160994Ssam * Copyright (c) 2006, Andrea Bittau <a.bittau@cs.ucl.ac.uk>
3160994Ssam * All rights reserved.
4160994Ssam *
5160994Ssam * Redistribution and use in source and binary forms, with or without
6160994Ssam * modification, are permitted provided that the following conditions
7160994Ssam * are met:
8160994Ssam * 1. Redistributions of source code must retain the above copyright
9160994Ssam *    notice, this list of conditions and the following disclaimer.
10160994Ssam * 2. Redistributions in binary form must reproduce the above copyright
11160994Ssam *    notice, this list of conditions and the following disclaimer in the
12160994Ssam *    documentation and/or other materials provided with the distribution.
13160994Ssam *
14160994Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15160994Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16160994Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17160994Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18160994Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19160994Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20160994Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21160994Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22160994Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23160994Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24160994Ssam * SUCH DAMAGE.
25160994Ssam *
26160994Ssam * $FreeBSD$
27160994Ssam */
28160994Ssam#include <sys/types.h>
29160994Ssam#include <sys/socket.h>
30160994Ssam#include <sys/ioctl.h>
31160994Ssam#include <sys/select.h>
32160994Ssam#include <sys/time.h>
33160994Ssam#include <net/if.h>
34160994Ssam#include <net/if_media.h>
35160994Ssam#include <net/bpf.h>
36160994Ssam#include <net80211/ieee80211_ioctl.h>
37160994Ssam#include <net80211/ieee80211.h>
38160994Ssam#include <net/ethernet.h>
39160994Ssam#include <net80211/ieee80211_radiotap.h>
40160994Ssam#include <sys/endian.h>
41160994Ssam#include <fcntl.h>
42160994Ssam#include <errno.h>
43160994Ssam#include <string.h>
44160994Ssam#include <stdlib.h>
45160994Ssam#include <stdio.h>
46160994Ssam#include <curses.h>
47160994Ssam#include <signal.h>
48160994Ssam#include <unistd.h>
49160994Ssam#include <assert.h>
50160994Ssam
51160994Ssam//int hopfreq = 3*1000; // ms
52160994Ssamint hopfreq = 500; // ms
53160994Ssamint sig_reset = 1*1000; // ms
54160994Ssam
55160994Ssam
56160994Ssamint ioctl_s = -1;
57160994Ssamint bpf_s = -1;
58160994Ssam
59160994Ssamstruct chan_info {
60160994Ssam	int locked;
61160994Ssam	int chan;
62160994Ssam	struct ieee80211req ireq;
63160994Ssam	struct timeval last_hop;
64160994Ssam} chaninfo;
65160994Ssam
66160994Ssam
67160994Ssam#define CRYPT_NONE		0
68160994Ssam#define CRYPT_WEP		1
69160994Ssam#define CRYPT_WPA1		2
70160994Ssam#define CRYPT_WPA		3
71160994Ssam#define CRYPT_WPA1_TKIP		4
72160994Ssam#define CRYPT_WPA1_TKIP_PSK	5
73160994Ssam#define CRYPT_WPA1_CCMP		6
74160994Ssam#define CRYPT_WPA1_CCMP_PSK	7
75160994Ssam#define CRYPT_80211i		8
76160994Ssam#define CRYPT_80211i_TKIP	9
77160994Ssam#define CRYPT_80211i_TKIP_PSK	10
78160994Ssam
79160994Ssamstruct node_info {
80160994Ssam	unsigned char mac[6];
81160994Ssam	int signal;
82160994Ssam	int noise;
83160994Ssam	int max;
84160994Ssam	unsigned char ssid[256];
85160994Ssam	int chan;
86160994Ssam	int wep;
87160994Ssam	int pos;
88160994Ssam	int ap;
89160994Ssam
90160994Ssam	struct timeval seen;
91160994Ssam
92160994Ssam	struct node_info* prev;
93160994Ssam	struct node_info* next;
94160994Ssam} *nodes = 0;
95160994Ssam
96160994Ssamvoid clean_crap() {
97160994Ssam	struct node_info* next;
98160994Ssam
99160994Ssam	if (ioctl_s != -1)
100160994Ssam		close(ioctl_s);
101160994Ssam	if (bpf_s != -1)
102160994Ssam		close(bpf_s);
103160994Ssam
104160994Ssam	while (nodes) {
105160994Ssam		next = nodes->next;
106160994Ssam		free(nodes);
107160994Ssam		nodes = next;
108160994Ssam	}
109160994Ssam}
110160994Ssam
111160994Ssamchar* mac2str(unsigned char* mac) {
112160994Ssam        static char ret[6*3];
113160994Ssam
114160994Ssam        sprintf(ret, "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
115160994Ssam                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
116160994Ssam
117160994Ssam        return ret;
118160994Ssam}
119160994Ssam
120160994Ssamchar* wep2str(int w) {
121160994Ssam	char* wep = 0;
122160994Ssam	static char res[14];
123160994Ssam
124160994Ssam	switch (w) {
125160994Ssam	case CRYPT_NONE:
126160994Ssam		wep = "";
127160994Ssam		break;
128160994Ssam
129160994Ssam	case CRYPT_WEP:
130160994Ssam		wep = "WEP";
131160994Ssam		break;
132160994Ssam
133160994Ssam	case CRYPT_WPA1:
134160994Ssam		wep = "WPA1";
135160994Ssam		break;
136160994Ssam
137160994Ssam	case CRYPT_WPA:
138160994Ssam		wep = "WPA?";
139160994Ssam		break;
140160994Ssam
141160994Ssam	case CRYPT_WPA1_TKIP:
142160994Ssam		wep = "WPA1-TKIP";
143160994Ssam		break;
144160994Ssam
145160994Ssam	case CRYPT_WPA1_TKIP_PSK:
146160994Ssam		wep = "WPA1-TKIP-PSK";
147160994Ssam		break;
148160994Ssam
149160994Ssam	case CRYPT_WPA1_CCMP:
150160994Ssam		wep = "WPA1-CCMP";
151160994Ssam		break;
152160994Ssam
153160994Ssam	case CRYPT_WPA1_CCMP_PSK:
154160994Ssam		wep = "WPA1-CCMP-PSK";
155160994Ssam		break;
156160994Ssam
157160994Ssam	case CRYPT_80211i:
158160994Ssam		wep = "i";
159160994Ssam		break;
160160994Ssam
161160994Ssam	case CRYPT_80211i_TKIP:
162160994Ssam		wep = "11i-TKIP";
163160994Ssam		break;
164160994Ssam
165160994Ssam	case CRYPT_80211i_TKIP_PSK:
166160994Ssam		wep = "11i-TKIP-PSK";
167160994Ssam		break;
168160994Ssam
169160994Ssam	default:
170160994Ssam		wep = "FIXME!";
171160994Ssam		break;
172160994Ssam	}
173160994Ssam
174160994Ssam	memset(res, ' ', sizeof(res));
175160994Ssam	assert(strlen(wep) < sizeof(res));
176160994Ssam	memcpy(res, wep, strlen(wep));
177160994Ssam	res[sizeof(res)-1] = 0;
178160994Ssam	return res;
179160994Ssam}
180160994Ssam
181160994Ssamchar* ssid2str(struct node_info* node) {
182160994Ssam	static char res[24];
183160994Ssam
184160994Ssam	memset(res, ' ', sizeof(res));
185160994Ssam	res[0] = '[';
186160994Ssam	strcpy(&res[sizeof(res)-2], "]");
187160994Ssam
188160994Ssam	if (node->ap) {
189160994Ssam		int left = sizeof(res) - 3;
190160994Ssam
191160994Ssam		if (strlen(node->ssid) < left)
192160994Ssam			left = strlen(node->ssid);
193160994Ssam		memcpy(&res[1], node->ssid, left);
194160994Ssam	}
195160994Ssam	else {
196160994Ssam		memcpy(&res[1], "<client>", 8);
197160994Ssam	}
198160994Ssam	return res;
199160994Ssam}
200160994Ssam
201160994Ssamvoid save_state() {
202160994Ssam	FILE* f;
203160994Ssam	struct node_info* node = nodes;
204160994Ssam
205160994Ssam	f = fopen("stumbler.log", "w");
206160994Ssam	if (!f) {
207160994Ssam		perror("fopen()");
208160994Ssam		exit(1);
209160994Ssam	}
210160994Ssam
211160994Ssam	while (node) {
212160994Ssam		struct tm* t;
213160994Ssam		char tim[16];
214160994Ssam
215160994Ssam		t = localtime( (time_t*) &node->seen.tv_sec);
216160994Ssam		if (!t) {
217160994Ssam			perror("localtime()");
218160994Ssam			exit(1);
219160994Ssam		}
220160994Ssam		tim[0] = 0;
221160994Ssam		strftime(tim, sizeof(tim), "%H:%M:%S", t);
222160994Ssam
223160994Ssam		fprintf(f, "%s %s %s %2d %s 0x%.2x\n", tim,
224160994Ssam			mac2str(node->mac), wep2str(node->wep),
225160994Ssam			node->chan, ssid2str(node), node->max);
226160994Ssam
227160994Ssam		node = node->next;
228160994Ssam	}
229160994Ssam
230160994Ssam	fclose(f);
231160994Ssam}
232160994Ssam
233160994Ssamvoid cleanup(int x) {
234160994Ssam	endwin();
235160994Ssam	clean_crap();
236160994Ssam	exit(0);
237160994Ssam}
238160994Ssam
239160994Ssamvoid die(int p, char* msg) {
240160994Ssam	endwin();
241160994Ssam	if (p)
242160994Ssam		perror(msg);
243160994Ssam	else
244160994Ssam		printf("%s\n", msg);
245160994Ssam	clean_crap();
246160994Ssam	exit(1);
247160994Ssam}
248160994Ssam
249160994Ssamvoid display_chan() {
250160994Ssam	int x, y;
251160994Ssam	char tmp[3];
252160994Ssam
253160994Ssam	x = COLS - 2;
254160994Ssam	y = LINES - 1;
255160994Ssam
256160994Ssam	snprintf(tmp, sizeof(tmp), "%.2d", chaninfo.chan);
257160994Ssam	mvaddstr(y, x, tmp);
258160994Ssam	refresh();
259160994Ssam}
260160994Ssam
261160994Ssamvoid set_chan(int c) {
262160994Ssam        chaninfo.ireq.i_val = c;
263160994Ssam
264160994Ssam        if (ioctl(ioctl_s, SIOCS80211, &chaninfo.ireq) == -1)
265160994Ssam                die(1, "ioctl(SIOCS80211) [chan]");
266160994Ssam
267160994Ssam	chaninfo.chan = c;
268160994Ssam
269160994Ssam	if (gettimeofday(&chaninfo.last_hop, NULL) == -1)
270160994Ssam		die(1, "gettimeofday()");
271160994Ssam
272160994Ssam	display_chan();
273160994Ssam}
274160994Ssam
275160994Ssamvoid setup_if(char *dev) {
276160994Ssam        struct ifreq ifr;
277160994Ssam        unsigned int flags;
278195848Ssam
279195848Ssam        // set chan
280195848Ssam        memset(&chaninfo.ireq, 0, sizeof(chaninfo.ireq));
281195848Ssam        strcpy(chaninfo.ireq.i_name, dev);
282195848Ssam        chaninfo.ireq.i_type = IEEE80211_IOC_CHANNEL;
283195848Ssam
284195848Ssam        set_chan(1);
285195848Ssam
286160994Ssam        // set iface up and promisc
287160994Ssam        memset(&ifr, 0, sizeof(ifr));
288160994Ssam        strcpy(ifr.ifr_name, dev);
289160994Ssam        if (ioctl(ioctl_s, SIOCGIFFLAGS, &ifr) == -1)
290160994Ssam                die(1, "ioctl(SIOCGIFFLAGS)");
291160994Ssam
292160994Ssam        flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
293160994Ssam        flags |= IFF_UP | IFF_PPROMISC;
294160994Ssam
295160994Ssam        memset(&ifr, 0, sizeof(ifr));
296160994Ssam        strcpy(ifr.ifr_name, dev);
297160994Ssam        ifr.ifr_flags = flags & 0xffff;
298160994Ssam        ifr.ifr_flagshigh = flags >> 16;
299160994Ssam        if (ioctl(ioctl_s, SIOCSIFFLAGS, &ifr) == -1)
300160994Ssam                die(1, "ioctl(SIOCSIFFLAGS)");
301160994Ssam}
302160994Ssam
303160994Ssamvoid open_bpf(char *dev, int dlt) {
304160994Ssam        int i;
305160994Ssam        char buf[64];
306160994Ssam        int fd = -1;
307160994Ssam        struct ifreq ifr;
308160994Ssam
309160994Ssam        for(i = 0;i < 16; i++) {
310160994Ssam                sprintf(buf, "/dev/bpf%d", i);
311160994Ssam
312160994Ssam                fd = open(buf, O_RDWR);
313160994Ssam                if(fd < 0) {
314160994Ssam                        if(errno != EBUSY)
315160994Ssam				die(1,"can't open /dev/bpf");
316160994Ssam                        continue;
317160994Ssam                }
318160994Ssam                else
319160994Ssam                        break;
320160994Ssam        }
321160994Ssam
322160994Ssam        if(fd < 0)
323160994Ssam                die(1, "can't open /dev/bpf");
324160994Ssam
325160994Ssam        strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)-1);
326160994Ssam        ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0;
327160994Ssam
328160994Ssam        if(ioctl(fd, BIOCSETIF, &ifr) < 0)
329160994Ssam                die(1, "ioctl(BIOCSETIF)");
330160994Ssam
331160994Ssam        if (ioctl(fd, BIOCSDLT, &dlt) < 0)
332160994Ssam                die(1, "ioctl(BIOCSDLT)");
333160994Ssam
334160994Ssam        i = 1;
335160994Ssam        if(ioctl(fd, BIOCIMMEDIATE, &i) < 0)
336160994Ssam                die(1, "ioctl(BIOCIMMEDIATE)");
337160994Ssam
338160994Ssam	bpf_s = fd;
339160994Ssam}
340160994Ssam
341160994Ssamvoid user_input() {
342160994Ssam	static char chan[3];
343160994Ssam	static int pos = 0;
344160994Ssam	int c;
345160994Ssam
346160994Ssam	c = getch();
347160994Ssam
348160994Ssam	switch (c) {
349160994Ssam		case 'w':
350160994Ssam			save_state();
351160994Ssam			break;
352160994Ssam
353160994Ssam		case 'q':
354160994Ssam			cleanup(0);
355160994Ssam			break;
356160994Ssam
357160994Ssam		case 'c':
358160994Ssam			chaninfo.locked = !chaninfo.locked;
359160994Ssam			break;
360160994Ssam
361160994Ssam		case ERR:
362160994Ssam			die(0, "getch()");
363160994Ssam			break;
364160994Ssam
365160994Ssam		case '0':
366160994Ssam		case '1':
367160994Ssam		case '2':
368160994Ssam		case '3':
369160994Ssam		case '4':
370160994Ssam		case '5':
371160994Ssam		case '6':
372160994Ssam		case '7':
373160994Ssam		case '8':
374160994Ssam		case '9':
375160994Ssam			chan[pos++] = c;
376160994Ssam			if (pos == 2) {
377160994Ssam				int ch = atoi(chan);
378160994Ssam				if (ch <= 11 && ch >= 1) {
379160994Ssam					set_chan(atoi(chan));
380160994Ssam					chaninfo.locked = 1;
381160994Ssam				}
382160994Ssam				pos = 0;
383160994Ssam			}
384160994Ssam			break;
385160994Ssam
386160994Ssam		default:
387160994Ssam			pos = 0;
388160994Ssam			break;
389160994Ssam	}
390160994Ssam}
391160994Ssam
392160994Ssamvoid display_node(struct node_info* node) {
393160994Ssam	int x = 0;
394160994Ssam	int y = 0;
395160994Ssam	int i;
396160994Ssam	char chan[3];
397160994Ssam	char* ssid = 0;
398160994Ssam	int sig, max, left, noise;
399160994Ssam	char* wep = 0;
400160994Ssam
401160994Ssam	y = node->pos;
402160994Ssam	if (y == -1) // offscreen
403160994Ssam		return;
404160994Ssam
405160994Ssam	assert(y < LINES);
406160994Ssam
407160994Ssam	// MAC
408160994Ssam	mvaddstr(y, x, mac2str(node->mac));
409160994Ssam	x += 6*3;
410160994Ssam
411160994Ssam	// WEP
412160994Ssam	wep = wep2str(node->wep);
413160994Ssam	assert(wep);
414160994Ssam	mvaddstr(y, x, wep);
415160994Ssam	x += strlen(wep);
416160994Ssam	x++;
417160994Ssam
418160994Ssam	// CHAN
419160994Ssam	sprintf(chan, "%.2d", node->chan);
420160994Ssam	mvaddstr(y, x, chan);
421160994Ssam	x += 3;
422160994Ssam
423160994Ssam	// ssid
424160994Ssam	ssid = ssid2str(node);
425160994Ssam	assert(ssid);
426160994Ssam	mvaddstr(y, x, ssid);
427160994Ssam	x += strlen(ssid);
428160994Ssam	x++;
429160994Ssam
430160994Ssam	left = COLS - x - 1;
431160994Ssam
432160994Ssam	sig = (int)  ( ((double)node->signal)*left/100.0 );
433160994Ssam	noise=(int)  ( ((double)node->noise)*left/100.0 );
434160994Ssam	max = (int)  ( ((double)node->max)*left/100.0 );
435160994Ssam
436160994Ssam	// SIGNAL BAR
437160994Ssam	for (i = 0; i < noise; i++)
438160994Ssam		mvaddch(y, x++, 'N');
439160994Ssam
440160994Ssam	for (; i < sig; i++)
441160994Ssam		mvaddch(y,x++, 'X');
442160994Ssam
443160994Ssam	for (; i < max; i++)
444160994Ssam		mvaddch(y,x++, ' ');
445160994Ssam	mvaddch(y,x++, '|');
446160994Ssam
447160994Ssam	for (; x < COLS-1; x++)
448160994Ssam		mvaddch(y, x, ' ');
449160994Ssam
450160994Ssam	assert (x <= COLS);
451160994Ssam}
452160994Ssam
453160994Ssamvoid update_node(struct node_info* data) {
454160994Ssam	struct node_info* node;
455160994Ssam	int sort = 0;
456160994Ssam
457160994Ssam	assert(data->signal <= 100);
458160994Ssam
459160994Ssam	node = nodes;
460160994Ssam
461160994Ssam	// first time [virgin]
462160994Ssam	if (!node) {
463160994Ssam		node = (struct node_info*) malloc(sizeof(struct node_info));
464160994Ssam		if (!node)
465160994Ssam			die(1, "malloc()");
466160994Ssam
467160994Ssam		memset(node, 0, sizeof(*node));
468160994Ssam		memcpy(node->mac, data->mac, 6);
469160994Ssam		nodes = node;
470160994Ssam	}
471160994Ssam
472160994Ssam	while (node) {
473160994Ssam		// found it
474160994Ssam		if (memcmp(node->mac, data->mac, 6) == 0)
475160994Ssam			break;
476160994Ssam
477160994Ssam		// end of chain
478160994Ssam		if (!node->next) {
479160994Ssam			node->next = (struct node_info*)
480160994Ssam				      malloc(sizeof(struct node_info));
481160994Ssam			if (!node->next)
482160994Ssam				die(1, "malloc()");
483160994Ssam
484160994Ssam			memset(node->next, 0, sizeof(*node->next));
485160994Ssam			memcpy(node->next->mac, data->mac, 6);
486160994Ssam			node->next->prev = node;
487160994Ssam			node->next->pos = node->pos+1;
488160994Ssam
489160994Ssam			node = node->next;
490160994Ssam			if (node->pos == LINES)
491160994Ssam				sort = 1;
492160994Ssam			break;
493160994Ssam		}
494160994Ssam
495160994Ssam		node = node->next;
496160994Ssam	}
497160994Ssam	assert(node);
498160994Ssam
499160994Ssam	// too many nodes for screen
500160994Ssam	if (sort) {
501160994Ssam		struct node_info* ni = nodes;
502160994Ssam
503160994Ssam		while (ni) {
504160994Ssam			if (ni->pos != -1)
505160994Ssam				ni->pos--;
506160994Ssam
507160994Ssam			display_node(ni);
508160994Ssam			ni = ni->next;
509160994Ssam		}
510160994Ssam	}
511160994Ssam
512160994Ssam	node->signal = data->signal;
513160994Ssam	if (data->signal > node->max)
514160994Ssam		node->max = data->signal;
515160994Ssam
516160994Ssam	if (gettimeofday(&node->seen, NULL) == -1)
517160994Ssam		die(1, "gettimeofday()");
518160994Ssam
519160994Ssam	if (data->ssid[0] != 0)
520160994Ssam		strcpy(node->ssid, data->ssid);
521160994Ssam	if (data->chan != -1)
522160994Ssam		node->chan = data->chan;
523160994Ssam	if (data->wep != -1) {
524160994Ssam		// XXX LAME --- won't detect if AP changes WEP mode in
525160994Ssam		// beacons...
526160994Ssam		if (node->wep != CRYPT_WEP &&
527160994Ssam		    node->wep != CRYPT_NONE &&
528160994Ssam		    data->wep == CRYPT_WEP) {
529160994Ssam		}
530160994Ssam		else
531160994Ssam			node->wep = data->wep;
532160994Ssam	}
533160994Ssam	if (data->ap != -1)
534160994Ssam		node->ap = data->ap;
535160994Ssam
536160994Ssam	display_node(node);
537160994Ssam	refresh();
538160994Ssam}
539160994Ssam
540160994Ssamvoid get_beacon_info(unsigned char* data, int rd,
541160994Ssam		     struct node_info* node) {
542160994Ssam
543160994Ssam	int blen = 8 + 2 + 2;
544160994Ssam
545160994Ssam	strcpy(node->ssid, "<hidden>");
546160994Ssam	node->chan = 0;
547160994Ssam	node->wep = CRYPT_NONE;
548160994Ssam
549160994Ssam	assert(rd >= blen);
550160994Ssam
551160994Ssam	if (IEEE80211_BEACON_CAPABILITY(data) & IEEE80211_CAPINFO_PRIVACY)
552160994Ssam		node->wep = CRYPT_WEP;
553160994Ssam
554160994Ssam	data += blen;
555160994Ssam	rd -= blen;
556160994Ssam
557160994Ssam	while (rd > 2) {
558160994Ssam                int eid, elen;
559160994Ssam
560160994Ssam                eid = *data;
561160994Ssam                data++;
562160994Ssam                elen = *data;
563160994Ssam                data++;
564160994Ssam                rd -= 2;
565160994Ssam
566160994Ssam		// short!
567160994Ssam                if (rd < elen) {
568160994Ssam                        return;
569160994Ssam                }
570160994Ssam
571160994Ssam                // ssid
572160994Ssam                if (eid == 0) {
573160994Ssam			if (elen == 1 && data[0] == 0) {
574160994Ssam			// hidden
575160994Ssam			}
576160994Ssam			else {
577160994Ssam                        	memcpy(node->ssid, data, elen);
578160994Ssam                        	node->ssid[elen] = 0;
579160994Ssam			}
580160994Ssam                }
581160994Ssam                // chan
582160994Ssam                else if(eid == 3) {
583160994Ssam			// weird chan!
584160994Ssam                        if( elen != 1)
585160994Ssam				goto next;
586160994Ssam
587160994Ssam                        node->chan = *data;
588160994Ssam                }
589160994Ssam		// WPA
590160994Ssam		else if (eid == 221 && node->wep == CRYPT_WEP) {
591160994Ssam			struct ieee80211_ie_wpa* wpa;
592160994Ssam
593160994Ssam			wpa = (struct ieee80211_ie_wpa*) data;
594160994Ssam			if (elen < 6)
595160994Ssam				goto next;
596160994Ssam
597160994Ssam			if (!memcmp(wpa->wpa_oui, "\x00\x50\xf2", 3)) {
598160994Ssam			//	node->wep = CRYPT_WPA;
599160994Ssam			}
600160994Ssam			else
601160994Ssam				goto next;
602160994Ssam
603160994Ssam			if (wpa->wpa_type == WPA_OUI_TYPE &&
604160994Ssam			    le16toh(wpa->wpa_version) == WPA_VERSION) {
605160994Ssam			    	int cipher, auth;
606160994Ssam				unsigned char* ptr;
607160994Ssam
608160994Ssam				node->wep = CRYPT_WPA1;
609160994Ssam
610160994Ssam				if (elen < 12)
611160994Ssam					goto next;
612160994Ssam
613160994Ssam				cipher = ((unsigned char*) wpa->wpa_mcipher)[3];
614160994Ssam
615160994Ssam				ptr = (unsigned char*)wpa + 12 +
616160994Ssam				      4 * le16toh(wpa->wpa_uciphercnt);
617160994Ssam
618160994Ssam				if (elen < (ptr - data + 6))
619160994Ssam					goto next;
620160994Ssam
621160994Ssam				if ( *((unsigned short*) ptr) == 0)
622160994Ssam					goto next;
623160994Ssam
624160994Ssam				ptr += 2 + 3;
625160994Ssam				auth = *ptr;
626160994Ssam
627160994Ssam				if (cipher == WPA_CSE_TKIP) {
628160994Ssam					node->wep = CRYPT_WPA1_TKIP;
629160994Ssam
630160994Ssam					if (auth == WPA_ASE_8021X_PSK)
631160994Ssam						node->wep = CRYPT_WPA1_TKIP_PSK;
632160994Ssam				}
633160994Ssam
634160994Ssam				if (cipher == WPA_CSE_CCMP) {
635160994Ssam					node->wep = CRYPT_WPA1_CCMP;
636160994Ssam
637160994Ssam					if (auth == WPA_ASE_8021X_PSK)
638160994Ssam						node->wep = CRYPT_WPA1_CCMP_PSK;
639160994Ssam				}
640160994Ssam			}
641160994Ssam		}
642160994Ssam		else if (eid == 48 && node->wep == CRYPT_WEP) {
643160994Ssam			unsigned char* ptr;
644160994Ssam
645160994Ssam			// XXX no bounds checking
646160994Ssam			ptr = data;
647160994Ssam
648160994Ssam			if (ptr[0] == 1 && ptr[1] == 0) {
649160994Ssam				unsigned short* count;
650160994Ssam				int cipher = 0;
651160994Ssam
652160994Ssam				ptr += 2;
653160994Ssam				node->wep = CRYPT_80211i;
654160994Ssam
655160994Ssam				if (!memcmp(ptr, "\x00\x0f\xac\x02", 4)) {
656160994Ssam					node->wep = CRYPT_80211i_TKIP;
657160994Ssam					cipher = 1;
658160994Ssam				}
659160994Ssam
660160994Ssam				ptr += 4;
661160994Ssam				count = (unsigned short*) ptr;
662160994Ssam				ptr +=2 + *count*4;
663160994Ssam
664160994Ssam				count = (unsigned short*) ptr;
665160994Ssam				if (*count) {
666160994Ssam					ptr += 2;
667160994Ssam
668160994Ssam					if (!memcmp(ptr,"\x00\x0f\xac\x02", 4)) {
669160994Ssam						if (cipher)
670160994Ssam							node->wep = CRYPT_80211i_TKIP_PSK;
671160994Ssam					}
672160994Ssam				}
673160994Ssam			}
674160994Ssam		}
675160994Ssam
676160994Ssamnext:
677160994Ssam                data += elen;
678160994Ssam                rd -= elen;
679160994Ssam	}
680160994Ssam}
681160994Ssam
682160994Ssamint get_packet_info(struct ieee80211_frame* wh,
683160994Ssam		     unsigned char* body, int bodylen,
684160994Ssam		     struct node_info* node) {
685160994Ssam
686160994Ssam	int type, stype;
687160994Ssam
688160994Ssam	node->chan = chaninfo.chan;
689160994Ssam	node->wep = -1;
690160994Ssam	node->ssid[0] = 0;
691160994Ssam	node->ap = -1;
692160994Ssam
693160994Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
694160994Ssam
695160994Ssam	if (type == IEEE80211_FC0_TYPE_CTL)
696160994Ssam		return 0;
697160994Ssam#if 0
698160994Ssam	if (wh->i_addr2[0] != 0) {
699160994Ssam		mvprintw(30,30,"%s %x",mac2str(wh->i_addr2), wh->i_fc[0]);
700160994Ssam	}
701160994Ssam#endif
702160994Ssam
703160994Ssam	stype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
704160994Ssam
705160994Ssam	if (type == IEEE80211_FC0_TYPE_MGT &&
706160994Ssam	    stype == IEEE80211_FC0_SUBTYPE_BEACON) {
707160994Ssam		get_beacon_info(body, bodylen, node);
708160994Ssam		node->ap = 1;
709160994Ssam	}
710160994Ssam
711160994Ssam	else if (type == IEEE80211_FC0_TYPE_DATA &&
712160994Ssam	    stype == IEEE80211_FC0_SUBTYPE_DATA) {
713160994Ssam
714262007Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
715160994Ssam			unsigned char* iv;
716160994Ssam
717160994Ssam			node->wep = CRYPT_WEP;
718160994Ssam
719160994Ssam			iv = body;
720160994Ssam			iv += 3;
721160994Ssam
722160994Ssam			// extended IV?
723160994Ssam			if (*iv & (1 << 1)) {
724160994Ssam#if 0
725160994Ssam				node->wep = CRYPT_WPA;
726160994Ssam				mvprintw(20,20, "shei");
727160994Ssam				exit(1);
728160994Ssam#endif
729160994Ssam			}
730160994Ssam		}
731160994Ssam
732160994Ssam		if (wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS)
733160994Ssam			node->ap = 1;
734160994Ssam		else
735160994Ssam			node->ap = 0;
736160994Ssam	}
737160994Ssam
738160994Ssam	memcpy(node->mac, wh->i_addr2, 6);
739160994Ssam	return 1;
740160994Ssam}
741160994Ssam
742160994Ssamvoid radiotap(unsigned char* data, int rd) {
743160994Ssam	struct ieee80211_radiotap_header* rth;
744160994Ssam	struct ieee80211_frame* wh;
745160994Ssam	char* body;
746160994Ssam	struct node_info node;
747160994Ssam	int8_t signal_dbm, noise_dbm;
748160994Ssam	uint8_t signal_db, noise_db;
749160994Ssam	int dbm = 0;
750160994Ssam	int signal = 0;
751160994Ssam	int i;
752160994Ssam
753160994Ssam	rd -= 4; // 802.11 CRC
754160994Ssam
755160994Ssam	// radiotap
756160994Ssam	rth = (struct ieee80211_radiotap_header*) data;
757160994Ssam
758160994Ssam	// 802.11
759160994Ssam	wh = (struct ieee80211_frame*)
760160994Ssam	     ((char*)rth + rth->it_len);
761160994Ssam        rd -= rth->it_len;
762160994Ssam
763160994Ssam	assert (rd >= 0);
764160994Ssam
765160994Ssam	// body
766160994Ssam	body = (char*) wh + sizeof(*wh);
767160994Ssam	rd -= sizeof(*wh);
768160994Ssam
769160994Ssam	if (!get_packet_info(wh, body, rd, &node))
770160994Ssam		return;
771160994Ssam
772160994Ssam	// signal and noise
773160994Ssam	body = (char*) rth + sizeof(*rth);
774160994Ssam	signal_dbm = noise_dbm = signal_db = noise_db = 0;
775160994Ssam
776160994Ssam	for (i = IEEE80211_RADIOTAP_TSFT; i <= IEEE80211_RADIOTAP_EXT; i++) {
777160994Ssam		if (!(rth->it_present & (1 << i)))
778160994Ssam			continue;
779160994Ssam
780160994Ssam		switch (i) {
781160994Ssam		case IEEE80211_RADIOTAP_TSFT:
782160994Ssam			body += sizeof(uint64_t);
783160994Ssam			break;
784160994Ssam
785160994Ssam		case IEEE80211_RADIOTAP_FLAGS:
786160994Ssam		case IEEE80211_RADIOTAP_RATE:
787160994Ssam			body += sizeof(uint8_t);
788160994Ssam			break;
789160994Ssam
790160994Ssam		case IEEE80211_RADIOTAP_CHANNEL:
791160994Ssam			body += sizeof(uint16_t)*2;
792160994Ssam			break;
793160994Ssam
794160994Ssam		case IEEE80211_RADIOTAP_FHSS:
795160994Ssam			body += sizeof(uint16_t);
796160994Ssam			break;
797160994Ssam
798160994Ssam		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
799160994Ssam			signal_dbm = *body;
800160994Ssam			body++;
801160994Ssam			dbm = 1;
802160994Ssam			break;
803160994Ssam
804160994Ssam		case IEEE80211_RADIOTAP_DBM_ANTNOISE:
805160994Ssam			noise_dbm = *body;
806160994Ssam			body++;
807160994Ssam			break;
808160994Ssam
809160994Ssam		case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
810160994Ssam			signal_db = *((unsigned char*)body);
811160994Ssam			body++;
812160994Ssam			break;
813160994Ssam
814160994Ssam		case IEEE80211_RADIOTAP_DB_ANTNOISE:
815160994Ssam			noise_db = *((unsigned char*)body);
816160994Ssam			body++;
817160994Ssam			break;
818160994Ssam
819160994Ssam		case IEEE80211_RADIOTAP_EXT:
820160994Ssam			abort();
821160994Ssam			break;
822160994Ssam		}
823160994Ssam	}
824160994Ssam	if (dbm) {
825160994Ssam		signal = signal_dbm - noise_dbm;
826160994Ssam	}
827160994Ssam	else {
828160994Ssam		signal = signal_db - noise_db;
829160994Ssam	}
830160994Ssam	if (signal < 0)
831160994Ssam		signal = 0;
832160994Ssam
833160994Ssam	node.signal = signal;
834160994Ssam#if 0
835160994Ssam	if (node.signal > 100 || node.signal < 0) {
836160994Ssam		mvprintw(25,25, "sig=%d", node.signal);
837160994Ssam	}
838160994Ssam#else
839160994Ssam	assert (node.signal <= 100 && node.signal >= 0);
840160994Ssam#endif
841160994Ssam
842160994Ssam	update_node(&node);
843160994Ssam}
844160994Ssam
845160994Ssamvoid bpf_input() {
846160994Ssam	static unsigned char buf[4096];
847160994Ssam	int rd;
848160994Ssam	struct bpf_hdr* bpfh;
849160994Ssam	unsigned char* data;
850160994Ssam
851160994Ssam	rd = read(bpf_s, buf, sizeof(buf));
852160994Ssam	if (rd == -1)
853160994Ssam		die(1,"read()");
854160994Ssam
855160994Ssam	bpfh = (struct bpf_hdr*) buf;
856160994Ssam	rd -= bpfh->bh_hdrlen;
857160994Ssam
858160994Ssam	if (rd != bpfh->bh_caplen) {
859160994Ssam		assert( rd > bpfh->bh_caplen);
860160994Ssam		rd = bpfh->bh_caplen;
861160994Ssam	}
862160994Ssam
863160994Ssam	data = (unsigned char*) bpfh + bpfh->bh_hdrlen;
864160994Ssam	radiotap(data, rd);
865160994Ssam}
866160994Ssam
867160994Ssamunsigned long elapsed_ms(struct timeval* now, struct timeval* prev) {
868160994Ssam	unsigned long elapsed = 0;
869160994Ssam
870160994Ssam	if (now->tv_sec > prev->tv_sec)
871160994Ssam		elapsed = 1000*1000 - prev->tv_usec +
872160994Ssam			  now->tv_usec;
873160994Ssam	else {
874160994Ssam		assert(now->tv_sec == prev->tv_sec);
875160994Ssam		elapsed = now->tv_usec - prev->tv_usec;
876160994Ssam	}
877160994Ssam	elapsed /= 1000; //ms
878160994Ssam
879160994Ssam	elapsed += (now->tv_sec - prev->tv_sec)*1000;
880160994Ssam	return elapsed;
881160994Ssam}
882160994Ssam
883160994Ssamvoid chanhop(struct timeval* tv) {
884160994Ssam	unsigned long elapsed = 0;
885160994Ssam
886160994Ssam	if (gettimeofday(tv, NULL) == -1)
887160994Ssam		die(1, "gettimeofday()");
888160994Ssam
889160994Ssam
890160994Ssam	elapsed = elapsed_ms(tv, &chaninfo.last_hop);
891160994Ssam
892160994Ssam	// need to chan hop
893160994Ssam	if (elapsed >= hopfreq) {
894160994Ssam		int c;
895160994Ssam
896160994Ssam		c = chaninfo.chan + 1;
897160994Ssam
898160994Ssam		if (c > 11)
899160994Ssam			c = 1;
900160994Ssam
901160994Ssam		set_chan(c);
902160994Ssam
903160994Ssam		elapsed = hopfreq;
904160994Ssam	}
905160994Ssam	// how much can we sleep?
906160994Ssam	else {
907160994Ssam		elapsed = hopfreq - elapsed;
908160994Ssam	}
909160994Ssam
910160994Ssam	// ok calculate sleeping time...
911160994Ssam	tv->tv_sec = elapsed/1000;
912160994Ssam	tv->tv_usec = (elapsed - tv->tv_sec*1000)*1000;
913160994Ssam}
914160994Ssam
915160994Ssamvoid check_seen(struct timeval* tv) {
916160994Ssam	unsigned long elapsed  = 0;
917160994Ssam	struct timeval now;
918160994Ssam	int need_refresh = 0;
919160994Ssam	unsigned long min_wait = 0;
920160994Ssam	unsigned long will_wait;
921160994Ssam
922160994Ssam	will_wait = tv->tv_sec*1000+tv->tv_usec/1000;
923160994Ssam	min_wait = will_wait;
924160994Ssam
925160994Ssam	struct node_info* node = nodes;
926160994Ssam
927160994Ssam	if (gettimeofday(&now, NULL) == -1)
928160994Ssam		die(1, "gettimeofday()");
929160994Ssam
930160994Ssam	while(node) {
931160994Ssam		if (node->signal) {
932160994Ssam			elapsed = elapsed_ms(&now, &node->seen);
933160994Ssam
934160994Ssam			// node is dead...
935160994Ssam			if (elapsed >= sig_reset) {
936160994Ssam				node->signal = 0;
937160994Ssam				display_node(node);
938160994Ssam				need_refresh = 1;
939160994Ssam			}
940160994Ssam
941160994Ssam			// need to check soon possibly...
942160994Ssam			else {
943160994Ssam				unsigned long left;
944160994Ssam
945160994Ssam				left = sig_reset - elapsed;
946160994Ssam				if (left < min_wait)
947160994Ssam					left = min_wait;
948160994Ssam			}
949160994Ssam		}
950160994Ssam		node = node->next;
951160994Ssam	}
952160994Ssam
953160994Ssam	if (need_refresh)
954160994Ssam		refresh();
955160994Ssam
956160994Ssam	// need to sleep for less...
957160994Ssam	if (min_wait < will_wait) {
958160994Ssam		tv->tv_sec = min_wait/1000;
959160994Ssam		tv->tv_usec = (min_wait - tv->tv_sec*1000)*1000;
960160994Ssam	}
961160994Ssam}
962160994Ssam
963160994Ssamvoid own(char* ifname) {
964160994Ssam	int rd;
965160994Ssam	fd_set fds;
966160994Ssam	struct timeval tv;
967160994Ssam	int dlt = DLT_IEEE802_11_RADIO;
968160994Ssam
969160994Ssam	hopfreq = 1000;
970160994Ssam
971160994Ssam	setup_if(ifname);
972160994Ssam	open_bpf(ifname, dlt);
973160994Ssam
974160994Ssam	while(1) {
975160994Ssam		// XXX innefficient all of this...
976160994Ssam		if (!chaninfo.locked)
977160994Ssam			chanhop(&tv);
978160994Ssam		else {
979160994Ssam			tv.tv_sec = 1;
980160994Ssam			tv.tv_usec = 0;
981160994Ssam		}
982160994Ssam
983160994Ssam		// especially this...
984160994Ssam		check_seen(&tv);
985160994Ssam
986160994Ssam		FD_ZERO(&fds);
987160994Ssam		FD_SET(0, &fds);
988160994Ssam		FD_SET(bpf_s, &fds);
989160994Ssam
990160994Ssam		rd = select(bpf_s+1, &fds,NULL , NULL, &tv);
991160994Ssam		if (rd == -1)
992160994Ssam			die(1, "select()");
993160994Ssam		if (FD_ISSET(0, &fds))
994160994Ssam			user_input();
995160994Ssam		if (FD_ISSET(bpf_s, &fds))
996160994Ssam			bpf_input();
997160994Ssam	}
998160994Ssam}
999160994Ssam
1000160994Ssamvoid init_globals() {
1001160994Ssam	ioctl_s = socket(PF_INET, SOCK_DGRAM, 0);
1002160994Ssam	if (ioctl_s == -1) {
1003160994Ssam		perror("socket()");
1004160994Ssam		exit(1);
1005160994Ssam	}
1006160994Ssam
1007160994Ssam	chaninfo.locked = 0;
1008160994Ssam	chaninfo.chan = 0;
1009160994Ssam}
1010160994Ssam
1011160994Ssamint main(int argc, char *argv[]) {
1012160994Ssam
1013160994Ssam
1014160994Ssam	if (argc < 2) {
1015160994Ssam		printf("Usage: %s <iface>\n", argv[0]);
1016160994Ssam		exit(1);
1017160994Ssam	}
1018160994Ssam
1019160994Ssam	init_globals();
1020160994Ssam
1021160994Ssam	initscr(); cbreak(); noecho();
1022160994Ssam
1023160994Ssam	nonl();
1024160994Ssam	intrflush(stdscr, FALSE);
1025160994Ssam	keypad(stdscr, TRUE);
1026160994Ssam
1027160994Ssam	curs_set(0);
1028160994Ssam
1029160994Ssam	clear();
1030160994Ssam	refresh();
1031160994Ssam
1032160994Ssam	signal(SIGINT, cleanup);
1033160994Ssam	signal(SIGTERM, cleanup);
1034160994Ssam
1035160994Ssam	own(argv[1]);
1036160994Ssam
1037160994Ssam	cleanup(0);
1038160994Ssam	exit(0);
1039160994Ssam}
1040