NEWS ==== -------------------- 0.7.3 (2011-12-22) -------------------- * Scan window: - items are now sorted first by channel (ascending) then by signal strength (descending), as before - using one line per entry doubled the capacity of the scan window - refresh interval is made configurable (via 'Statistics updates') * Configuration screen: - memorizes the last-changed item to ease longer configuration tasks * Fixes: - better (cleaner) termination on error - fixed clear-line refresh in info screen - fixed bug in generator for random link quality (causing negative values) - reverted using separate process group (problems when running under sudo) - miscellaneous code reordering to decouple functional units -------------------- 0.7.2 (2011-02-25) -------------------- * General: - support SIGWINCH for resizing (thanks to Koniu) - reorganized screen handling to support resizing - make geometry checking optional and off by default - allow suspending wavemon (CTRL-Z) on most screens * Info screen: - suppress obvious broadcast address (deriving from prefix) - special case for "no interface address assigned" - display interface MTU if different from the default value - show interface flags and TX queue length if enough space * Scan window: - set up enable interface if it is down (otherwise no scan) - drop support for wireless extensions < 18 (minimum requirement) - clean up child scan process on error (avoid garbled screen) * Configuration screen: - support 'Cisco-style' MAC addresses (dots rather than colons) * Fixes: - do not report zero bitrates (Info screen, 802.11n interface) - remove unused timer in Scan window (data sampling is now confined to info/histogram screens) - fixed various compiler warnings - fixed several problems in and improved the random generator - use sigaction() rather than signal() - miscellaneous reorganization of the configuration menu code -------------------- 0.7.1 (2010-12-24) -------------------- * Scan window: - improved and more consistent formatting of entries - better responsiveness by running scan operation as child process * Security capabilities: - display capabilities supported by the driver - display current and available key sizes - decode relevant IE information * check and warn about insufficient permissions (CAP_NET_ADMIN) that are required for several wireless operations (e.g. scanning) * Bug Fixes: - avoid triggering assertion on large screens - require minimum version (18) of the wireless extensions - better clearing of info screen - various cleanups * overhaul of manpages -------------------- 0.7.0 (2010-10-18) -------------------- * NEW Scan Window: this replaces the old access point screen. It displays a sorted list (in descending order of signal quality) of access points and other wireless clients within range, periodically refreshed at a configurable rate. * Access Point screen: finally deprecated this - the SIOCGIWAPLIST ioctl itself is deprecated and works only on some older kernels. * Info Window: clear screen to erase stale data. * Bug Fixes: fixed a compiler warning in string-tolower operation. -------------------- 0.6.11 (2010-06-14) -------------------- * Info screen: - fixed a bug in the freq/channel display: some drivers, such as ipw2100, only return the channel number, requiring to look up the frequency; - added a warning to indicate the absence of data from the interface. * Histogram screen: Tag the grid lines with the corresponding dBm levels: - no levels are shown if there is no valid interface data (screen empty); - level tags are restricted to the signal level graph only: o if both signal and noise level data are valid, there are three graphs (signal level, noise level, SNR), which would cause ambiguity, o many cards (e.g. ath5k, rt73usb, iwl3945) do not supply noise data, so that the histogram screen will then only contain the level graph; - since the scale is adjustable, while the grid lines are fixed, the tags are dynamically recomputed based on the y position and current scale; - the min/max bounds of the scale are also shown. Thanks to Sean Muir for this suggestion. * Updated manpage regarding access point screen; as soon there is time, the outdated and restricted SIOCGIWAPLIST ioctl should be replaced by a scan. -------------------- 0.6.10 (2009-10-25) -------------------- * Bug Fixes: - fixed installation bug which caused manpages to be installed in /man; - fixed mode of installation to support sandbox-builds on Gentoo (Lovelace); - improved (automated) linking process, now possible to pass LDFLAGS (Boeck); - bail out when no statistics are available, instead of flagging an error. -------------------- 0.6.7 (2009-07-09) -------------------- * Screen geometry: Reduced the screen size again, to 24x80, since this is a widely used default size (thanks to Nelson A. de Oliveira). Removed the "wireless extensions" information from the information screen, to reduce the minimum required number of lines from 25 to 24. The wireless extension information however remains still available, via the "wavemon -d" dump option. * Histogram screen improvements: - a new revised approach to fix several old bugs and limitations, - new code better observes the boundaries and the configured ranges, - better visibility, signal/noise are now plotted in bold. * Better separation of error messages, into: - informational warning messages (outside ncurses mode), - fatal errors unrelated to system calls, - fatal errors following a system call (with interpretation of errno(3)). * Bug Fixes: - fixed a bug in the histogram initialisation (producing ghost values); - fixed a bug which caused non-ASCII encryption key strings to be garbled; - key string length is now checked to avoid spilling over the right border into the next line; curtailing long keys in a pretty-printing manner. -------------------- 0.6.6 (2009-06-01) -------------------- * Access point screen: Overly long access point lists (more than 6 peer access points) are now truncated, so as not to overwrite screen borders. * Histogram screen: The bottom 'key' window used to replicate the configured range values which are available also via the configuration screen. This replication has now been superseded by a dynamic range-tracking feature: - for each of the three ranges (signal level, noise level, SNR), it shows the min..max values on the screen or 'unknown' if the driver reports values as unknown (e.g. noise levels on iwl3945), - these values correspond to the current histogram and are updated dynamically, thus complementing the visual display. * Bug Fixes: - histogram screen no longer tracks values that the driver reports as invalid; - fixed off-by-one bug which caused the bottom access-point border to be erased; - miscellaneous code clean-ups: o unified description of window sizes via header file constants, o updated and extended source code documentation. -------------------- 0.6.5 (2009-05-08) -------------------- * Several improvements in displaying driver parameters: - ESSID: support for indexed ESS identifiers, - nickname: no longer present empty/void nicknames, - nwid: added support for displaying it (even if is pre-802.11), - IP-address: collapse netmask information into shorter CIDR prefix length, - mode: preliminary support to display 802.11s mesh mode (2.6.26), - retry: new code to display information about MAC-layer retransmissions, - frequency: suppress trailing zeroes, unified conversion, - channel: now also prints the number of the current channel, - encryption keys: added pretty-printing, suppress always-present '0' index, - sensitivity: only print if card supports it, now also supports dBm values, - tx-power: better support for the various formats (relative | mW | dBm), - power management: improved output formatting. * Mode-sensitive display: - when operating in 'monitor' or 'access point' mode, the 'access point' field is not necessary or redundant (in AP mode, it is the same as the mac address shown in the network window), hence omitted; - when operating in 'master', 'repeater' or 'secondary' mode, the bitrate information is also not needed and hence suppressed; - wavemon is now able to distinguish auto-configured values (using upper-case labels) from manually configured values (using standard lower-case labels). * Encryption keys (only with CAP_NET_ADMIN privileges): display of all-ASCII keys is now in cleartext, otherwise the hex format is now pretty-printed. * Screen Layout: redundancies (repeated interface name) have been removed, the whole screen fits again into a 25x80 console window. In addition, wavemon tests if the screen size and bails out if the screen is too small for a readable layout. * Better display of MAC addresses: - detects the "not associated" state of a managed client and then - displays 'non-associated' state instead of showing zero MAC address, - sanity checks on access point addresses (broadcast address is invalid), - Ethernet addresses are automatically translated to symbolic names if a mapping exists for the MAC address in /etc/ethers. * The Access Point List is now sorted in descending order of signal strength. * The LC_NUMERIC locale is honoured: when displaying large numbers (e.g. packet counters, numbers are now grouped according to the locale. * Bug Fixes: - fixed several conditions which caused the window borders to be punctured; - fixed a bug in the display of MAC addresses on the access point screen; - RTS/CTS threshold suffered from the same problem - also fixed, - fixed broken display of frag[mentation] threshold; - fixed a bug which caused empty nicknames ("") to be displayed; - fixed several smaller bugs, thanks to sparse. -------------------- 0.6 (2009-04-01) -------------------- * Update to the latest wireless extension API (number 22), As a consequence of updating the wireless extensions, more information is now displayed, including - excessive TX MAC reassembly retries, - missed beacon count - display of wireless extension version used by the driver - the driver's internal WE version * wavemon now uses ioctl to obtain IW statistics and falls back to /proc/net/wireless only as a last resort. (It is planned to add netlink support in a later release.) * Linear scales (-l option) no longer supported, since levels are always reported both in log (dBm) and lin (mW). (NB: in case of using an old version of ~/.wavemonrc, it is necessary to remove the line containing 'linear_scale', which is the option that has been removed.) * Added sanity checks to the display of level information: - noise level is only displayed if valid signal levels exist; - some cards set a magic number to mean an invalid noise level - a constant has been added to catch such cases; - if the noise level (and hence the SNR value) is undefined, spread out the display of quality/signal levels over the info window. * Added gui option for smoothing level meters (better readability). * Much better and more accurate information in the Access Point List. * Bug Fixes: - it is finally possible to use the Access Point List as start screen, - updated and fixed display of operation modes, - selecting the wireless interface now works with multiple interfaces, - header inclusion bug fix for Puppy Linux (thanks to Tom Gordon). * Updated all components of the autoconf build system: - DESTDIR is now supported for building packages (thanks to Christoph J. Thompson), - now using autoconf to set package version and build date. * Reorganised source tree organisation: - merged many small (one-line) header files into one, - created a 'contrib' directory for contributed scripts, - added 'config' directory for autoconf auxiliary scripts - reinstated standard GNU autoconf listing of files -------------------- 0.5 (2009-01-17) -------------------- Update on the build process, new maintainer, git tree to keep the changes. Fixes in this release include * minor bug fixes * build fixups and fixes for the build process * a stub handler for SIGWINCH (still not fully supported) * adding units to the levels (lin/log) * tidied up code -------------------- 0.4.0b (2002-12-21) -------------------- Last release by the original author, Jan Morgenstern. ' href='#n304'>304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
/*
 * Auxiliary declarations and functions imported from iwlib in order to
 * process and parse scan events. This code is copied with little change
 * from wireless tools 30. It remains here until the wext code will be
 * replaced by corresponding netlink calls.
 */
#include "iw_if.h"
#include <search.h>		/* lsearch(3) */

#define MAX_SCAN_WAIT	10000	/* maximum milliseconds spent waiting */

/*
 * Meta-data about all the additional standard Wireless Extension events
 * we know about.
 */
/* Type of headers we know about (basically union iwreq_data) */
#define IW_HEADER_TYPE_NULL	0	/* Not available */
#define IW_HEADER_TYPE_CHAR	2	/* char [IFNAMSIZ] */
#define IW_HEADER_TYPE_UINT	4	/* __u32 */
#define IW_HEADER_TYPE_FREQ	5	/* struct iw_freq */
#define IW_HEADER_TYPE_ADDR	6	/* struct sockaddr */
#define IW_HEADER_TYPE_POINT	8	/* struct iw_point */
#define IW_HEADER_TYPE_PARAM	9	/* struct iw_param */
#define IW_HEADER_TYPE_QUAL	10	/* struct iw_quality */

/* Size (in bytes) of various events */
static const int event_type_size[] = {
	[IW_HEADER_TYPE_NULL]  = IW_EV_LCP_PK_LEN,
	[IW_HEADER_TYPE_CHAR]  = IW_EV_CHAR_PK_LEN,
	[IW_HEADER_TYPE_UINT]  = IW_EV_UINT_PK_LEN,
	[IW_HEADER_TYPE_FREQ]  = IW_EV_FREQ_PK_LEN,
	[IW_HEADER_TYPE_ADDR]  = IW_EV_ADDR_PK_LEN,
	/*
	 * Fix IW_EV_POINT_PK_LEN: some wireless.h versions define this
	 * erroneously as IW_EV_LCP_LEN + 4 (e.g. ESSID will disappear).
	 * The value below is from wireless tools 30.
	 */
	[IW_HEADER_TYPE_POINT] = IW_EV_LCP_PK_LEN + 4,
	[IW_HEADER_TYPE_PARAM] = IW_EV_PARAM_PK_LEN,
	[IW_HEADER_TYPE_QUAL]  = IW_EV_QUAL_PK_LEN
};

/* Handling flags */
#define IW_DESCR_FLAG_NONE	0x0000	/* Obvious */
/* Wrapper level flags */
#define IW_DESCR_FLAG_DUMP	0x0001	/* Not part of the dump command */
#define IW_DESCR_FLAG_EVENT	0x0002	/* Generate an event on SET */
#define IW_DESCR_FLAG_RESTRICT	0x0004	/* GET : request is ROOT only */
				/* SET : Omit payload from generated iwevent */
#define IW_DESCR_FLAG_NOMAX	0x0008	/* GET : no limit on request size */
/* Driver level flags */
#define IW_DESCR_FLAG_WAIT	0x0100	/* Wait for driver event */

struct iw_ioctl_description {
	__u8 header_type;	/* NULL, iw_point or other */
	__u8 token_type;	/* Future */
	__u16 token_size;	/* Granularity of payload */
	__u16 min_tokens;	/* Min acceptable token number */
	__u16 max_tokens;	/* Max acceptable token number */
	__u32 flags;		/* Special handling of the request */
};

/*
 * Meta-data about all the standard Wireless Extension request we
 * know about.
 */
static const struct iw_ioctl_description standard_ioctl_descr[] = {
	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWNAME	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_CHAR,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWNWID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWNWID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWFREQ	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_FREQ,
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWFREQ	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_FREQ,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWMODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_UINT,
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWMODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_UINT,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWSENS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWSENS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWRANGE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWRANGE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= sizeof(struct iw_range),
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWPRIV	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCSIWSTATS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
		.header_type	= IW_HEADER_TYPE_NULL,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct sockaddr),
		.max_tokens	= IW_MAX_SPY,
	},
	[SIOCGIWSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct sockaddr) +
				  sizeof(struct iw_quality),
		.max_tokens	= IW_MAX_SPY,
	},
	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct iw_thrspy),
		.min_tokens	= 1,
		.max_tokens	= 1,
	},
	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct iw_thrspy),
		.min_tokens	= 1,
		.max_tokens	= 1,
	},
	[SIOCSIWAP	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
	},
	[SIOCGIWAP	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWMLME	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= sizeof(struct iw_mlme),
		.max_tokens	= sizeof(struct iw_mlme),
	},
	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct sockaddr) +
				  sizeof(struct iw_quality),
		.max_tokens	= IW_MAX_AP,
		.flags		= IW_DESCR_FLAG_NOMAX,
	},
	[SIOCSIWSCAN	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= 0,
		.max_tokens	= sizeof(struct iw_scan_req),
	},
	[SIOCGIWSCAN	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_SCAN_MAX_DATA,
		.flags		= IW_DESCR_FLAG_NOMAX,
	},
	[SIOCSIWESSID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ESSID_MAX_SIZE + 1,
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWESSID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ESSID_MAX_SIZE + 1,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWNICKN	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ESSID_MAX_SIZE + 1,
	},
	[SIOCGIWNICKN	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ESSID_MAX_SIZE + 1,
	},
	[SIOCSIWRATE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWRATE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWRTS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWRTS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWFRAG	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWFRAG	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWRETRY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWRETRY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWENCODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ENCODING_TOKEN_MAX,
		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
	},
	[SIOCGIWENCODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ENCODING_TOKEN_MAX,
		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
	},
	[SIOCSIWPOWER	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWPOWER	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
#ifdef SIOCSIWMODUL
	[SIOCSIWMODUL	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
#endif
#ifdef SIOCGIWMODUL
	[SIOCGIWMODUL	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
#endif
	[SIOCSIWGENIE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[SIOCGIWGENIE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[SIOCSIWAUTH	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWAUTH	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= sizeof(struct iw_encode_ext),
		.max_tokens	= sizeof(struct iw_encode_ext) +
				  IW_ENCODING_TOKEN_MAX,
	},
	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= sizeof(struct iw_encode_ext),
		.max_tokens	= sizeof(struct iw_encode_ext) +
				  IW_ENCODING_TOKEN_MAX,
	},
	[SIOCSIWPMKSA - SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= sizeof(struct iw_pmksa),
		.max_tokens	= sizeof(struct iw_pmksa),
	},
};

static const struct iw_ioctl_description standard_event_descr[] = {
	[IWEVTXDROP - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
	},
	[IWEVQUAL - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_QUAL,
	},
	[IWEVCUSTOM - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_CUSTOM_MAX,
	},
	[IWEVREGISTERED - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
	},
	[IWEVEXPIRED - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
	},
	[IWEVGENIE - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[IWEVMICHAELMICFAILURE - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= sizeof(struct iw_michaelmicfailure),
	},
	[IWEVASSOCREQIE - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[IWEVASSOCRESPIE - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[IWEVPMKIDCAND - IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= sizeof(struct iw_pmkid_cand),
	},
};

struct stream_descr {
	char *current;		/* Current event in stream of events */
	char *value;		/* Current value in event */
	char *end;		/* End of the stream */
};

/*
 * Extract the next event from the event stream.
 */
static int iw_extract_event_stream(struct stream_descr *stream,
				   struct iw_event *iwe, int we_version)
{
	const struct iw_ioctl_description *descr = NULL;
	int event_type;
	unsigned int event_len = 1;	/* Invalid */
	unsigned cmd_index;	/* *MUST* be unsigned */
	char *pointer;

	if (stream->current + IW_EV_LCP_PK_LEN > stream->end)
		return 0;

	/* Extract the event header to get the event id.
	 * Note : the event may be unaligned, therefore copy... */
	memcpy((char *)iwe, stream->current, IW_EV_LCP_PK_LEN);

	if (iwe->len <= IW_EV_LCP_PK_LEN)
		return -1;

	/* Get the type and length of that event */
	if (iwe->cmd <= SIOCIWLAST) {
		cmd_index = iwe->cmd - SIOCIWFIRST;
		if (cmd_index < ARRAY_SIZE(standard_ioctl_descr))
			descr = standard_ioctl_descr + cmd_index;
	} else {
		cmd_index = iwe->cmd - IWEVFIRST;
		if (cmd_index < ARRAY_SIZE(standard_event_descr))
			descr = standard_event_descr + cmd_index;
	}

	/* Unknown events -> event_type = 0  =>  IW_EV_LCP_PK_LEN */
	event_type = descr ? descr->header_type : 0;
	event_len  = event_type_size[event_type];

	/* Check if we know about this event */
	if (event_len <= IW_EV_LCP_PK_LEN) {
		stream->current += iwe->len;			/* Skip to next event */
		return 2;
	}
	event_len -= IW_EV_LCP_PK_LEN;

	/* Fixup for earlier version of WE */
	if (we_version <= 18 && event_type == IW_HEADER_TYPE_POINT)
		event_len += IW_EV_POINT_OFF;

	if (stream->value != NULL)
		pointer = stream->value;			/* Next value in event */
	else
		pointer = stream->current + IW_EV_LCP_PK_LEN;	/* First value in event */

	/* Copy the rest of the event (at least, fixed part) */
	if (pointer + event_len > stream->end) {
		stream->current += iwe->len;			/* Skip to next event */
		return -2;
	}

	/* Fixup for WE-19 and later: pointer no longer in the stream */
	/* Beware of alignment. Dest has local alignment, not packed */
	if (we_version > 18 && event_type == IW_HEADER_TYPE_POINT)
		memcpy((char *)iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, pointer, event_len);
	else
		memcpy((char *)iwe + IW_EV_LCP_LEN, pointer, event_len);

	/* Skip event in the stream */
	pointer += event_len;

	/* Special processing for iw_point events */
	if (event_type == IW_HEADER_TYPE_POINT) {
		unsigned int extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN);

		if (extra_len > 0) {
			/* Set pointer on variable part (warning : non aligned) */
			iwe->u.data.pointer = pointer;

			/* Check that we have a descriptor for the command */
			if (descr == NULL) {
				/* Can't check payload -> unsafe... */
				iwe->u.data.pointer = NULL;	/* Discard paylod */
			} else {
				unsigned int token_len = iwe->u.data.length * descr->token_size;
				/*
				 * Ugly fixup for alignment issues.
				 * If the kernel is 64 bits and userspace 32 bits, we have an extra 4 + 4
				 * bytes. Fixing that in the kernel would break 64 bits userspace.
				 */
				if (token_len != extra_len && extra_len >= 4) {
					union iw_align_u16 {
						__u16 value;
						unsigned char byte[2];
					} alt_dlen;
					unsigned int alt_token_len;

					/* Userspace seems to not always like unaligned access,
					 * so be careful and make sure to align value.
					 * I hope gcc won't play any of its aliasing tricks... */
					alt_dlen.byte[0] = *(pointer);
					alt_dlen.byte[1] = *(pointer + 1);
					alt_token_len = alt_dlen.value * descr->token_size;

					/* Verify that data is consistent if assuming 64 bit alignment... */
					if (alt_token_len + 8 == extra_len) {

						/* Ok, let's redo everything */
						pointer -= event_len;
						pointer += 4;

						/* Dest has local alignment, not packed */
						memcpy((char *)iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, pointer, event_len);
						pointer += event_len + 4;
						token_len = alt_token_len;

						/* We may have no payload */
						if (alt_token_len)
							iwe->u.data.pointer = pointer;
						else
							iwe->u.data.pointer = NULL;
					}
				}

				/* Discard bogus events which advertise more tokens than they carry ... */
				if (token_len > extra_len)
					iwe->u.data.pointer = NULL;	/* Discard paylod */

				/* Check that the advertised token size is not going to
				 * produce buffer overflow to our caller... */
				if (iwe->u.data.length > descr->max_tokens
				    && !(descr->flags & IW_DESCR_FLAG_NOMAX))
					iwe->u.data.pointer = NULL;	/* Discard payload */

				/* Same for underflows... */
				if (iwe->u.data.length < descr->min_tokens)
					iwe->u.data.pointer = NULL;	/* Discard paylod */
			}
		} else {
			/* No data */
			iwe->u.data.pointer = NULL;
		}

		stream->current += iwe->len;			/* Go to next event */
	} else {
		/*
		 * Ugly fixup for alignment issues.
		 * If the kernel is 64 bits and userspace 32 bits, we have an extra 4 bytes.
		 * Fixing that in the kernel would break 64 bits userspace.
		 */
		if (stream->value == NULL &&
		    ((iwe->len - IW_EV_LCP_PK_LEN) % event_len == 4 ||
		     (iwe->len == 12 && (event_type == IW_HEADER_TYPE_UINT ||
					 event_type == IW_HEADER_TYPE_QUAL)))) {

			pointer -= event_len;
			pointer += 4;

			/* Beware of alignment. Dest has local alignment, not packed */
			memcpy((char *)iwe + IW_EV_LCP_LEN, pointer, event_len);
			pointer += event_len;
		}

		if (pointer + event_len <= stream->current + iwe->len) {
			stream->value = pointer;		/* Go to next value */
		} else {
			stream->value = NULL;
			stream->current += iwe->len;		/* Go to next event */
		}
	}
	return 1;
}

static void iw_extract_ie(struct iw_event *iwe, struct scan_result *sr)
{
	const uint8_t wpa1_oui[3] = { 0x00, 0x50, 0xf2 };
	uint8_t *buffer = iwe->u.data.pointer;
	int ielen = 0, ietype, i;

	/* Loop on each IE, each is min. 2 bytes TLV: IE-ID - Length - Value */
	for (i = 0; i <= iwe->u.data.length - 2;  i += ielen + 2) {
		ietype = buffer[i];
		ielen  = buffer[i + 1];

		switch (ietype) {
		case 0x30:
			if (ielen < 4)	/* make sure we have enough data */
				continue;
			sr->flags |= IW_ENC_CAPA_WPA2;
			break;
		case 0xdd:
			/* Not all IEs that start with 0xdd are WPA1 */
			if (ielen < 8 || memcmp(buffer + i + 2, wpa1_oui, 3) ||
			    buffer[i + 5] != 1)
				continue;
			sr->flags |= IW_ENC_CAPA_WPA;
			break;
		}
	}
}
/*----------------- End of code copied from iwlib -----------------------*/

/*
 * Ordering functions for scan results
 */
/* Order by ascending frequency. */
static int cmp_chan(const struct scan_result *a, const struct scan_result *b)
{
	return a->freq < b->freq;
}

static int cmp_chan_rev(const struct scan_result *a, const struct scan_result *b)
{
	return cmp_chan(b, a);
}

/* Order by descending signal strength. */
static int cmp_sig(const struct scan_result *a, const struct scan_result *b)
{
	return a->qual.level - b->qual.level;
}

/* Order by ascending frequency first, then by descending signal strength. */
static int cmp_chan_sig(const struct scan_result *a, const struct scan_result *b)
{
	return a->freq == b->freq ? cmp_sig(a, b) : cmp_chan(a, b);
}

/* Show open access points first, ordered by mode. */
static int cmp_open(const struct scan_result *a, const struct scan_result *b)
{
	return a->has_key > b->has_key;
}

/* Sort (open) access points by descending signal strength. */
static int cmp_open_sig(const struct scan_result *a, const struct scan_result *b)
{
	return a->has_key == b->has_key ? cmp_sig(a, b) : cmp_open(a, b);
}

/* Sort (open) access points by ascending frequency, then by descending signal. */
static int cmp_open_chan_sig(const struct scan_result *a, const struct scan_result *b)
{
	return a->has_key == b->has_key ? cmp_chan_sig(a, b) : cmp_open(a, b);
}

static int (*scan_cmp[])(const struct scan_result *, const struct scan_result *) = {
	[SO_CHAN]	= cmp_chan,
	[SO_CHAN_REV]	= cmp_chan_rev,
	[SO_SIGNAL]	= cmp_sig,
	[SO_OPEN]	= cmp_open,
	[SO_CHAN_SIG]	= cmp_chan_sig,
	[SO_OPEN_SIG]	= cmp_open_sig,
	[SO_OPEN_CH_SI]	= cmp_open_chan_sig
};

struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version)
{
	struct scan_result *head = NULL;
	struct iwreq wrq;
	int wait, waited = 0;
	/*
	 * Some drivers may return very large scan results, either because there
	 * are many cells, or there are many large elements. Do not bother to
	 * guess buffer size, use maximum u16 wrq.u.data.length size.
	 */
	char scan_buf[0xffff];

	/* We are checking errno when returning NULL, so reset it here */
	errno = 0;

	memset(&wrq, 0, sizeof(wrq));
	strncpy(wrq.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ);
	if (ioctl(skfd, SIOCSIWSCAN, &wrq) < 0)
		return NULL;

	/* Larger initial timeout of 250ms between set and first get */
	for (wait = 250; (waited += wait) < MAX_SCAN_WAIT; wait = 100) {
		struct timeval tv = { 0, wait * 1000 };

		while (select(0, NULL, NULL, NULL, &tv) < 0)
			if (errno != EINTR && errno != EAGAIN)
				return NULL;

		wrq.u.data.pointer = scan_buf;
		wrq.u.data.length  = sizeof(scan_buf);
		wrq.u.data.flags   = 0;

		if (ioctl(skfd, SIOCGIWSCAN, &wrq) == 0)
			break;
	}

	if (wrq.u.data.length) {
		struct iw_event iwe;
		struct stream_descr stream;
		struct scan_result *new = NULL;
		int f = 0;		/* Idea taken from waproamd */

		memset(&stream, 0, sizeof(stream));
		stream.current = scan_buf;
		stream.end     = scan_buf + wrq.u.data.length;

		while (iw_extract_event_stream(&stream, &iwe, we_version) > 0) {
        		if (!new)
				new = calloc(1, sizeof(*new));

			switch (iwe.cmd) {
			case SIOCGIWAP:
                		f = 1;
				memcpy(&new->ap_addr, &iwe.u.ap_addr.sa_data, sizeof(new->ap_addr));
				break;
			case SIOCGIWESSID:
                		f |= 2;
				memset(new->essid, 0, sizeof(new->essid));

				if (iwe.u.essid.flags && iwe.u.essid.pointer && iwe.u.essid.length)
					memcpy(new->essid, iwe.u.essid.pointer, iwe.u.essid.length);
				break;
			case SIOCGIWMODE:
				new->mode = iwe.u.mode;
                    		f |= 4;
				break;
			case SIOCGIWFREQ:
                		f |= 8;
				new->freq = freq_to_hz(&iwe.u.freq);
				break;
			case SIOCGIWENCODE:
                		f |= 16;
				new->has_key = !(iwe.u.data.flags & IW_ENCODE_DISABLED);
				break;
			case IWEVQUAL:
				f |= 32;
				memcpy(&new->qual, &iwe.u.qual, sizeof(struct iw_quality));
				break;
			case IWEVGENIE:
				f |= 64;
				iw_extract_ie(&iwe, new);
				break;
			}
			if (f == 127) {
				struct scan_result *cur = head, **prev = &head;

				while (cur && scan_cmp[conf.scan_sort_order](cur, new) > 0)
					prev = &cur->next, cur = cur->next;

				*prev     = new;
				new->next = cur;
				new       = NULL;
				f = 0;
			}
		}
		free(new);	/* may have been allocated but not filled in */
	}
	return head;
}

void free_scan_result(struct scan_result *head)
{
	if (head) {
		free_scan_result(head->next);
		free(head);
	}
}

/*
 * Channel statistics
 */


/*
 * For lfind, it compares key value with array member, needs to
 * return 0 if they are the same, non-0 otherwise.
 */
static int cmp_key(const void *a, const void *b)
{
	return ((struct cnt *)a)->val - ((struct cnt *)b)->val;
}

/* For quick-sorting the array in descending order of counts */
static int cmp_cnt(const void *a, const void *b)
{
	if (conf.scan_sort_order == SO_CHAN_REV)
		return ((struct cnt *)a)->count - ((struct cnt *)b)->count;
	return ((struct cnt *)b)->count - ((struct cnt *)a)->count;
}

struct cnt *channel_stats(struct scan_result *head,
			  struct iw_range *iw_range, int *max_cnt)
{
	struct scan_result *cur;
	struct cnt *arr = NULL, *bin, key = {0, 0};
	size_t cnt = 0, n = 0;

	for (cur = head; cur; cur = cur->next)
		cnt++;
	if (!cnt)
		return NULL;

	arr = calloc(cnt, sizeof(key));
	for (cur = head; cur && n < *max_cnt; cur = cur->next) {
		key.val = freq_to_channel(cur->freq, iw_range);

		if (key.val >= 0) {
			bin = lsearch(&key, arr, &n, sizeof(key), cmp_key);
			if (bin)
				bin->count++;
		}
	}

	if (n < *max_cnt)
		*max_cnt = n;
	if (n > 0) {
		qsort(arr, n, sizeof(key), cmp_cnt);
	} else {
		free(arr);
		return NULL;
	}
	return arr;
}