pax_global_header00006660000000000000000000000064127354376650014534gustar00rootroot0000000000000052 comment=6283f3b0769448d02e9eda03f1e68d58102363e4 horst-version-5.0/000077500000000000000000000000001273543766500142225ustar00rootroot00000000000000horst-version-5.0/.gitignore000066400000000000000000000000751273543766500162140ustar00rootroot00000000000000horst *.o .buildflags .cproject .objdeps.mk .project *.kdev4 horst-version-5.0/LICENSE000066400000000000000000000431331273543766500152330ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. horst-version-5.0/Makefile000066400000000000000000000050741273543766500156700ustar00rootroot00000000000000# horst - Highly Optimized Radio Scanning Tool # # Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # build options DEBUG=1 PCAP=0 WEXT=0 LIBNL=3.0 OSX=0 NAME=horst OBJS= \ average.o \ capture$(if $(filter 1,$(PCAP)),-pcap).o \ channel.o \ conf_options.o \ control.o \ display-channel.o \ display-essid.o \ display-filter.o \ display-help.o \ display-history.o \ display-main.o \ display-spectrum.o \ display-statistics.o \ display.o \ essid.o \ ieee80211_util.o \ ifctrl-ioctl.o \ listsort.o \ main.o \ network.o \ node.o \ protocol_parser.o \ wlan_parser.o \ radiotap/radiotap.o \ util.o \ wlan_util.o LIBS=-lncurses -lm CFLAGS+=-std=gnu99 -Wall -Wextra -g -I. ifeq ($(OSX),1) PCAP=1 WEXT=0 LIBNL=0 LIBS+=-framework CoreWLAN -framework CoreData -framework Foundation endif ifeq ($(DEBUG),1) CFLAGS+=-DDO_DEBUG endif ifeq ($(PCAP),1) CFLAGS+=-DPCAP LIBS+=-lpcap endif ifeq ($(WEXT),1) OBJS += ifctrl-wext.o else ifeq ($(LIBNL),0) ifeq ($(OSX),0) OBJS += ifctrl-dummy.o endif else OBJS += ifctrl-nl80211.o CFLAGS += $(shell pkg-config --cflags libnl-$(LIBNL)) ifeq ($(LIBNL),tiny) LIBS+=-lnl-tiny else LIBS+=-lnl-3 -lnl-genl-3 endif endif endif .PHONY: all check clean force all: $(NAME) .objdeps.mk: $(OBJS:%.o=%.c) gcc -MM -I. $^ >$@ ifeq ($(OSX),1) gcc -MM -I. ifctrl-osx.m >>$@ endif -include .objdeps.mk ifeq ($(OSX),1) OBJS += ifctrl-osx.o endif $(NAME): $(OBJS) $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(OBJS): .buildflags check: sparse $(CFLAGS) *.[ch] clean: -rm -f *.o radiotap/*.o *~ -rm -f $(NAME) -rm -f .buildflags -rm -f .objdeps.mk .buildflags: force echo '$(CFLAGS)' | cmp -s - $@ || echo '$(CFLAGS)' > $@ horst-version-5.0/README.md000066400000000000000000000147711273543766500155130ustar00rootroot00000000000000# HORST - Highly Optimized Radio Scanning Tool or "Horsts OLSR Radio Scanning Tool" Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) and licensed under the GNU Public License (GPL) V2 ## Links * Main page: https://github.com/br101/horst * Issue tracker: https://github.com/br101/horst/issues * Download Stable (Version 5.0): https://github.com/br101/horst/archive/version-5.0.tar.gz * Download Development (MASTER): https://github.com/br101/horst/tarball/master ## Overview `horst` is a small, lightweight IEEE802.11 WLAN analyzer with a text interface. Its basic function is similar to tcpdump, Wireshark or Kismet, but it's much smaller and shows different, aggregated information which is not easily available from other tools. It is made for debugging wireless LANs with a focus on getting a quick overview instead of deep packet inspection and has special features for Ad-hoc (IBSS) mode and mesh networks. It can be useful to get a quick overview of what's going on on all wireless LAN channels and to identify problems. * Shows signal (RSSI) values per station, something hard to get, especially in IBSS mode * Calculates channel utilization (“usage”) by adding up the amount of time the packets actually occupy the medium * “Spectrum Analyzer” shows signal levels and usage per channel * Graphical packet history, with signal, packet type and physical rate * Shows all stations per ESSID and the live TSF per node as it is counting * Detects IBSS “splits” (same ESSID but different BSSID – this is/was a common driver problem on IBSS mode) * Statistics of packets/bytes per physical rate and per packet type * Has some support for mesh protocols (OLSR and batman) * Can filter specific packet types, operating modes, source addresses or BSSIDs * Client/server support for monitoring on remote nodes * Automatically adds and removes monitor interface `horst` is a Linux program and can be used on any wireless LAN interface which supports monitor mode. The latest stable version is 5.0 from July 01 2016. ## Building git clone https://github.com/br101/horst `horst` is just a simple tool, and `libncurses` and header files is the only hard requirement. Recently we have added support for `nl80211` via `libnl`, so normally you need `libnl3` + header files as well. Building is as simple as typing: make If you have an old or proprietary WLAN driver which only knows the deprecated `wireless extensions` you can build `horst` with support for them: make WEXT=1 To experimentally build using libpcap (note that libpcap on Linux is not necessary and not recommended) use: make PCAP=1 If you build on another Unix which is not Linux, you probably need to disable libnl: make PCAP=1 LIBNL=0 To build for Mac OSX: make OSX=1 Please note that PCAP and OSX support is not so well tested and some features may be missing. ## Usage notes With version 5.0 `horst` can automatically set the WLAN interface into monitor mode or add a monitor interface. But you can still set the interface into monitor mode manually before you start `horst` as well. With most standard Linux (mac80211) drivers you can use the `iw` command to add an additional monitor interface while you can continue to use the existing interface: `iw wlan0 interface add mon0 type monitor`. (If you have to use the deprecated WEXT interface can put the interface into monitor mode like this `iwconfig wlan0 mode monitor channel X`). Please note that if the main interface (`wlan0`) is in use, either as a client to an AP, in Ad-hoc mode, or creating an AP, the wifi driver does not allow `horst` to change the channel because that would disrupt connectivity. If you want `horst` to be able to change channels (`horst -s` or `channel_scan` option) you need to have only monitor interfaces. This is how you usually set an existing interface to monitor mode and a specific channel: iw wlan0 set type monitor ifconfig wlan0 up iw wlan0 set channel 6 Usually you have to start `horst` as root: sudo horst -i wlan0 To do remote monitoring over the network you can start a server (-q without a user interface), usually on your AP or device with horst -i wlan0 -C -q and connect a client (only one client is allowed at a time), usually from your PC with horst -c IP Please read the man page for more details about the options, output and abbreviations. It should be be part of your distribution package, but you can read it in the source code locally with: man -l horst.1 man -l horst.conf.5 Please contact me if you have any problems or questions. New feature ideas, patches and feedback are always welcome. Please create GitHub issues at https://github.com/br101/horst/issues for problem reports and support. ## Background and history `horst` was created in 2005 to fill a need in the Wireless Mesh networking and Freifunk community of Berlin but has since grown to be a useful tool for debugging any kind of wireless network. A notorious Berlin Freifunk community member known as "Offline Horst" had enough persistence to convince me that such a tool is necessary and thus started the development and gave the name to the `horst` tool. With the usual wireless tools like iw, iwconfig and iwspy and even kismet or WireShark it is hard to measure the received signal strength (RSSI) of all available access points, stations and ad-hoc networks in a given location. It's especially difficult to differentiate the different nodes which form an ad-hoc network. This information however is very important for setting up, debugging and optimizing wireless mesh networks and antenna positions. `horst` aims to fill this gap and lists each single node of an ad-hoc network separately, showing the signal strength (RSSI) of the last received packet. This way you can see which nodes are part of a specific ad-hoc cell (BSSID), discover problems with ad-hoc cell merging ("cell splitting", a problem of many WLAN drivers) and get a general overview of what's going on in the "air". To do this, `horst` uses the monitor mode including radiotap headers (or before prism2 headers) for the signal strength information of the wlan cards and listens to all packets which come in the wireless interface. The packets are summarized by the MAC address of the sending node, analyzed and aggregated and displayed in a simple text (ncurses) interface. ## Contributors Thanks to the following persons for contributions: * Horst Krause * Sven-Ola Tuecke * Robert Schuster * Jonathan Guerin * David Rowe * Antoine Beaupré * Rami Refaeli * Joerg Albert * Tuomas Räsänen * Jiantao Fu horst-version-5.0/average.c000066400000000000000000000035211273543766500160010ustar00rootroot00000000000000/* * lib/average.c * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #include #include "average.h" #include "util.h" /** * DOC: Exponentially Weighted Moving Average (EWMA) * * These are generic functions for calculating Exponentially Weighted Moving * Averages (EWMA). We keep a structure with the EWMA parameters and a scaled * up internal representation of the average value to prevent rounding errors. * The factor for scaling up and the exponential weight (or decay rate) have to * be specified thru the init fuction. The structure should not be accessed * directly but only thru the helper functions. */ /** * ewma_init() - Initialize EWMA parameters * @avg: Average structure * @factor: Factor to use for the scaled up internal value. The maximum value * of averages can be ULONG_MAX/(factor*weight). For performance reasons * factor has to be a power of 2. * @weight: Exponential weight, or decay rate. This defines how fast the * influence of older values decreases. For performance reasons weight has * to be a power of 2. * * Initialize the EWMA parameters for a given struct ewma @avg. */ void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight) { if(!is_power_of_2(weight) || !is_power_of_2(factor)) err(1, "weight and factor have to be a power of two!"); avg->weight = ilog2(weight); avg->factor = ilog2(factor); avg->internal = 0; } /** * ewma_add() - Exponentially weighted moving average (EWMA) * @avg: Average structure * @val: Current value * * Add a sample to the average. */ struct ewma *ewma_add(struct ewma *avg, unsigned long val) { avg->internal = avg->internal ? (((avg->internal << avg->weight) - avg->internal) + (val << avg->factor)) >> avg->weight : (val << avg->factor); return avg; } horst-version-5.0/average.h000066400000000000000000000012111273543766500160000ustar00rootroot00000000000000#ifndef _LINUX_AVERAGE_H #define _LINUX_AVERAGE_H /* Exponentially weighted moving average (EWMA) */ /* For more documentation see average.c */ struct ewma { unsigned long internal; unsigned long factor; unsigned long weight; }; extern void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight); extern struct ewma *ewma_add(struct ewma *avg, unsigned long val); /** * ewma_read() - Get average value * @avg: Average structure * * Returns the average value held in @avg. */ static inline unsigned long ewma_read(const struct ewma *avg) { return avg->internal >> avg->factor; } #endif /* _LINUX_AVERAGE_H */ horst-version-5.0/batman_adv_header-14.h000066400000000000000000000130751273543766500202270ustar00rootroot00000000000000/* * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA * */ #ifndef _NET_BATMAN_ADV_PACKET_H_ #define _NET_BATMAN_ADV_PACKET_H_ #include #define ETH_ALEN 6 #define ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ enum bat_packettype { BAT_OGM = 0x01, BAT_ICMP = 0x02, BAT_UNICAST = 0x03, BAT_BCAST = 0x04, BAT_VIS = 0x05, BAT_UNICAST_FRAG = 0x06, BAT_TT_QUERY = 0x07, BAT_ROAM_ADV = 0x08 }; /* this file is included by batctl which needs these defines */ #define COMPAT_VERSION 14 enum batman_flags { PRIMARIES_FIRST_HOP = 1 << 4, VIS_SERVER = 1 << 5, DIRECTLINK = 1 << 6 }; /* ICMP message types */ enum icmp_packettype { ECHO_REPLY = 0, DESTINATION_UNREACHABLE = 3, ECHO_REQUEST = 8, TTL_EXCEEDED = 11, PARAMETER_PROBLEM = 12 }; /* vis defines */ enum vis_packettype { VIS_TYPE_SERVER_SYNC = 0, VIS_TYPE_CLIENT_UPDATE = 1 }; /* fragmentation defines */ enum unicast_frag_flags { UNI_FRAG_HEAD = 1 << 0, UNI_FRAG_LARGETAIL = 1 << 1 }; /* TT_QUERY subtypes */ #define TT_QUERY_TYPE_MASK 0x3 enum tt_query_packettype { TT_REQUEST = 0, TT_RESPONSE = 1 }; /* TT_QUERY flags */ enum tt_query_flags { TT_FULL_TABLE = 1 << 2 }; /* TT_CLIENT flags. * Flags from 1 to 1 << 7 are sent on the wire, while flags from 1 << 8 to * 1 << 15 are used for local computation only */ enum tt_client_flags { TT_CLIENT_DEL = 1 << 0, TT_CLIENT_ROAM = 1 << 1, TT_CLIENT_WIFI = 1 << 2, TT_CLIENT_NOPURGE = 1 << 8, TT_CLIENT_NEW = 1 << 9, TT_CLIENT_PENDING = 1 << 10 }; struct batman_ogm_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */ uint32_t seqno; uint8_t orig[6]; uint8_t prev_sender[6]; uint8_t gw_flags; /* flags related to gateway class */ uint8_t tq; uint8_t tt_num_changes; uint8_t ttvn; /* translation table version number */ uint16_t tt_crc; } __attribute__ ((packed)); #define BATMAN_OGM_LEN sizeof(struct batman_ogm_packet) struct icmp_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[6]; uint8_t orig[6]; uint16_t seqno; uint8_t uid; uint8_t reserved; } __attribute__ ((packed)); #define BAT_RR_LEN 16 /* icmp_packet_rr must start with all fields from imcp_packet * as this is assumed by code that handles ICMP packets */ struct icmp_packet_rr { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[6]; uint8_t orig[6]; uint16_t seqno; uint8_t uid; uint8_t rr_cur; uint8_t rr[BAT_RR_LEN][ETH_ALEN]; } __attribute__ ((packed)); struct unicast_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t ttvn; /* destination translation table version number */ uint8_t dest[6]; } __attribute__ ((packed)); struct unicast_frag_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t ttvn; /* destination translation table version number */ uint8_t dest[6]; uint8_t flags; uint8_t align; uint8_t orig[6]; uint16_t seqno; } __attribute__ ((packed)); struct bcast_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t reserved; uint32_t seqno; uint8_t orig[6]; } __attribute__ ((packed)); struct vis_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; /* TTL */ uint8_t vis_type; /* which type of vis-participant sent this? */ uint32_t seqno; /* sequence number */ uint8_t entries; /* number of entries behind this struct */ uint8_t reserved; uint8_t vis_orig[6]; /* originator that announces its neighbors */ uint8_t target_orig[6]; /* who should receive this packet */ uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */ } __attribute__ ((packed)); struct tt_query_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; /* the flag field is a combination of: * - TT_REQUEST or TT_RESPONSE * - TT_FULL_TABLE */ uint8_t flags; uint8_t dst[ETH_ALEN]; uint8_t src[ETH_ALEN]; /* the ttvn field is: * if TT_REQUEST: ttvn that triggered the * request * if TT_RESPONSE: new ttvn for the src * orig_node */ uint8_t ttvn; /* tt_data field is: * if TT_REQUEST: crc associated with the * ttvn * if TT_RESPONSE: table_size */ uint16_t tt_data; } __attribute__ ((packed)); struct roam_adv_packet { uint8_t packet_type; uint8_t version; uint8_t ttl; uint8_t reserved; uint8_t dst[ETH_ALEN]; uint8_t src[ETH_ALEN]; uint8_t client[ETH_ALEN]; } __attribute__ ((packed)); struct tt_change { uint8_t flags; uint8_t addr[ETH_ALEN]; } __attribute__ ((packed)); #endif /* _NET_BATMAN_ADV_PACKET_H_ */ horst-version-5.0/batman_adv_header-15.h000066400000000000000000000403071273543766500202260ustar00rootroot00000000000000/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef _NET_BATMAN_ADV_PACKET_H_ #define _NET_BATMAN_ADV_PACKET_H_ #include #define BIT(_x) (1<_x) typedef uint16_t __be16; typedef uint32_t __be32; #define ETH_ALEN 6 #define __BIG_ENDIAN_BITFIELD /* todo */ /** * enum batadv_packettype - types for batman-adv encapsulated packets * @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV * @BATADV_BCAST: broadcast packets carrying broadcast payload * @BATADV_CODED: network coded packets * * @BATADV_UNICAST: unicast packets carrying unicast payload traffic * @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original * payload packet * @BATADV_UNICAST_4ADDR: unicast packet including the originator address of * the sender * @BATADV_ICMP: unicast packet like IP ICMP used for ping or traceroute * @BATADV_UNICAST_TVLV: unicast packet carrying TVLV containers */ enum batadv_packettype { /* 0x00 - 0x3f: local packets or special rules for handling */ BATADV_IV_OGM = 0x00, BATADV_BCAST = 0x01, BATADV_CODED = 0x02, /* 0x40 - 0x7f: unicast */ #define BATADV_UNICAST_MIN 0x40 BATADV_UNICAST = 0x40, BATADV_UNICAST_FRAG = 0x41, BATADV_UNICAST_4ADDR = 0x42, BATADV_ICMP = 0x43, BATADV_UNICAST_TVLV = 0x44, #define BATADV_UNICAST_MAX 0x7f /* 0x80 - 0xff: reserved */ }; /** * enum batadv_subtype - packet subtype for unicast4addr * @BATADV_P_DATA: user payload * @BATADV_P_DAT_DHT_GET: DHT request message * @BATADV_P_DAT_DHT_PUT: DHT store message * @BATADV_P_DAT_CACHE_REPLY: ARP reply generated by DAT */ enum batadv_subtype { BATADV_P_DATA = 0x01, BATADV_P_DAT_DHT_GET = 0x02, BATADV_P_DAT_DHT_PUT = 0x03, BATADV_P_DAT_CACHE_REPLY = 0x04, }; /* this file is included by batctl which needs these defines */ #define BATADV_COMPAT_VERSION 15 /** * enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets * @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was * previously received from someone else than the best neighbor. * @BATADV_PRIMARIES_FIRST_HOP: flag is set when the primary interface address * is used, and the packet travels its first hop. * @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a * one hop neighbor on the interface where it was originally received. */ enum batadv_iv_flags { BATADV_NOT_BEST_NEXT_HOP = BIT(0), BATADV_PRIMARIES_FIRST_HOP = BIT(1), BATADV_DIRECTLINK = BIT(2), }; /* ICMP message types */ enum batadv_icmp_packettype { BATADV_ECHO_REPLY = 0, BATADV_DESTINATION_UNREACHABLE = 3, BATADV_ECHO_REQUEST = 8, BATADV_TTL_EXCEEDED = 11, BATADV_PARAMETER_PROBLEM = 12, }; /* tt data subtypes */ #define BATADV_TT_DATA_TYPE_MASK 0x0F /** * enum batadv_tt_data_flags - flags for tt data tvlv * @BATADV_TT_OGM_DIFF: TT diff propagated through OGM * @BATADV_TT_REQUEST: TT request message * @BATADV_TT_RESPONSE: TT response message * @BATADV_TT_FULL_TABLE: contains full table to replace existing table */ enum batadv_tt_data_flags { BATADV_TT_OGM_DIFF = BIT(0), BATADV_TT_REQUEST = BIT(1), BATADV_TT_RESPONSE = BIT(2), BATADV_TT_FULL_TABLE = BIT(4), }; /* BATADV_TT_CLIENT flags. * Flags from BIT(0) to BIT(7) are sent on the wire, while flags from BIT(8) to * BIT(15) are used for local computation only. * Flags from BIT(4) to BIT(7) are kept in sync with the rest of the network. */ enum batadv_tt_client_flags { BATADV_TT_CLIENT_DEL = BIT(0), BATADV_TT_CLIENT_ROAM = BIT(1), BATADV_TT_CLIENT_WIFI = BIT(4), BATADV_TT_CLIENT_ISOLA = BIT(5), BATADV_TT_CLIENT_NOPURGE = BIT(8), BATADV_TT_CLIENT_NEW = BIT(9), BATADV_TT_CLIENT_PENDING = BIT(10), BATADV_TT_CLIENT_TEMP = BIT(11), }; /** * batadv_vlan_flags - flags for the four MSB of any vlan ID field * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not */ enum batadv_vlan_flags { BATADV_VLAN_HAS_TAG = BIT(15), }; /* claim frame types for the bridge loop avoidance */ enum batadv_bla_claimframe { BATADV_CLAIM_TYPE_CLAIM = 0x00, BATADV_CLAIM_TYPE_UNCLAIM = 0x01, BATADV_CLAIM_TYPE_ANNOUNCE = 0x02, BATADV_CLAIM_TYPE_REQUEST = 0x03, }; /** * enum batadv_tvlv_type - tvlv type definitions * @BATADV_TVLV_GW: gateway tvlv * @BATADV_TVLV_DAT: distributed arp table tvlv * @BATADV_TVLV_NC: network coding tvlv * @BATADV_TVLV_TT: translation table tvlv * @BATADV_TVLV_ROAM: roaming advertisement tvlv */ enum batadv_tvlv_type { BATADV_TVLV_GW = 0x01, BATADV_TVLV_DAT = 0x02, BATADV_TVLV_NC = 0x03, BATADV_TVLV_TT = 0x04, BATADV_TVLV_ROAM = 0x05, }; #pragma pack(2) /* the destination hardware field in the ARP frame is used to * transport the claim type and the group id */ struct batadv_bla_claim_dst { uint8_t magic[3]; /* FF:43:05 */ uint8_t type; /* bla_claimframe */ __be16 group; /* group id */ }; #pragma pack() /** * struct batadv_ogm_packet - ogm (routing protocol) packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @flags: contains routing relevant flags - see enum batadv_iv_flags * @tvlv_len: length of tvlv data following the ogm header */ struct batadv_ogm_packet { uint8_t packet_type; uint8_t version; uint8_t ttl; uint8_t flags; __be32 seqno; uint8_t orig[ETH_ALEN]; uint8_t prev_sender[ETH_ALEN]; uint8_t reserved; uint8_t tq; __be16 tvlv_len; /* __packed is not needed as the struct size is divisible by 4, * and the largest data type in this struct has a size of 4. */ }; #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet) /** * batadv_icmp_header - common members among all the ICMP packets * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @msg_type: ICMP packet type * @dst: address of the destination node * @orig: address of the source node * @uid: local ICMP socket identifier * @align: not used - useful for alignment purposes only * * This structure is used for ICMP packets parsing only and it is never sent * over the wire. The alignment field at the end is there to ensure that * members are padded the same way as they are in real packets. */ struct batadv_icmp_header { uint8_t packet_type; uint8_t version; uint8_t ttl; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[ETH_ALEN]; uint8_t orig[ETH_ALEN]; uint8_t uid; uint8_t align[3]; }; /** * batadv_icmp_packet - ICMP packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @msg_type: ICMP packet type * @dst: address of the destination node * @orig: address of the source node * @uid: local ICMP socket identifier * @reserved: not used - useful for alignment * @seqno: ICMP sequence number */ struct batadv_icmp_packet { uint8_t packet_type; uint8_t version; uint8_t ttl; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[ETH_ALEN]; uint8_t orig[ETH_ALEN]; uint8_t uid; uint8_t reserved; __be16 seqno; }; #define BATADV_RR_LEN 16 /** * batadv_icmp_packet_rr - ICMP RouteRecord packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @msg_type: ICMP packet type * @dst: address of the destination node * @orig: address of the source node * @uid: local ICMP socket identifier * @rr_cur: number of entries the rr array * @seqno: ICMP sequence number * @rr: route record array */ struct batadv_icmp_packet_rr { uint8_t packet_type; uint8_t version; uint8_t ttl; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[ETH_ALEN]; uint8_t orig[ETH_ALEN]; uint8_t uid; uint8_t rr_cur; __be16 seqno; uint8_t rr[BATADV_RR_LEN][ETH_ALEN]; }; #define BATADV_ICMP_MAX_PACKET_SIZE sizeof(struct batadv_icmp_packet_rr) /* All packet headers in front of an ethernet header have to be completely * divisible by 2 but not by 4 to make the payload after the ethernet * header again 4 bytes boundary aligned. * * A packing of 2 is necessary to avoid extra padding at the end of the struct * caused by a structure member which is larger than two bytes. Otherwise * the structure would not fulfill the previously mentioned rule to avoid the * misalignment of the payload after the ethernet header. It may also lead to * leakage of information when the padding it not initialized before sending. */ #pragma pack(2) /** * struct batadv_unicast_packet - unicast packet for network payload * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @ttvn: translation table version number * @dest: originator destination of the unicast packet */ struct batadv_unicast_packet { uint8_t packet_type; uint8_t version; uint8_t ttl; uint8_t ttvn; /* destination translation table version number */ uint8_t dest[ETH_ALEN]; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; /** * struct batadv_unicast_4addr_packet - extended unicast packet * @u: common unicast packet header * @src: address of the source * @subtype: packet subtype */ struct batadv_unicast_4addr_packet { struct batadv_unicast_packet u; uint8_t src[ETH_ALEN]; uint8_t subtype; uint8_t reserved; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; /** * struct batadv_frag_packet - fragmented packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @dest: final destination used when routing fragments * @orig: originator of the fragment used when merging the packet * @no: fragment number within this sequence * @reserved: reserved byte for alignment * @seqno: sequence identification * @total_size: size of the merged packet */ struct batadv_frag_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; #if defined(__BIG_ENDIAN_BITFIELD) uint8_t no:4; uint8_t reserved:4; #elif defined(__LITTLE_ENDIAN_BITFIELD) uint8_t reserved:4; uint8_t no:4; #else #error "unknown bitfield endianess" #endif uint8_t dest[ETH_ALEN]; uint8_t orig[ETH_ALEN]; __be16 seqno; __be16 total_size; }; /** * struct batadv_bcast_packet - broadcast packet for network payload * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @reserved: reserved byte for alignment * @seqno: sequence identification * @orig: originator of the broadcast packet */ struct batadv_bcast_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t reserved; __be32 seqno; uint8_t orig[ETH_ALEN]; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; /** * struct batadv_coded_packet - network coded packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @reserved: Align following fields to 2-byte boundaries * @first_source: original source of first included packet * @first_orig_dest: original destinal of first included packet * @first_crc: checksum of first included packet * @first_ttvn: tt-version number of first included packet * @second_ttl: ttl of second packet * @second_dest: second receiver of this coded packet * @second_source: original source of second included packet * @second_orig_dest: original destination of second included packet * @second_crc: checksum of second included packet * @second_ttvn: tt version number of second included packet * @coded_len: length of network coded part of the payload */ struct batadv_coded_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t first_ttvn; /* uint8_t first_dest[ETH_ALEN]; - saved in mac header destination */ uint8_t first_source[ETH_ALEN]; uint8_t first_orig_dest[ETH_ALEN]; __be32 first_crc; uint8_t second_ttl; uint8_t second_ttvn; uint8_t second_dest[ETH_ALEN]; uint8_t second_source[ETH_ALEN]; uint8_t second_orig_dest[ETH_ALEN]; __be32 second_crc; __be16 coded_len; }; #pragma pack() /** * struct batadv_unicast_tvlv - generic unicast packet with tvlv payload * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @reserved: reserved field (for packet alignment) * @src: address of the source * @dst: address of the destination * @tvlv_len: length of tvlv data following the unicast tvlv header * @align: 2 bytes to align the header to a 4 byte boundry */ struct batadv_unicast_tvlv_packet { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; uint8_t reserved; uint8_t dst[ETH_ALEN]; uint8_t src[ETH_ALEN]; __be16 tvlv_len; uint16_t align; }; /** * struct batadv_tvlv_hdr - base tvlv header struct * @type: tvlv container type (see batadv_tvlv_type) * @version: tvlv container version * @len: tvlv container length */ struct batadv_tvlv_hdr { uint8_t type; uint8_t version; __be16 len; }; /** * struct batadv_tvlv_gateway_data - gateway data propagated through gw tvlv * container * @bandwidth_down: advertised uplink download bandwidth * @bandwidth_up: advertised uplink upload bandwidth */ struct batadv_tvlv_gateway_data { __be32 bandwidth_down; __be32 bandwidth_up; }; /** * struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container * @flags: translation table flags (see batadv_tt_data_flags) * @ttvn: translation table version number * @vlan_num: number of announced VLANs. In the TVLV this struct is followed by * one batadv_tvlv_tt_vlan_data object per announced vlan */ struct batadv_tvlv_tt_data { uint8_t flags; uint8_t ttvn; __be16 num_vlan; }; /** * struct batadv_tvlv_tt_vlan_data - vlan specific tt data propagated through * the tt tvlv container * @crc: crc32 checksum of the entries belonging to this vlan * @vid: vlan identifier * @reserved: unused, useful for alignment purposes */ struct batadv_tvlv_tt_vlan_data { __be32 crc; __be16 vid; uint16_t reserved; }; /** * struct batadv_tvlv_tt_change - translation table diff data * @flags: status indicators concerning the non-mesh client (see * batadv_tt_client_flags) * @reserved: reserved field - useful for alignment purposes only * @addr: mac address of non-mesh client that triggered this tt change * @vid: VLAN identifier */ struct batadv_tvlv_tt_change { uint8_t flags; uint8_t reserved[3]; uint8_t addr[ETH_ALEN]; __be16 vid; }; /** * struct batadv_tvlv_roam_adv - roaming advertisement * @client: mac address of roaming client * @vid: VLAN identifier */ struct batadv_tvlv_roam_adv { uint8_t client[ETH_ALEN]; __be16 vid; }; #endif /* _NET_BATMAN_ADV_PACKET_H_ */ horst-version-5.0/batman_header.h000066400000000000000000000026321273543766500171500ustar00rootroot00000000000000/* copied from batman batman.h */ /* * Copyright (C) 2006 B.A.T.M.A.N. contributors: * Thomas Lopatic, Corinna 'Elektra' Aichele, Axel Neumann, Marek Lindner * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA * */ #ifndef _BATMAN_BATMAN_H #define _BATMAN_BATMAN_H #include #define BAT_PORT 4305 #define BAT_UNIDIRECTIONAL 0x80 #define BAT_DIRECTLINK 0x40 #define BAT_ADDR_STR_LEN 16 #define BAT_TQ_MAX_VALUE 255 struct bat_packet { uint32_t orig; uint32_t old_orig; uint8_t flags; /* 0x80: UNIDIRECTIONAL link, 0x40: DIRECTLINK flag, ... */ uint8_t ttl; uint16_t seqno; uint8_t gwflags; /* flags related to gateway functions: gateway class */ uint8_t version; /* batman version field */ uint8_t tq; } __attribute__((packed)); #endif horst-version-5.0/capture-pcap.c000066400000000000000000000054211273543766500167540ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * Copyright (C) 2007 Sven-Ola Tuecke * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "capture.h" #include "util.h" void __attribute__ ((format (printf, 1, 2))) printlog(const char *fmt, ...); #define PCAP_TIMEOUT 200 static unsigned char* pcap_buffer; static size_t pcap_bufsize; static pcap_t *pcap_fp = NULL; static void handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { *((int *)user) = h->len; if (pcap_bufsize < h->len) { printlog("ERROR: Buffer(%d) too small for %d bytes", (int)pcap_bufsize, h->len); *((int *)user) = pcap_bufsize; } memmove(pcap_buffer, bytes, *((int *)user)); } int open_packet_socket(char* devname, __attribute__((unused)) int recv_buffer_size) { char error[PCAP_ERRBUF_SIZE]; int ret; pcap_fp = pcap_create(devname, error); if (pcap_fp == NULL) { fprintf(stderr, "Couldn't create pcap: %s\n", error); return -1; } pcap_set_promisc(pcap_fp, 1); #if defined(__APPLE__) if (pcap_can_set_rfmon(pcap_fp)) pcap_set_rfmon(pcap_fp, 1); else err(1, "Couldn't activate monitor mode"); #endif ret = pcap_activate(pcap_fp); if (ret < 0) { fprintf(stderr, "Can't activate pcap: %d\n", ret); return -1; } return pcap_fileno(pcap_fp); } int device_get_hwinfo(__attribute__((unused)) int fd, __attribute__((unused)) char* ifname, __attribute__((unused)) unsigned char* mac) { if (pcap_fp != NULL) { switch (pcap_datalink(pcap_fp)) { case DLT_IEEE802_11_RADIO: return 803; case DLT_PRISM_HEADER: return 802; default: return 801; } } /* TODO: mac address not implemented */ return -1; } int recv_packet(__attribute__((unused)) int fd, unsigned char* buffer, size_t bufsize) { int ret = 0; pcap_buffer = buffer; pcap_bufsize = bufsize; if (0 == pcap_dispatch(pcap_fp, 1, handler, (u_char *)&ret)) return -1; return ret; } void close_packet_socket(__attribute__((unused)) int fd) { if (pcap_fp != NULL) pcap_close(pcap_fp); } horst-version-5.0/capture.c000066400000000000000000000061341273543766500160350ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "capture.h" #include "util.h" #include "main.h" /* * Get the hardware type of the given interface as ARPHRD_xxx constant. */ int device_get_hwinfo(int fd, char* ifname, unsigned char* mac) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); ifr.ifr_name[IFNAMSIZ - 1] = '\0'; if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) err(1, "Could not get arptype"); DEBUG("ARPTYPE %d\n", ifr.ifr_hwaddr.sa_family); memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); DEBUG("MY MAC %s\n", ether_sprintf(mac)); return ifr.ifr_hwaddr.sa_family; } static void set_receive_buffer(int fd, int sockbufsize) { int ret; /* the maximum allowed value is set by the rmem_max sysctl */ FILE* PF = fopen("/proc/sys/net/core/rmem_max", "w"); fprintf(PF, "%d", sockbufsize); fclose(PF); ret = setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &sockbufsize, sizeof(sockbufsize)); if (ret != 0) err(1, "setsockopt failed"); #if DO_DEBUG socklen_t size = sizeof(sockbufsize); sockbufsize = 0; ret = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &sockbufsize, &size); if (ret != 0) err(1, "getsockopt failed"); DEBUG("socket receive buffer size %d\n", sockbufsize); #endif } int open_packet_socket(char* devname, int recv_buffer_size) { int ret; int mon_fd; int ifindex; struct sockaddr_ll sall; mon_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (mon_fd < 0) { printlog("Could not create packet socket! Please run horst as root!"); exit(1); } /* bind only to one interface */ ifindex = if_nametoindex(devname); memset(&sall, 0, sizeof(struct sockaddr_ll)); sall.sll_ifindex = ifindex; sall.sll_family = AF_PACKET; sall.sll_protocol = htons(ETH_P_ALL); ret = bind(mon_fd, (struct sockaddr*)&sall, sizeof(sall)); if (ret != 0) err(1, "bind failed"); if (recv_buffer_size) set_receive_buffer(mon_fd, recv_buffer_size); return mon_fd; } inline int recv_packet(int fd, unsigned char* buffer, size_t bufsize) { return recv(fd, buffer, bufsize, MSG_DONTWAIT); } void close_packet_socket(int fd) { if (fd > 0) close(fd); } horst-version-5.0/capture.h000066400000000000000000000021751273543766500160430ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _CAPTURE_H_ #define _CAPTURE_H_ #include int open_packet_socket(char* devname, int recv_buffer_size); int recv_packet(int fd, unsigned char* buffer, size_t bufsize); void close_packet_socket(int fd); int device_get_hwinfo(int fd, char* ifname, unsigned char* mac); #endif // _CAPTURE_H_ horst-version-5.0/ccan/000077500000000000000000000000001273543766500151265ustar00rootroot00000000000000horst-version-5.0/ccan/build_assert/000077500000000000000000000000001273543766500176065ustar00rootroot00000000000000horst-version-5.0/ccan/build_assert/LICENSE000077700000000000000000000000001273543766500233312../../licenses/CC0ustar00rootroot00000000000000horst-version-5.0/ccan/build_assert/_info000066400000000000000000000025061273543766500206260ustar00rootroot00000000000000#include "config.h" #include #include /** * build_assert - routines for build-time assertions * * This code provides routines which will cause compilation to fail should some * assertion be untrue: such failures are preferable to run-time assertions, * but much more limited since they can only depends on compile-time constants. * * These assertions are most useful when two parts of the code must be kept in * sync: it is better to avoid such cases if possible, but seconds best is to * detect invalid changes at build time. * * For example, a tricky piece of code might rely on a certain element being at * the start of the structure. To ensure that future changes don't break it, * you would catch such changes in your code like so: * * Example: * #include * #include * * struct foo { * char string[5]; * int x; * }; * * static char *foo_string(struct foo *foo) * { * // This trick requires that the string be first in the structure * BUILD_ASSERT(offsetof(struct foo, string) == 0); * return (char *)foo; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) /* Nothing. */ return 0; return 1; } horst-version-5.0/ccan/build_assert/build_assert.h000066400000000000000000000023141273543766500224370ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_BUILD_ASSERT_H #define CCAN_BUILD_ASSERT_H /** * BUILD_ASSERT - assert a build-time dependency. * @cond: the compile-time condition which must be true. * * Your compile will fail if the condition isn't true, or can't be evaluated * by the compiler. This can only be used within a function. * * Example: * #include * ... * static char *foo_to_char(struct foo *foo) * { * // This code needs string to be at start of foo. * BUILD_ASSERT(offsetof(struct foo, string) == 0); * return (char *)foo; * } */ #define BUILD_ASSERT(cond) \ do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) /** * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression. * @cond: the compile-time condition which must be true. * * Your compile will fail if the condition isn't true, or can't be evaluated * by the compiler. This can be used in an expression: its value is "0". * * Example: * #define foo_to_char(foo) \ * ((char *)(foo) \ * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0)) */ #define BUILD_ASSERT_OR_ZERO(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1) #endif /* CCAN_BUILD_ASSERT_H */ horst-version-5.0/ccan/build_assert/test/000077500000000000000000000000001273543766500205655ustar00rootroot00000000000000horst-version-5.0/ccan/build_assert/test/compile_fail-expr.c000066400000000000000000000002341273543766500243270ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL return BUILD_ASSERT_OR_ZERO(1 == 0); #else return 0; #endif } horst-version-5.0/ccan/build_assert/test/compile_fail.c000066400000000000000000000002071273543766500233530ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL BUILD_ASSERT(1 == 0); #endif return 0; } horst-version-5.0/ccan/build_assert/test/compile_ok.c000066400000000000000000000001641273543766500230530ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { BUILD_ASSERT(1 == 1); return 0; } horst-version-5.0/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c000066400000000000000000000002741273543766500247150ustar00rootroot00000000000000#include #include int main(int argc, char *argv[]) { plan_tests(1); ok1(BUILD_ASSERT_OR_ZERO(1 == 1) == 0); return exit_status(); } horst-version-5.0/ccan/check_type/000077500000000000000000000000001273543766500172445ustar00rootroot00000000000000horst-version-5.0/ccan/check_type/LICENSE000077700000000000000000000000001273543766500227672../../licenses/CC0ustar00rootroot00000000000000horst-version-5.0/ccan/check_type/_info000066400000000000000000000015111273543766500202570ustar00rootroot00000000000000#include "config.h" #include #include /** * check_type - routines for compile time type checking * * C has fairly weak typing: ints get automatically converted to longs, signed * to unsigned, etc. There are some cases where this is best avoided, and * these macros provide methods for evoking warnings (or build errors) when * a precise type isn't used. * * On compilers which don't support typeof() these routines are less effective, * since they have to use sizeof() which can only distiguish between types of * different size. * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { #if !HAVE_TYPEOF printf("ccan/build_assert\n"); #endif return 0; } return 1; } horst-version-5.0/ccan/check_type/check_type.h000066400000000000000000000045051273543766500215370ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_CHECK_TYPE_H #define CCAN_CHECK_TYPE_H #include "config.h" /** * check_type - issue a warning or build failure if type is not correct. * @expr: the expression whose type we should check (not evaluated). * @type: the exact type we expect the expression to be. * * This macro is usually used within other macros to try to ensure that a macro * argument is of the expected type. No type promotion of the expression is * done: an unsigned int is not the same as an int! * * check_type() always evaluates to 0. * * If your compiler does not support typeof, then the best we can do is fail * to compile if the sizes of the types are unequal (a less complete check). * * Example: * // They should always pass a 64-bit value to _set_some_value! * #define set_some_value(expr) \ * _set_some_value((check_type((expr), uint64_t), (expr))) */ /** * check_types_match - issue a warning or build failure if types are not same. * @expr1: the first expression (not evaluated). * @expr2: the second expression (not evaluated). * * This macro is usually used within other macros to try to ensure that * arguments are of identical types. No type promotion of the expressions is * done: an unsigned int is not the same as an int! * * check_types_match() always evaluates to 0. * * If your compiler does not support typeof, then the best we can do is fail * to compile if the sizes of the types are unequal (a less complete check). * * Example: * // Do subtraction to get to enclosing type, but make sure that * // pointer is of correct type for that member. * #define container_of(mbr_ptr, encl_type, mbr) \ * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ * ((encl_type *) \ * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) */ #if HAVE_TYPEOF #define check_type(expr, type) \ ((typeof(expr) *)0 != (type *)0) #define check_types_match(expr1, expr2) \ ((typeof(expr1) *)0 != (typeof(expr2) *)0) #else #include /* Without typeof, we can only test the sizes. */ #define check_type(expr, type) \ BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type)) #define check_types_match(expr1, expr2) \ BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2)) #endif /* HAVE_TYPEOF */ #endif /* CCAN_CHECK_TYPE_H */ horst-version-5.0/ccan/check_type/test/000077500000000000000000000000001273543766500202235ustar00rootroot00000000000000horst-version-5.0/ccan/check_type/test/compile_fail-check_type.c000066400000000000000000000002051273543766500251230ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL check_type(argc, char); #endif return 0; } horst-version-5.0/ccan/check_type/test/compile_fail-check_type_unsigned.c000066400000000000000000000003751273543766500270270ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL #if HAVE_TYPEOF check_type(argc, unsigned int); #else /* This doesn't work without typeof, so just fail */ #error "Fail without typeof" #endif #endif return 0; } horst-version-5.0/ccan/check_type/test/compile_fail-check_types_match.c000066400000000000000000000002421273543766500264630ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { unsigned char x = argc; #ifdef FAIL check_types_match(argc, x); #endif return x; } horst-version-5.0/ccan/check_type/test/run.c000066400000000000000000000010621273543766500211720ustar00rootroot00000000000000#include #include int main(int argc, char *argv[]) { int x = 0, y = 0; plan_tests(9); ok1(check_type(argc, int) == 0); ok1(check_type(&argc, int *) == 0); ok1(check_types_match(argc, argc) == 0); ok1(check_types_match(argc, x) == 0); ok1(check_types_match(&argc, &x) == 0); ok1(check_type(x++, int) == 0); ok(x == 0, "check_type does not evaluate expression"); ok1(check_types_match(x++, y++) == 0); ok(x == 0 && y == 0, "check_types_match does not evaluate expressions"); return exit_status(); } horst-version-5.0/ccan/container_of/000077500000000000000000000000001273543766500175745ustar00rootroot00000000000000horst-version-5.0/ccan/container_of/LICENSE000077700000000000000000000000001273543766500233172../../licenses/CC0ustar00rootroot00000000000000horst-version-5.0/ccan/container_of/_info000066400000000000000000000024741273543766500206200ustar00rootroot00000000000000#include "config.h" #include #include /** * container_of - routine for upcasting * * It is often convenient to create code where the caller registers a pointer * to a generic structure and a callback. The callback might know that the * pointer points to within a larger structure, and container_of gives a * convenient and fairly type-safe way of returning to the enclosing structure. * * This idiom is an alternative to providing a void * pointer for every * callback. * * Example: * #include * #include * * struct timer { * void *members; * }; * * struct info { * int my_stuff; * struct timer timer; * }; * * static void register_timer(struct timer *timer) * { * //... * } * * static void my_timer_callback(struct timer *timer) * { * struct info *info = container_of(timer, struct info, timer); * printf("my_stuff is %u\n", info->my_stuff); * } * * int main(void) * { * struct info info = { .my_stuff = 1 }; * * register_timer(&info.timer); * // ... * return 0; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/check_type\n"); return 0; } return 1; } horst-version-5.0/ccan/container_of/container_of.h000066400000000000000000000061341273543766500224170ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_CONTAINER_OF_H #define CCAN_CONTAINER_OF_H #include #include "config.h" #include /** * container_of - get pointer to enclosing structure * @member_ptr: pointer to the structure member * @containing_type: the type this member is within * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does pointer * subtraction to return the pointer to the enclosing type. * * Example: * struct foo { * int fielda, fieldb; * // ... * }; * struct info { * int some_other_field; * struct foo my_foo; * }; * * static struct info *foo_to_info(struct foo *foo) * { * return container_of(foo, struct info, my_foo); * } */ #define container_of(member_ptr, containing_type, member) \ ((containing_type *) \ ((char *)(member_ptr) \ - container_off(containing_type, member)) \ + check_types_match(*(member_ptr), ((containing_type *)0)->member)) /** * container_off - get offset to enclosing structure * @containing_type: the type this member is within * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does * typechecking and figures out the offset to the enclosing type. * * Example: * struct foo { * int fielda, fieldb; * // ... * }; * struct info { * int some_other_field; * struct foo my_foo; * }; * * static struct info *foo_to_info(struct foo *foo) * { * size_t off = container_off(struct info, my_foo); * return (void *)((char *)foo - off); * } */ #define container_off(containing_type, member) \ offsetof(containing_type, member) /** * container_of_var - get pointer to enclosing structure using a variable * @member_ptr: pointer to the structure member * @container_var: a pointer of same type as this member's container * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does pointer * subtraction to return the pointer to the enclosing type. * * Example: * static struct info *foo_to_i(struct foo *foo) * { * struct info *i = container_of_var(foo, i, my_foo); * return i; * } */ #if HAVE_TYPEOF #define container_of_var(member_ptr, container_var, member) \ container_of(member_ptr, typeof(*container_var), member) #else #define container_of_var(member_ptr, container_var, member) \ ((void *)((char *)(member_ptr) - \ container_off_var(container_var, member))) #endif /** * container_off_var - get offset of a field in enclosing structure * @container_var: a pointer to a container structure * @member: the name of a member within the structure. * * Given (any) pointer to a structure and a its member name, this * macro does pointer subtraction to return offset of member in a * structure memory layout. * */ #if HAVE_TYPEOF #define container_off_var(var, member) \ container_off(typeof(*var), member) #else #define container_off_var(var, member) \ ((const char *)&(var)->member - (const char *)(var)) #endif #endif /* CCAN_CONTAINER_OF_H */ horst-version-5.0/ccan/container_of/test/000077500000000000000000000000001273543766500205535ustar00rootroot00000000000000horst-version-5.0/ccan/container_of/test/compile_fail-bad-type.c000066400000000000000000000005511273543766500250460ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }; int *intp = &foo.a; char *p; #ifdef FAIL /* p is a char *, but this gives a struct foo * */ p = container_of(intp, struct foo, a); #else p = (char *)intp; #endif return p == NULL; } horst-version-5.0/ccan/container_of/test/compile_fail-types.c000066400000000000000000000006321273543766500245050ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }, *foop; int *intp = &foo.a; #ifdef FAIL /* b is a char, but intp is an int * */ foop = container_of(intp, struct foo, b); #else foop = NULL; #endif (void) foop; /* Suppress unused-but-set-variable warning. */ return intp == NULL; } horst-version-5.0/ccan/container_of/test/compile_fail-var-types.c000066400000000000000000000007561273543766500253020ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }, *foop; int *intp = &foo.a; #ifdef FAIL /* b is a char, but intp is an int * */ foop = container_of_var(intp, foop, b); #if !HAVE_TYPEOF #error "Unfortunately we don't fail if we don't have typeof." #endif #else foop = NULL; #endif (void) foop; /* Suppress unused-but-set-variable warning. */ return intp == NULL; } horst-version-5.0/ccan/container_of/test/run.c000066400000000000000000000012271273543766500215250ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }; int *intp = &foo.a; char *charp = &foo.b; plan_tests(8); ok1(container_of(intp, struct foo, a) == &foo); ok1(container_of(charp, struct foo, b) == &foo); ok1(container_of_var(intp, &foo, a) == &foo); ok1(container_of_var(charp, &foo, b) == &foo); ok1(container_off(struct foo, a) == 0); ok1(container_off(struct foo, b) == offsetof(struct foo, b)); ok1(container_off_var(&foo, a) == 0); ok1(container_off_var(&foo, b) == offsetof(struct foo, b)); return exit_status(); } horst-version-5.0/ccan/list/000077500000000000000000000000001273543766500161015ustar00rootroot00000000000000horst-version-5.0/ccan/list/LICENSE000077700000000000000000000000001273543766500223162../../licenses/BSD-MITustar00rootroot00000000000000horst-version-5.0/ccan/list/_info000066400000000000000000000027701273543766500171240ustar00rootroot00000000000000#include "config.h" #include #include /** * list - double linked list routines * * The list header contains routines for manipulating double linked lists. * It defines two types: struct list_head used for anchoring lists, and * struct list_node which is usually embedded in the structure which is placed * in the list. * * Example: * #include * #include * #include * #include * * struct parent { * const char *name; * struct list_head children; * unsigned int num_children; * }; * * struct child { * const char *name; * struct list_node list; * }; * * int main(int argc, char *argv[]) * { * struct parent p; * struct child *c; * unsigned int i; * * if (argc < 2) * errx(1, "Usage: %s parent children...", argv[0]); * * p.name = argv[1]; * list_head_init(&p.children); * p.num_children = 0; * for (i = 2; i < argc; i++) { * c = malloc(sizeof(*c)); * c->name = argv[i]; * list_add(&p.children, &c->list); * p.num_children++; * } * * printf("%s has %u children:", p.name, p.num_children); * list_for_each(&p.children, c, list) * printf("%s ", c->name); * printf("\n"); * return 0; * } * * License: BSD-MIT * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/str\n"); printf("ccan/container_of\n"); printf("ccan/check_type\n"); return 0; } return 1; } horst-version-5.0/ccan/list/list.c000066400000000000000000000017501273543766500172230ustar00rootroot00000000000000/* Licensed under BSD-MIT - see LICENSE file for details */ #include #include #include "list.h" static void *corrupt(const char *abortstr, const struct list_node *head, const struct list_node *node, unsigned int count) { if (abortstr) { fprintf(stderr, "%s: prev corrupt in node %p (%u) of %p\n", abortstr, node, count, head); abort(); } return NULL; } struct list_node *list_check_node(const struct list_node *node, const char *abortstr) { const struct list_node *p, *n; int count = 0; for (p = node, n = node->next; n != node; p = n, n = n->next) { count++; if (n->prev != p) return corrupt(abortstr, node, n, count); } /* Check prev on head node. */ if (node->prev != p) return corrupt(abortstr, node, node, 0); return (struct list_node *)node; } struct list_head *list_check(const struct list_head *h, const char *abortstr) { if (!list_check_node(&h->n, abortstr)) return NULL; return (struct list_head *)h; } horst-version-5.0/ccan/list/list.h000066400000000000000000000451371273543766500172370ustar00rootroot00000000000000/* Licensed under BSD-MIT - see LICENSE file for details */ #ifndef CCAN_LIST_H #define CCAN_LIST_H //#define CCAN_LIST_DEBUG 1 #include #include #include #include #include /** * struct list_node - an entry in a doubly-linked list * @next: next entry (self if empty) * @prev: previous entry (self if empty) * * This is used as an entry in a linked list. * Example: * struct child { * const char *name; * // Linked list of all us children. * struct list_node list; * }; */ struct list_node { struct list_node *next, *prev; }; /** * struct list_head - the head of a doubly-linked list * @h: the list_head (containing next and prev pointers) * * This is used as the head of a linked list. * Example: * struct parent { * const char *name; * struct list_head children; * unsigned int num_children; * }; */ struct list_head { struct list_node n; }; /** * list_check - check head of a list for consistency * @h: the list_head * @abortstr: the location to print on aborting, or NULL. * * Because list_nodes have redundant information, consistency checking between * the back and forward links can be done. This is useful as a debugging check. * If @abortstr is non-NULL, that will be printed in a diagnostic if the list * is inconsistent, and the function will abort. * * Returns the list head if the list is consistent, NULL if not (it * can never return NULL if @abortstr is set). * * See also: list_check_node() * * Example: * static void dump_parent(struct parent *p) * { * struct child *c; * * printf("%s (%u children):\n", p->name, p->num_children); * list_check(&p->children, "bad child list"); * list_for_each(&p->children, c, list) * printf(" -> %s\n", c->name); * } */ struct list_head *list_check(const struct list_head *h, const char *abortstr); /** * list_check_node - check node of a list for consistency * @n: the list_node * @abortstr: the location to print on aborting, or NULL. * * Check consistency of the list node is in (it must be in one). * * See also: list_check() * * Example: * static void dump_child(const struct child *c) * { * list_check_node(&c->list, "bad child list"); * printf("%s\n", c->name); * } */ struct list_node *list_check_node(const struct list_node *n, const char *abortstr); #define LIST_LOC __FILE__ ":" stringify(__LINE__) #ifdef CCAN_LIST_DEBUG #define list_debug(h, loc) list_check((h), loc) #define list_debug_node(n, loc) list_check_node((n), loc) #else #define list_debug(h, loc) (h) #define list_debug_node(n, loc) (n) #endif /** * LIST_HEAD_INIT - initializer for an empty list_head * @name: the name of the list. * * Explicit initializer for an empty list. * * See also: * LIST_HEAD, list_head_init() * * Example: * static struct list_head my_list = LIST_HEAD_INIT(my_list); */ #define LIST_HEAD_INIT(name) { { &name.n, &name.n } } /** * LIST_HEAD - define and initialize an empty list_head * @name: the name of the list. * * The LIST_HEAD macro defines a list_head and initializes it to an empty * list. It can be prepended by "static" to define a static list_head. * * See also: * LIST_HEAD_INIT, list_head_init() * * Example: * static LIST_HEAD(my_global_list); */ #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) /** * list_head_init - initialize a list_head * @h: the list_head to set to the empty list * * Example: * ... * struct parent *parent = malloc(sizeof(*parent)); * * list_head_init(&parent->children); * parent->num_children = 0; */ static inline void list_head_init(struct list_head *h) { h->n.next = h->n.prev = &h->n; } /** * list_add - add an entry at the start of a linked list. * @h: the list_head to add the node to * @n: the list_node to add to the list. * * The list_node does not need to be initialized; it will be overwritten. * Example: * struct child *child = malloc(sizeof(*child)); * * child->name = "marvin"; * list_add(&parent->children, &child->list); * parent->num_children++; */ #define list_add(h, n) list_add_(h, n, LIST_LOC) static inline void list_add_(struct list_head *h, struct list_node *n, __attribute__((unused)) const char *abortstr) { n->next = h->n.next; n->prev = &h->n; h->n.next->prev = n; h->n.next = n; (void)list_debug(h, abortstr); } /** * list_add_tail - add an entry at the end of a linked list. * @h: the list_head to add the node to * @n: the list_node to add to the list. * * The list_node does not need to be initialized; it will be overwritten. * Example: * list_add_tail(&parent->children, &child->list); * parent->num_children++; */ #define list_add_tail(h, n) list_add_tail_(h, n, LIST_LOC) static inline void list_add_tail_(struct list_head *h, struct list_node *n, __attribute__((unused)) const char *abortstr) { n->next = &h->n; n->prev = h->n.prev; h->n.prev->next = n; h->n.prev = n; (void)list_debug(h, abortstr); } /** * list_empty - is a list empty? * @h: the list_head * * If the list is empty, returns true. * * Example: * assert(list_empty(&parent->children) == (parent->num_children == 0)); */ #define list_empty(h) list_empty_(h, LIST_LOC) static inline bool list_empty_(const struct list_head *h, __attribute__((unused)) const char* abortstr) { (void)list_debug(h, abortstr); return h->n.next == &h->n; } /** * list_empty_nodebug - is a list empty (and don't perform debug checks)? * @h: the list_head * * If the list is empty, returns true. * This differs from list_empty() in that if CCAN_LIST_DEBUG is set it * will NOT perform debug checks. Only use this function if you REALLY * know what you're doing. * * Example: * assert(list_empty_nodebug(&parent->children) == (parent->num_children == 0)); */ #ifndef CCAN_LIST_DEBUG #define list_empty_nodebug(h) list_empty(h) #else static inline bool list_empty_nodebug(const struct list_head *h) { return h->n.next == &h->n; } #endif /** * list_del - delete an entry from an (unknown) linked list. * @n: the list_node to delete from the list. * * Note that this leaves @n in an undefined state; it can be added to * another list, but not deleted again. * * See also: * list_del_from() * * Example: * list_del(&child->list); * parent->num_children--; */ #define list_del(n) list_del_(n, LIST_LOC) static inline void list_del_(struct list_node *n, __attribute__((unused)) const char* abortstr) { (void)list_debug_node(n, abortstr); n->next->prev = n->prev; n->prev->next = n->next; #ifdef CCAN_LIST_DEBUG /* Catch use-after-del. */ n->next = n->prev = NULL; #endif } /** * list_del_from - delete an entry from a known linked list. * @h: the list_head the node is in. * @n: the list_node to delete from the list. * * This explicitly indicates which list a node is expected to be in, * which is better documentation and can catch more bugs. * * See also: list_del() * * Example: * list_del_from(&parent->children, &child->list); * parent->num_children--; */ static inline void list_del_from(struct list_head *h, struct list_node *n) { #ifdef CCAN_LIST_DEBUG { /* Thorough check: make sure it was in list! */ struct list_node *i; for (i = h->n.next; i != n; i = i->next) assert(i != &h->n); } #endif /* CCAN_LIST_DEBUG */ /* Quick test that catches a surprising number of bugs. */ assert(!list_empty(h)); list_del(n); } /** * list_entry - convert a list_node back into the structure containing it. * @n: the list_node * @type: the type of the entry * @member: the list_node member of the type * * Example: * // First list entry is children.next; convert back to child. * child = list_entry(parent->children.n.next, struct child, list); * * See Also: * list_top(), list_for_each() */ #define list_entry(n, type, member) container_of(n, type, member) /** * list_top - get the first entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *first; * first = list_top(&parent->children, struct child, list); * if (!first) * printf("Empty list!\n"); */ #define list_top(h, type, member) \ ((type *)list_top_((h), list_off_(type, member))) static inline const void *list_top_(const struct list_head *h, size_t off) { if (list_empty(h)) return NULL; return (const char *)h->n.next - off; } /** * list_pop - remove the first entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *one; * one = list_pop(&parent->children, struct child, list); * if (!one) * printf("Empty list!\n"); */ #define list_pop(h, type, member) \ ((type *)list_pop_((h), list_off_(type, member))) static inline const void *list_pop_(const struct list_head *h, size_t off) { struct list_node *n; if (list_empty(h)) return NULL; n = h->n.next; list_del(n); return (const char *)n - off; } /** * list_tail - get the last entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *last; * last = list_tail(&parent->children, struct child, list); * if (!last) * printf("Empty list!\n"); */ #define list_tail(h, type, member) \ ((type *)list_tail_((h), list_off_(type, member))) static inline const void *list_tail_(const struct list_head *h, size_t off) { if (list_empty(h)) return NULL; return (const char *)h->n.prev - off; } /** * list_for_each - iterate through a list. * @h: the list_head (warning: evaluated multiple times!) * @i: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. * * Example: * list_for_each(&parent->children, child, list) * printf("Name: %s\n", child->name); */ #define list_for_each(h, i, member) \ list_for_each_off(h, i, list_off_var_(i, member)) /** * list_for_each_rev - iterate through a list backwards. * @h: the list_head * @i: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. * * Example: * list_for_each_rev(&parent->children, child, list) * printf("Name: %s\n", child->name); */ #define list_for_each_rev(h, i, member) \ for (i = container_of_var(list_debug(h, LIST_LOC)->n.prev, i, member); \ &i->member != &(h)->n; \ i = container_of_var(i->member.prev, i, member)) /** * list_for_each_safe - iterate through a list, maybe during deletion * @h: the list_head * @i: the structure containing the list_node * @nxt: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. The extra variable * @nxt is used to hold the next element, so you can delete @i from the list. * * Example: * struct child *next; * list_for_each_safe(&parent->children, child, next, list) { * list_del(&child->list); * parent->num_children--; * } */ #define list_for_each_safe(h, i, nxt, member) \ list_for_each_safe_off(h, i, nxt, list_off_var_(i, member)) /** * list_next - get the next entry in a list * @h: the list_head * @i: a pointer to an entry in the list. * @member: the list_node member of the structure * * If @i was the last entry in the list, returns NULL. * * Example: * struct child *second; * second = list_next(&parent->children, first, list); * if (!second) * printf("No second child!\n"); */ #define list_next(h, i, member) \ ((list_typeof(i))list_entry_or_null(list_debug(h, \ __FILE__ ":" stringify(__LINE__)), \ (i)->member.next, \ list_off_var_((i), member))) /** * list_prev - get the previous entry in a list * @h: the list_head * @i: a pointer to an entry in the list. * @member: the list_node member of the structure * * If @i was the first entry in the list, returns NULL. * * Example: * first = list_prev(&parent->children, second, list); * if (!first) * printf("Can't go back to first child?!\n"); */ #define list_prev(h, i, member) \ ((list_typeof(i))list_entry_or_null(list_debug(h, \ __FILE__ ":" stringify(__LINE__)), \ (i)->member.prev, \ list_off_var_((i), member))) /** * list_append_list - empty one list onto the end of another. * @to: the list to append into * @from: the list to empty. * * This takes the entire contents of @from and moves it to the end of * @to. After this @from will be empty. * * Example: * struct list_head adopter; * * list_append_list(&adopter, &parent->children); * assert(list_empty(&parent->children)); * parent->num_children = 0; */ #define list_append_list(t, f) list_append_list_(t, f, \ __FILE__ ":" stringify(__LINE__)) static inline void list_append_list_(struct list_head *to, struct list_head *from, __attribute__((unused)) const char *abortstr) { struct list_node *from_tail = list_debug(from, abortstr)->n.prev; struct list_node *to_tail = list_debug(to, abortstr)->n.prev; /* Sew in head and entire list. */ to->n.prev = from_tail; from_tail->next = &to->n; to_tail->next = &from->n; from->n.prev = to_tail; /* Now remove head. */ list_del(&from->n); list_head_init(from); } /** * list_prepend_list - empty one list into the start of another. * @to: the list to prepend into * @from: the list to empty. * * This takes the entire contents of @from and moves it to the start * of @to. After this @from will be empty. * * Example: * list_prepend_list(&adopter, &parent->children); * assert(list_empty(&parent->children)); * parent->num_children = 0; */ #define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC) static inline void list_prepend_list_(struct list_head *to, struct list_head *from, __attribute__((unused)) const char *abortstr) { struct list_node *from_tail = list_debug(from, abortstr)->n.prev; struct list_node *to_head = list_debug(to, abortstr)->n.next; /* Sew in head and entire list. */ to->n.next = &from->n; from->n.prev = &to->n; to_head->prev = from_tail; from_tail->next = to_head; /* Now remove head. */ list_del(&from->n); list_head_init(from); } /** * list_for_each_off - iterate through a list of memory regions. * @h: the list_head * @i: the pointer to a memory region wich contains list node data. * @off: offset(relative to @i) at which list node data resides. * * This is a low-level wrapper to iterate @i over the entire list, used to * implement all oher, more high-level, for-each constructs. It's a for loop, * so you can break and continue as normal. * * WARNING! Being the low-level macro that it is, this wrapper doesn't know * nor care about the type of @i. The only assumtion made is that @i points * to a chunk of memory that at some @offset, relative to @i, contains a * properly filled `struct node_list' which in turn contains pointers to * memory chunks and it's turtles all the way down. Whith all that in mind * remember that given the wrong pointer/offset couple this macro will * happilly churn all you memory untill SEGFAULT stops it, in other words * caveat emptor. * * It is worth mentioning that one of legitimate use-cases for that wrapper * is operation on opaque types with known offset for `struct list_node' * member(preferably 0), because it allows you not to disclose the type of * @i. * * Example: * list_for_each_off(&parent->children, child, * offsetof(struct child, list)) * printf("Name: %s\n", child->name); */ #define list_for_each_off(h, i, off) \ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next, \ (off)); \ list_node_from_off_((void *)i, (off)) != &(h)->n; \ i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \ (off))) /** * list_for_each_safe_off - iterate through a list of memory regions, maybe * during deletion * @h: the list_head * @i: the pointer to a memory region wich contains list node data. * @nxt: the structure containing the list_node * @off: offset(relative to @i) at which list node data resides. * * For details see `list_for_each_off' and `list_for_each_safe' * descriptions. * * Example: * list_for_each_safe_off(&parent->children, child, * next, offsetof(struct child, list)) * printf("Name: %s\n", child->name); */ #define list_for_each_safe_off(h, i, nxt, off) \ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next, \ (off)), \ nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ (off)); \ list_node_from_off_(i, (off)) != &(h)->n; \ i = nxt, \ nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ (off))) /* Other -off variants. */ #define list_entry_off(n, type, off) \ ((type *)list_node_from_off_((n), (off))) #define list_head_off(h, type, off) \ ((type *)list_head_off((h), (off))) #define list_tail_off(h, type, off) \ ((type *)list_tail_((h), (off))) #define list_add_off(h, n, off) \ list_add((h), list_node_from_off_((n), (off))) #define list_del_off(n, off) \ list_del(list_node_from_off_((n), (off))) #define list_del_from_off(h, n, off) \ list_del_from(h, list_node_from_off_((n), (off))) /* Offset helper functions so we only single-evaluate. */ static inline void *list_node_to_off_(struct list_node *node, size_t off) { return (void *)((char *)node - off); } static inline struct list_node *list_node_from_off_(void *ptr, size_t off) { return (struct list_node *)((char *)ptr + off); } /* Get the offset of the member, but make sure it's a list_node. */ #define list_off_(type, member) \ (container_off(type, member) + \ check_type(((type *)0)->member, struct list_node)) #define list_off_var_(var, member) \ (container_off_var(var, member) + \ check_type(var->member, struct list_node)) #if HAVE_TYPEOF #define list_typeof(var) typeof(var) #else #define list_typeof(var) void * #endif /* Returns member, or NULL if at end of list. */ static inline void *list_entry_or_null(const struct list_head *h, const struct list_node *n, size_t off) { if (n == &h->n) return NULL; return (char *)n - off; } #endif /* CCAN_LIST_H */ horst-version-5.0/ccan/list/test/000077500000000000000000000000001273543766500170605ustar00rootroot00000000000000horst-version-5.0/ccan/list/test/compile_ok-constant.c000066400000000000000000000015641273543766500232020ustar00rootroot00000000000000#include #include #include #include #include struct child { const char *name; struct list_node list; }; static bool children(const struct list_head *list) { return !list_empty(list); } static const struct child *first_child(const struct list_head *list) { return list_top(list, struct child, list); } static const struct child *last_child(const struct list_head *list) { return list_tail(list, struct child, list); } static void check_children(const struct list_head *list) { list_check(list, "bad child list"); } static void print_children(const struct list_head *list) { const struct child *c; list_for_each(list, c, list) printf("%s\n", c->name); } int main(void) { LIST_HEAD(h); children(&h); first_child(&h); last_child(&h); check_children(&h); print_children(&h); return 0; } horst-version-5.0/ccan/list/test/helper.c000066400000000000000000000024261273543766500205070ustar00rootroot00000000000000#include #include #include #include #include "helper.h" #define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \ (42) struct opaque { struct list_node list; size_t secret_offset; char secret_drawer[42]; }; static bool not_randomized = true; struct opaque *create_opaque_blob(void) { struct opaque *blob = calloc(1, sizeof(struct opaque)); if (not_randomized) { srandom((int)time(NULL)); not_randomized = false; } blob->secret_offset = random() % (sizeof(blob->secret_drawer)); blob->secret_drawer[blob->secret_offset] = ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING; return blob; } bool if_blobs_know_the_secret(struct opaque *blob) { bool answer = true; int i; for (i = 0; i < sizeof(blob->secret_drawer) / sizeof(blob->secret_drawer[0]); i++) if (i != blob->secret_offset) answer = answer && (blob->secret_drawer[i] == 0); else answer = answer && (blob->secret_drawer[blob->secret_offset] == ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING); return answer; } void destroy_opaque_blob(struct opaque *blob) { free(blob); } horst-version-5.0/ccan/list/test/helper.h000066400000000000000000000003711273543766500205110ustar00rootroot00000000000000/* These are in a separate C file so we can test undefined structures. */ struct opaque; typedef struct opaque opaque_t; opaque_t *create_opaque_blob(void); bool if_blobs_know_the_secret(opaque_t *blob); void destroy_opaque_blob(opaque_t *blob); horst-version-5.0/ccan/list/test/run-CCAN_LIST_DEBUG.c000066400000000000000000000024441273543766500222770ustar00rootroot00000000000000/* Check that CCAN_LIST_DEBUG works */ #include #include #include #include #include #include /* We don't actually want it to exit... */ static jmp_buf aborted; #define abort() longjmp(aborted, 1) #define fprintf my_fprintf static char printf_buffer[1000]; static int my_fprintf(FILE *stream, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = vsprintf(printf_buffer, format, ap); va_end(ap); return ret; } #define CCAN_LIST_DEBUG 1 #include #include #include int main(int argc, char *argv[]) { struct list_head list; struct list_node n1; char expect[100]; plan_tests(2); /* Empty list. */ list.n.next = &list.n; list.n.prev = &list.n; ok1(list_check(&list, NULL) == &list); /* Bad back ptr */ list.n.prev = &n1; /* Aborting version. */ sprintf(expect, "run-CCAN_LIST_DEBUG.c:50: prev corrupt in node %p (0) of %p\n", &list, &list); if (setjmp(aborted) == 0) { assert(list_empty(&list)); fail("list_empty on empty with bad back ptr didn't fail!"); } else { /* __FILE__ might give full path. */ int prep = strlen(printf_buffer) - strlen(expect); ok1(prep >= 0 && strcmp(printf_buffer + prep, expect) == 0); } return exit_status(); } horst-version-5.0/ccan/list/test/run-check-corrupt.c000066400000000000000000000040111273543766500225730ustar00rootroot00000000000000#include #include #include #include #include #include /* We don't actually want it to exit... */ static jmp_buf aborted; #define abort() longjmp(aborted, 1) #define fprintf my_fprintf static char printf_buffer[1000]; static int my_fprintf(FILE *stream, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = vsprintf(printf_buffer, format, ap); va_end(ap); return ret; } #include #include #include int main(int argc, char *argv[]) { struct list_head list; struct list_node n1; char expect[100]; plan_tests(9); /* Empty list. */ list.n.next = &list.n; list.n.prev = &list.n; ok1(list_check(&list, NULL) == &list); /* Bad back ptr */ list.n.prev = &n1; /* Non-aborting version. */ ok1(list_check(&list, NULL) == NULL); /* Aborting version. */ sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", &list, &list); if (setjmp(aborted) == 0) { list_check(&list, "test message"); fail("list_check on empty with bad back ptr didn't fail!"); } else { ok1(strcmp(printf_buffer, expect) == 0); } /* n1 in list. */ list.n.next = &n1; list.n.prev = &n1; n1.prev = &list.n; n1.next = &list.n; ok1(list_check(&list, NULL) == &list); ok1(list_check_node(&n1, NULL) == &n1); /* Bad back ptr */ n1.prev = &n1; ok1(list_check(&list, NULL) == NULL); ok1(list_check_node(&n1, NULL) == NULL); /* Aborting version. */ sprintf(expect, "test message: prev corrupt in node %p (1) of %p\n", &n1, &list); if (setjmp(aborted) == 0) { list_check(&list, "test message"); fail("list_check on n1 bad back ptr didn't fail!"); } else { ok1(strcmp(printf_buffer, expect) == 0); } sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", &n1, &n1); if (setjmp(aborted) == 0) { list_check_node(&n1, "test message"); fail("list_check_node on n1 bad back ptr didn't fail!"); } else { ok1(strcmp(printf_buffer, expect) == 0); } return exit_status(); } horst-version-5.0/ccan/list/test/run-check-nonconst.c000066400000000000000000000012101273543766500227340ustar00rootroot00000000000000#include #include #include #include "helper.h" struct child { const char *name; struct list_node list; }; int main(int argc, char *argv[]) { struct child c1, c2; struct list_head list = LIST_HEAD_INIT(list); plan_tests(1); list_add(&list, &c1.list); list_add_tail(list_check(&list, "Bad list!"), &c2.list); list_del_from(list_check(&list, "Bad list!"), list_check_node(&c2.list, "Bad node!")); list_del_from(list_check(&list, "Bad list!"), list_check_node(&c1.list, "Bad node!")); ok1(list_empty(list_check(&list, "Bad emptied list"))); return exit_status(); } horst-version-5.0/ccan/list/test/run-list_del_from-assert.c000066400000000000000000000013011273543766500241420ustar00rootroot00000000000000#define CCAN_LIST_DEBUG 1 #include #include #include #include #include #include #include int main(int argc, char *argv[]) { struct list_head list1, list2; struct list_node n1, n2, n3; pid_t child; int status; plan_tests(1); list_head_init(&list1); list_head_init(&list2); list_add(&list1, &n1); list_add(&list2, &n2); list_add_tail(&list2, &n3); child = fork(); if (child) { wait(&status); } else { /* This should abort. */ list_del_from(&list1, &n3); exit(0); } ok1(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT); list_del_from(&list2, &n3); return exit_status(); } horst-version-5.0/ccan/list/test/run-list_prev-list_next.c000066400000000000000000000033451273543766500240510ustar00rootroot00000000000000#include #include #include #include "helper.h" struct parent { const char *name; unsigned int num_children; struct list_head children; }; struct child { const char *name; struct list_node list; }; int main(int argc, char *argv[]) { struct parent parent; struct child c1, c2, c3; const struct parent *p; const struct child *c; plan_tests(20); parent.num_children = 0; list_head_init(&parent.children); c1.name = "c1"; list_add(&parent.children, &c1.list); ok1(list_next(&parent.children, &c1, list) == NULL); ok1(list_prev(&parent.children, &c1, list) == NULL); c2.name = "c2"; list_add_tail(&parent.children, &c2.list); ok1(list_next(&parent.children, &c1, list) == &c2); ok1(list_prev(&parent.children, &c1, list) == NULL); ok1(list_next(&parent.children, &c2, list) == NULL); ok1(list_prev(&parent.children, &c2, list) == &c1); c3.name = "c3"; list_add_tail(&parent.children, &c3.list); ok1(list_next(&parent.children, &c1, list) == &c2); ok1(list_prev(&parent.children, &c1, list) == NULL); ok1(list_next(&parent.children, &c2, list) == &c3); ok1(list_prev(&parent.children, &c2, list) == &c1); ok1(list_next(&parent.children, &c3, list) == NULL); ok1(list_prev(&parent.children, &c3, list) == &c2); /* Const variants */ p = &parent; c = &c2; ok1(list_next(&p->children, &c1, list) == &c2); ok1(list_prev(&p->children, &c1, list) == NULL); ok1(list_next(&p->children, c, list) == &c3); ok1(list_prev(&p->children, c, list) == &c1); ok1(list_next(&parent.children, c, list) == &c3); ok1(list_prev(&parent.children, c, list) == &c1); ok1(list_next(&p->children, &c3, list) == NULL); ok1(list_prev(&p->children, &c3, list) == &c2); return exit_status(); } horst-version-5.0/ccan/list/test/run-prepend_list.c000066400000000000000000000053761273543766500225310ustar00rootroot00000000000000#include #include #include #include static bool list_expect(struct list_head *h, ...) { va_list ap; struct list_node *n = &h->n, *expected; va_start(ap, h); while ((expected = va_arg(ap, struct list_node *)) != NULL) { n = n->next; if (n != expected) return false; } return (n->next == &h->n); } int main(int argc, char *argv[]) { struct list_head h1, h2; struct list_node n[4]; plan_tests(40); list_head_init(&h1); list_head_init(&h2); /* Append an empty list to an empty list. */ list_append_list(&h1, &h2); ok1(list_empty(&h1)); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); /* Prepend an empty list to an empty list. */ list_prepend_list(&h1, &h2); ok1(list_empty(&h1)); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); /* Append an empty list to a non-empty list */ list_add(&h1, &n[0]); list_append_list(&h1, &h2); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h1, &n[0], NULL)); /* Prepend an empty list to a non-empty list */ list_prepend_list(&h1, &h2); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h1, &n[0], NULL)); /* Append a non-empty list to an empty list. */ list_append_list(&h2, &h1); ok1(list_empty(&h1)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h2, &n[0], NULL)); /* Prepend a non-empty list to an empty list. */ list_prepend_list(&h1, &h2); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h1, &n[0], NULL)); /* Prepend a non-empty list to non-empty list. */ list_add(&h2, &n[1]); list_prepend_list(&h1, &h2); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h1, &n[1], &n[0], NULL)); /* Append a non-empty list to non-empty list. */ list_add(&h2, &n[2]); list_append_list(&h1, &h2); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h1, &n[1], &n[0], &n[2], NULL)); /* Prepend a 2-entry list to a 2-entry list. */ list_del_from(&h1, &n[2]); list_add(&h2, &n[2]); list_add_tail(&h2, &n[3]); list_prepend_list(&h1, &h2); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h1, &n[2], &n[3], &n[1], &n[0], NULL)); /* Append a 2-entry list to a 2-entry list. */ list_del_from(&h1, &n[2]); list_del_from(&h1, &n[3]); list_add(&h2, &n[2]); list_add_tail(&h2, &n[3]); list_append_list(&h1, &h2); ok1(list_empty(&h2)); ok1(list_check(&h1, NULL)); ok1(list_check(&h2, NULL)); ok1(list_expect(&h1, &n[1], &n[0], &n[2], &n[3], NULL)); return exit_status(); } horst-version-5.0/ccan/list/test/run-single-eval.c000066400000000000000000000111511273543766500222330ustar00rootroot00000000000000/* Make sure macros only evaluate their args once. */ #include #include #include struct parent { const char *name; struct list_head children; unsigned int num_children; int eval_count; }; struct child { const char *name; struct list_node list; }; static LIST_HEAD(static_list); #define ref(obj, counter) ((counter)++, (obj)) int main(int argc, char *argv[]) { struct parent parent; struct child c1, c2, c3, *c, *n; unsigned int i; unsigned int static_count = 0, parent_count = 0, list_count = 0, node_count = 0; struct list_head list = LIST_HEAD_INIT(list); plan_tests(74); /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ ok1(list_empty(ref(&static_list, static_count))); ok1(static_count == 1); ok1(list_check(ref(&static_list, static_count), NULL)); ok1(static_count == 2); ok1(list_empty(ref(&list, list_count))); ok1(list_count == 1); ok1(list_check(ref(&list, list_count), NULL)); ok1(list_count == 2); parent.num_children = 0; list_head_init(ref(&parent.children, parent_count)); ok1(parent_count == 1); /* Test list_head_init */ ok1(list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 2); ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 3); c2.name = "c2"; list_add(ref(&parent.children, parent_count), &c2.list); ok1(parent_count == 4); /* Test list_add and !list_empty. */ ok1(!list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 5); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &parent.children.n); ok1(parent.children.n.next == &c2.list); ok1(parent.children.n.prev == &c2.list); /* Test list_check */ ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 6); c1.name = "c1"; list_add(ref(&parent.children, parent_count), &c1.list); ok1(parent_count == 7); /* Test list_add and !list_empty. */ ok1(!list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 8); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &c1.list); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c2.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); /* Test list_check */ ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 9); c3.name = "c3"; list_add_tail(ref(&parent.children, parent_count), &c3.list); ok1(parent_count == 10); /* Test list_add_tail and !list_empty. */ ok1(!list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 11); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c3.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); ok1(c2.list.next == &c3.list); ok1(c2.list.prev == &c1.list); ok1(c3.list.next == &parent.children.n); ok1(c3.list.prev == &c2.list); /* Test list_check */ ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 12); /* Test list_check_node */ ok1(list_check_node(&c1.list, NULL)); ok1(list_check_node(&c2.list, NULL)); ok1(list_check_node(&c3.list, NULL)); /* Test list_top */ ok1(list_top(ref(&parent.children, parent_count), struct child, list) == &c1); ok1(parent_count == 13); /* Test list_tail */ ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == &c3); ok1(parent_count == 14); /* Test list_for_each. */ i = 0; list_for_each(&parent.children, c, list) { switch (i++) { case 0: ok1(c == &c1); break; case 1: ok1(c == &c2); break; case 2: ok1(c == &c3); break; } if (i > 2) break; } ok1(i == 3); /* Test list_for_each_safe, list_del and list_del_from. */ i = 0; list_for_each_safe(&parent.children, c, n, list) { switch (i++) { case 0: ok1(c == &c1); list_del(ref(&c->list, node_count)); ok1(node_count == 1); break; case 1: ok1(c == &c2); list_del_from(ref(&parent.children, parent_count), ref(&c->list, node_count)); ok1(node_count == 2); break; case 2: ok1(c == &c3); list_del_from(ref(&parent.children, parent_count), ref(&c->list, node_count)); ok1(node_count == 3); break; } ok1(list_check(ref(&parent.children, parent_count), NULL)); if (i > 2) break; } ok1(i == 3); ok1(parent_count == 19); ok1(list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 20); /* Test list_top/list_tail on empty list. */ ok1(list_top(ref(&parent.children, parent_count), struct child, list) == NULL); ok1(parent_count == 21); ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == NULL); ok1(parent_count == 22); return exit_status(); } horst-version-5.0/ccan/list/test/run-with-debug.c000066400000000000000000000001641273543766500220660ustar00rootroot00000000000000/* Just like run.c, but with all debug checks enabled. */ #define CCAN_LIST_DEBUG 1 #include horst-version-5.0/ccan/list/test/run.c000066400000000000000000000115431273543766500200340ustar00rootroot00000000000000#include #include #include #include "helper.h" struct parent { const char *name; struct list_head children; unsigned int num_children; }; struct child { const char *name; struct list_node list; }; static LIST_HEAD(static_list); int main(int argc, char *argv[]) { struct parent parent; struct child c1, c2, c3, *c, *n; unsigned int i; struct list_head list = LIST_HEAD_INIT(list); opaque_t *q, *nq; struct list_head opaque_list = LIST_HEAD_INIT(opaque_list); plan_tests(68); /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ ok1(list_empty(&static_list)); ok1(list_check(&static_list, NULL)); ok1(list_empty(&list)); ok1(list_check(&list, NULL)); parent.num_children = 0; list_head_init(&parent.children); /* Test list_head_init */ ok1(list_empty(&parent.children)); ok1(list_check(&parent.children, NULL)); c2.name = "c2"; list_add(&parent.children, &c2.list); /* Test list_add and !list_empty. */ ok1(!list_empty(&parent.children)); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &parent.children.n); ok1(parent.children.n.next == &c2.list); ok1(parent.children.n.prev == &c2.list); /* Test list_check */ ok1(list_check(&parent.children, NULL)); c1.name = "c1"; list_add(&parent.children, &c1.list); /* Test list_add and !list_empty. */ ok1(!list_empty(&parent.children)); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &c1.list); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c2.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); /* Test list_check */ ok1(list_check(&parent.children, NULL)); c3.name = "c3"; list_add_tail(&parent.children, &c3.list); /* Test list_add_tail and !list_empty. */ ok1(!list_empty(&parent.children)); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c3.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); ok1(c2.list.next == &c3.list); ok1(c2.list.prev == &c1.list); ok1(c3.list.next == &parent.children.n); ok1(c3.list.prev == &c2.list); /* Test list_check */ ok1(list_check(&parent.children, NULL)); /* Test list_check_node */ ok1(list_check_node(&c1.list, NULL)); ok1(list_check_node(&c2.list, NULL)); ok1(list_check_node(&c3.list, NULL)); /* Test list_top */ ok1(list_top(&parent.children, struct child, list) == &c1); /* Test list_pop */ ok1(list_pop(&parent.children, struct child, list) == &c1); ok1(list_top(&parent.children, struct child, list) == &c2); list_add(&parent.children, &c1.list); /* Test list_tail */ ok1(list_tail(&parent.children, struct child, list) == &c3); /* Test list_for_each. */ i = 0; list_for_each(&parent.children, c, list) { switch (i++) { case 0: ok1(c == &c1); break; case 1: ok1(c == &c2); break; case 2: ok1(c == &c3); break; } if (i > 2) break; } ok1(i == 3); /* Test list_for_each_rev. */ i = 0; list_for_each_rev(&parent.children, c, list) { switch (i++) { case 0: ok1(c == &c3); break; case 1: ok1(c == &c2); break; case 2: ok1(c == &c1); break; } if (i > 2) break; } ok1(i == 3); /* Test list_for_each_safe, list_del and list_del_from. */ i = 0; list_for_each_safe(&parent.children, c, n, list) { switch (i++) { case 0: ok1(c == &c1); list_del(&c->list); break; case 1: ok1(c == &c2); list_del_from(&parent.children, &c->list); break; case 2: ok1(c == &c3); list_del_from(&parent.children, &c->list); break; } ok1(list_check(&parent.children, NULL)); if (i > 2) break; } ok1(i == 3); ok1(list_empty(&parent.children)); /* Test list_for_each_off. */ list_add_tail(&opaque_list, (struct list_node *)create_opaque_blob()); list_add_tail(&opaque_list, (struct list_node *)create_opaque_blob()); list_add_tail(&opaque_list, (struct list_node *)create_opaque_blob()); i = 0; list_for_each_off(&opaque_list, q, 0) { i++; ok1(if_blobs_know_the_secret(q)); } ok1(i == 3); /* Test list_for_each_safe_off, list_del_off and list_del_from_off. */ i = 0; list_for_each_safe_off(&opaque_list, q, nq, 0) { switch (i++) { case 0: ok1(if_blobs_know_the_secret(q)); list_del_off(q, 0); destroy_opaque_blob(q); break; case 1: ok1(if_blobs_know_the_secret(q)); list_del_from_off(&opaque_list, q, 0); destroy_opaque_blob(q); break; case 2: ok1(c == &c3); list_del_from_off(&opaque_list, q, 0); destroy_opaque_blob(q); break; } ok1(list_check(&opaque_list, NULL)); if (i > 2) break; } ok1(i == 3); ok1(list_empty(&opaque_list)); /* Test list_top/list_tail/list_pop on empty list. */ ok1(list_top(&parent.children, struct child, list) == NULL); ok1(list_tail(&parent.children, struct child, list) == NULL); ok1(list_pop(&parent.children, struct child, list) == NULL); return exit_status(); } horst-version-5.0/ccan/str/000077500000000000000000000000001273543766500157365ustar00rootroot00000000000000horst-version-5.0/ccan/str/LICENSE000077700000000000000000000000001273543766500214612../../licenses/CC0ustar00rootroot00000000000000horst-version-5.0/ccan/str/_info000066400000000000000000000025101273543766500167510ustar00rootroot00000000000000#include "config.h" #include #include /** * str - string helper routines * * This is a grab bag of functions for string operations, designed to enhance * the standard string.h. * * Note that if you define CCAN_STR_DEBUG, you will get extra compile * checks on common misuses of the following functions (they will now * be out-of-line, so there is a runtime penalty!). * * strstr, strchr, strrchr: * Return const char * if first argument is const (gcc only). * * isalnum, isalpha, isascii, isblank, iscntrl, isdigit, isgraph, * islower, isprint, ispunct, isspace, isupper, isxdigit: * Static and runtime check that input is EOF or an *unsigned* * char, as per C standard (really!). * * Example: * #include * #include * * int main(int argc, char *argv[]) * { * if (argv[1] && streq(argv[1], "--verbose")) * printf("verbose set\n"); * if (argv[1] && strstarts(argv[1], "--")) * printf("Some option set\n"); * if (argv[1] && strends(argv[1], "cow-powers")) * printf("Magic option set\n"); * return 0; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/build_assert\n"); return 0; } return 1; } horst-version-5.0/ccan/str/debug.c000066400000000000000000000031021273543766500171640ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #include "config.h" #include #include #include #include #ifdef CCAN_STR_DEBUG /* Because we mug the real ones with macros, we need our own wrappers. */ int str_isalnum(int i) { assert(i >= -1 && i < 256); return isalnum(i); } int str_isalpha(int i) { assert(i >= -1 && i < 256); return isalpha(i); } int str_isascii(int i) { assert(i >= -1 && i < 256); return isascii(i); } #if HAVE_ISBLANK int str_isblank(int i) { assert(i >= -1 && i < 256); return isblank(i); } #endif int str_iscntrl(int i) { assert(i >= -1 && i < 256); return iscntrl(i); } int str_isdigit(int i) { assert(i >= -1 && i < 256); return isdigit(i); } int str_isgraph(int i) { assert(i >= -1 && i < 256); return isgraph(i); } int str_islower(int i) { assert(i >= -1 && i < 256); return islower(i); } int str_isprint(int i) { assert(i >= -1 && i < 256); return isprint(i); } int str_ispunct(int i) { assert(i >= -1 && i < 256); return ispunct(i); } int str_isspace(int i) { assert(i >= -1 && i < 256); return isspace(i); } int str_isupper(int i) { assert(i >= -1 && i < 256); return isupper(i); } int str_isxdigit(int i) { assert(i >= -1 && i < 256); return isxdigit(i); } #undef strstr #undef strchr #undef strrchr char *str_strstr(const char *haystack, const char *needle) { return strstr(haystack, needle); } char *str_strchr(const char *haystack, int c) { return strchr(haystack, c); } char *str_strrchr(const char *haystack, int c) { return strrchr(haystack, c); } #endif horst-version-5.0/ccan/str/str.c000066400000000000000000000004331273543766500167120ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #include size_t strcount(const char *haystack, const char *needle) { size_t i = 0, nlen = strlen(needle); while ((haystack = strstr(haystack, needle)) != NULL) { i++; haystack += nlen; } return i; } horst-version-5.0/ccan/str/str.h000066400000000000000000000135031273543766500167210ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_STR_H #define CCAN_STR_H #include "config.h" #include #include #include #include /** * streq - Are two strings equal? * @a: first string * @b: first string * * This macro is arguably more readable than "!strcmp(a, b)". * * Example: * if (streq(somestring, "")) * printf("String is empty!\n"); */ #define streq(a,b) (strcmp((a),(b)) == 0) /** * strstarts - Does this string start with this prefix? * @str: string to test * @prefix: prefix to look for at start of str * * Example: * if (strstarts(somestring, "foo")) * printf("String %s begins with 'foo'!\n", somestring); */ #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) /** * strends - Does this string end with this postfix? * @str: string to test * @postfix: postfix to look for at end of str * * Example: * if (strends(somestring, "foo")) * printf("String %s end with 'foo'!\n", somestring); */ static inline bool strends(const char *str, const char *postfix) { if (strlen(str) < strlen(postfix)) return false; return streq(str + strlen(str) - strlen(postfix), postfix); } /** * stringify - Turn expression into a string literal * @expr: any C expression * * Example: * #define PRINT_COND_IF_FALSE(cond) \ * ((cond) || printf("%s is false!", stringify(cond))) */ #define stringify(expr) stringify_1(expr) /* Double-indirection required to stringify expansions */ #define stringify_1(expr) #expr /** * strcount - Count number of (non-overlapping) occurrences of a substring. * @haystack: a C string * @needle: a substring * * Example: * assert(strcount("aaa aaa", "a") == 6); * assert(strcount("aaa aaa", "ab") == 0); * assert(strcount("aaa aaa", "aa") == 2); */ size_t strcount(const char *haystack, const char *needle); /** * STR_MAX_CHARS - Maximum possible size of numeric string for this type. * @type_or_expr: a pointer or integer type or expression. * * This provides enough space for a nul-terminated string which represents the * largest possible value for the type or expression. * * Note: The implementation adds extra space so hex values or negative * values will fit (eg. sprintf(... "%p"). ) * * Example: * char str[STR_MAX_CHARS(int)]; * * sprintf(str, "%i", 7); */ #define STR_MAX_CHARS(type_or_expr) \ ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \ + STR_MAX_CHARS_TCHECK_(type_or_expr)) #if HAVE_TYPEOF /* Only a simple type can have 0 assigned, so test that. */ #define STR_MAX_CHARS_TCHECK_(type_or_expr) \ ({ typeof(type_or_expr) x = 0; (void)x; 0; }) #else #define STR_MAX_CHARS_TCHECK_(type_or_expr) 0 #endif /** * cisalnum - isalnum() which takes a char (and doesn't accept EOF) * @c: a character * * Surprisingly, the standard ctype.h isalnum() takes an int, which * must have the value of EOF (-1) or an unsigned char. This variant * takes a real char, and doesn't accept EOF. */ static inline bool cisalnum(char c) { return isalnum((unsigned char)c); } static inline bool cisalpha(char c) { return isalpha((unsigned char)c); } static inline bool cisascii(char c) { return isascii((unsigned char)c); } #if HAVE_ISBLANK static inline bool cisblank(char c) { return isblank((unsigned char)c); } #endif static inline bool ciscntrl(char c) { return iscntrl((unsigned char)c); } static inline bool cisdigit(char c) { return isdigit((unsigned char)c); } static inline bool cisgraph(char c) { return isgraph((unsigned char)c); } static inline bool cislower(char c) { return islower((unsigned char)c); } static inline bool cisprint(char c) { return isprint((unsigned char)c); } static inline bool cispunct(char c) { return ispunct((unsigned char)c); } static inline bool cisspace(char c) { return isspace((unsigned char)c); } static inline bool cisupper(char c) { return isupper((unsigned char)c); } static inline bool cisxdigit(char c) { return isxdigit((unsigned char)c); } #include /* These checks force things out of line, hence they are under DEBUG. */ #ifdef CCAN_STR_DEBUG #include /* These are commonly misused: they take -1 or an *unsigned* char value. */ #undef isalnum #undef isalpha #undef isascii #undef isblank #undef iscntrl #undef isdigit #undef isgraph #undef islower #undef isprint #undef ispunct #undef isspace #undef isupper #undef isxdigit /* You can use a char if char is unsigned. */ #if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF #define str_check_arg_(i) \ ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \ char) \ || (char)255 > 0)) #else #define str_check_arg_(i) (i) #endif #define isalnum(i) str_isalnum(str_check_arg_(i)) #define isalpha(i) str_isalpha(str_check_arg_(i)) #define isascii(i) str_isascii(str_check_arg_(i)) #if HAVE_ISBLANK #define isblank(i) str_isblank(str_check_arg_(i)) #endif #define iscntrl(i) str_iscntrl(str_check_arg_(i)) #define isdigit(i) str_isdigit(str_check_arg_(i)) #define isgraph(i) str_isgraph(str_check_arg_(i)) #define islower(i) str_islower(str_check_arg_(i)) #define isprint(i) str_isprint(str_check_arg_(i)) #define ispunct(i) str_ispunct(str_check_arg_(i)) #define isspace(i) str_isspace(str_check_arg_(i)) #define isupper(i) str_isupper(str_check_arg_(i)) #define isxdigit(i) str_isxdigit(str_check_arg_(i)) #if HAVE_TYPEOF /* With GNU magic, we can make const-respecting standard string functions. */ #undef strstr #undef strchr #undef strrchr /* + 0 is needed to decay array into pointer. */ #define strstr(haystack, needle) \ ((typeof((haystack) + 0))str_strstr((haystack), (needle))) #define strchr(haystack, c) \ ((typeof((haystack) + 0))str_strchr((haystack), (c))) #define strrchr(haystack, c) \ ((typeof((haystack) + 0))str_strrchr((haystack), (c))) #endif #endif /* CCAN_STR_DEBUG */ #endif /* CCAN_STR_H */ horst-version-5.0/ccan/str/str_debug.h000066400000000000000000000014061273543766500200660ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_STR_DEBUG_H #define CCAN_STR_DEBUG_H /* #define CCAN_STR_DEBUG 1 */ #ifdef CCAN_STR_DEBUG /* Because we mug the real ones with macros, we need our own wrappers. */ int str_isalnum(int i); int str_isalpha(int i); int str_isascii(int i); #if HAVE_ISBLANK int str_isblank(int i); #endif int str_iscntrl(int i); int str_isdigit(int i); int str_isgraph(int i); int str_islower(int i); int str_isprint(int i); int str_ispunct(int i); int str_isspace(int i); int str_isupper(int i); int str_isxdigit(int i); char *str_strstr(const char *haystack, const char *needle); char *str_strchr(const char *s, int c); char *str_strrchr(const char *s, int c); #endif /* CCAN_STR_DEBUG */ #endif /* CCAN_STR_DEBUG_H */ horst-version-5.0/ccan/str/test/000077500000000000000000000000001273543766500167155ustar00rootroot00000000000000horst-version-5.0/ccan/str/test/compile_fail-STR_MAX_CHARS.c000066400000000000000000000004561273543766500236040ustar00rootroot00000000000000#include struct s { int val; }; int main(int argc, char *argv[]) { struct s #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check STR_MAX_CHARS. #endif #else /* A pointer is OK. */ * #endif val; char str[STR_MAX_CHARS(val)]; str[0] = '\0'; return str[0] ? 0 : 1; } horst-version-5.0/ccan/str/test/compile_fail-isalnum.c000066400000000000000000000005611273543766500231540ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isalnum. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isalnum(c); } horst-version-5.0/ccan/str/test/compile_fail-isalpha.c000066400000000000000000000005611273543766500231250ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isalpha. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isalpha(c); } horst-version-5.0/ccan/str/test/compile_fail-isascii.c000066400000000000000000000005611273543766500231300ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isascii. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isascii(c); } horst-version-5.0/ccan/str/test/compile_fail-isblank.c000066400000000000000000000006531273543766500231310ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF || !HAVE_ISBLANK #error We need typeof to check isblank. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif #if HAVE_ISBLANK return isblank(c); #else return c; #endif } horst-version-5.0/ccan/str/test/compile_fail-iscntrl.c000066400000000000000000000005611273543766500231620ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check iscntrl. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return iscntrl(c); } horst-version-5.0/ccan/str/test/compile_fail-isdigit.c000066400000000000000000000005611273543766500231400ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isdigit. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isdigit(c); } horst-version-5.0/ccan/str/test/compile_fail-islower.c000066400000000000000000000005611273543766500231700ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check islower. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return islower(c); } horst-version-5.0/ccan/str/test/compile_fail-isprint.c000066400000000000000000000005611273543766500231740ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isprint. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isprint(c); } horst-version-5.0/ccan/str/test/compile_fail-ispunct.c000066400000000000000000000005611273543766500231710ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check ispunct. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return ispunct(c); } horst-version-5.0/ccan/str/test/compile_fail-isspace.c000066400000000000000000000005611273543766500231330ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isspace. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isspace(c); } horst-version-5.0/ccan/str/test/compile_fail-isupper.c000066400000000000000000000005611273543766500231730ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isupper. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isupper(c); } horst-version-5.0/ccan/str/test/compile_fail-isxdigit.c000066400000000000000000000005631273543766500233320ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isxdigit. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isxdigit(c); } horst-version-5.0/ccan/str/test/compile_fail-strchr.c000066400000000000000000000004211273543766500230040ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check strstr. #endif #else const #endif char *ret; const char *str = "hello"; ret = strchr(str, 'l'); return ret ? 0 : 1; } horst-version-5.0/ccan/str/test/compile_fail-strrchr.c000066400000000000000000000004221273543766500231670ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check strstr. #endif #else const #endif char *ret; const char *str = "hello"; ret = strrchr(str, 'l'); return ret ? 0 : 1; } horst-version-5.0/ccan/str/test/compile_fail-strstr.c000066400000000000000000000004241273543766500230430ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check strstr. #endif #else const #endif char *ret; const char *str = "hello"; ret = strstr(str, "hell"); return ret ? 0 : 1; } horst-version-5.0/ccan/str/test/debug.c000066400000000000000000000003241273543766500201460ustar00rootroot00000000000000/* We can't use the normal "#include the .c file" trick, since this is contaminated by str.h's macro overrides. So we put it in all tests like this. */ #define CCAN_STR_DEBUG 1 #include horst-version-5.0/ccan/str/test/run-STR_MAX_CHARS.c000066400000000000000000000032661273543766500217670ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char *argv[]) { char str[1000]; struct { uint8_t u1byte; int8_t s1byte; uint16_t u2byte; int16_t s2byte; uint32_t u4byte; int32_t s4byte; uint64_t u8byte; int64_t s8byte; void *ptr; } types; plan_tests(13); memset(&types, 0xFF, sizeof(types)); /* Hex versions */ sprintf(str, "0x%llx", (unsigned long long)types.u1byte); ok1(strlen(str) < STR_MAX_CHARS(types.u1byte)); sprintf(str, "0x%llx", (unsigned long long)types.u2byte); ok1(strlen(str) < STR_MAX_CHARS(types.u2byte)); sprintf(str, "0x%llx", (unsigned long long)types.u4byte); ok1(strlen(str) < STR_MAX_CHARS(types.u4byte)); sprintf(str, "0x%llx", (unsigned long long)types.u8byte); ok1(strlen(str) < STR_MAX_CHARS(types.u8byte)); /* Decimal versions */ sprintf(str, "%u", types.u1byte); ok1(strlen(str) < STR_MAX_CHARS(types.u1byte)); sprintf(str, "%d", types.s1byte); ok1(strlen(str) < STR_MAX_CHARS(types.s1byte)); sprintf(str, "%u", types.u2byte); ok1(strlen(str) < STR_MAX_CHARS(types.u2byte)); sprintf(str, "%d", types.s2byte); ok1(strlen(str) < STR_MAX_CHARS(types.s2byte)); sprintf(str, "%u", types.u4byte); ok1(strlen(str) < STR_MAX_CHARS(types.u4byte)); sprintf(str, "%d", types.s4byte); ok1(strlen(str) < STR_MAX_CHARS(types.s4byte)); sprintf(str, "%llu", (unsigned long long)types.u8byte); ok1(strlen(str) < STR_MAX_CHARS(types.u8byte)); sprintf(str, "%lld", (long long)types.s8byte); ok1(strlen(str) < STR_MAX_CHARS(types.s8byte)); /* Pointer version. */ sprintf(str, "%p", types.ptr); ok1(strlen(str) < STR_MAX_CHARS(types.ptr)); return exit_status(); } horst-version-5.0/ccan/str/test/run.c000066400000000000000000000051321273543766500176660ustar00rootroot00000000000000#include #include #include #include #include #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) static const char *substrings[] = { "far", "bar", "baz", "b", "ba", "z", "ar", NULL }; #define NUM_SUBSTRINGS (ARRAY_SIZE(substrings) - 1) static char *strdup_rev(const char *s) { char *ret = strdup(s); unsigned int i; for (i = 0; i < strlen(s); i++) ret[i] = s[strlen(s) - i - 1]; return ret; } int main(int argc, char *argv[]) { unsigned int i, j, n; char *strings[NUM_SUBSTRINGS * NUM_SUBSTRINGS]; n = 0; for (i = 0; i < NUM_SUBSTRINGS; i++) { for (j = 0; j < NUM_SUBSTRINGS; j++) { strings[n] = malloc(strlen(substrings[i]) + strlen(substrings[j]) + 1); sprintf(strings[n++], "%s%s", substrings[i], substrings[j]); } } plan_tests(n * n * 5 + 16); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { unsigned int k, identical = 0; char *reva, *revb; /* Find first difference. */ for (k = 0; strings[i][k]==strings[j][k]; k++) { if (k == strlen(strings[i])) { identical = 1; break; } } if (identical) ok1(streq(strings[i], strings[j])); else ok1(!streq(strings[i], strings[j])); /* Postfix test should be equivalent to prefix * test on reversed string. */ reva = strdup_rev(strings[i]); revb = strdup_rev(strings[j]); if (!strings[i][k]) { ok1(strstarts(strings[j], strings[i])); ok1(strends(revb, reva)); } else { ok1(!strstarts(strings[j], strings[i])); ok1(!strends(revb, reva)); } if (!strings[j][k]) { ok1(strstarts(strings[i], strings[j])); ok1(strends(reva, revb)); } else { ok1(!strstarts(strings[i], strings[j])); ok1(!strends(reva, revb)); } free(reva); free(revb); } } for (i = 0; i < n; i++) free(strings[i]); ok1(streq(stringify(NUM_SUBSTRINGS), "((sizeof(substrings) / sizeof(substrings[0])) - 1)")); ok1(streq(stringify(ARRAY_SIZE(substrings)), "(sizeof(substrings) / sizeof(substrings[0]))")); ok1(streq(stringify(i == 0), "i == 0")); ok1(strcount("aaaaaa", "b") == 0); ok1(strcount("aaaaaa", "a") == 6); ok1(strcount("aaaaaa", "aa") == 3); ok1(strcount("aaaaaa", "aaa") == 2); ok1(strcount("aaaaaa", "aaaa") == 1); ok1(strcount("aaaaaa", "aaaaa") == 1); ok1(strcount("aaaaaa", "aaaaaa") == 1); ok1(strcount("aaa aaa", "b") == 0); ok1(strcount("aaa aaa", "a") == 6); ok1(strcount("aaa aaa", "aa") == 2); ok1(strcount("aaa aaa", "aaa") == 2); ok1(strcount("aaa aaa", "aaaa") == 0); ok1(strcount("aaa aaa", "aaaaa") == 0); return exit_status(); } horst-version-5.0/channel.c000066400000000000000000000265741273543766500160140ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "main.h" #include "util.h" #include "ifctrl.h" #include "channel.h" #include "ieee80211_util.h" #include "wlan_util.h" static struct timespec last_channelchange; static struct channel_list channels; uint32_t channel_get_remaining_dwell_time(void) { if (!conf.do_change_channel) return UINT32_MAX; int64_t ret = (int64_t)conf.channel_time - (the_time.tv_sec - last_channelchange.tv_sec) * 1000000 - (the_time.tv_nsec - last_channelchange.tv_nsec) / 1000; if (ret < 0) return 0; else if (ret > UINT32_MAX) return UINT32_MAX; else return ret; } static struct band_info channel_get_band_from_idx(int idx) { int b = idx - channels.band[0].num_channels < 0 ? 0 : 1; return channels.band[b]; } static int get_center_freq_ht40(unsigned int freq, bool upper) { unsigned int center = 0; /* * For HT40 we have a channel offset of 20 MHz, and the * center frequency is in the middle: +/- 10 MHz, depending * on HT40+ or HT40- and whether the channel exists */ if (upper && channel_find_index_from_freq(freq + 20) != -1) center = freq + 10; else if (!upper && channel_find_index_from_freq(freq - 20) != -1) center = freq - 10; return center; } static int get_center_freq_vht(unsigned int freq, enum chan_width width) { unsigned int center1 = 0; switch(width) { case CHAN_WIDTH_80: /* * VHT80 channels are non-overlapping and the primary * channel can be on any HT20/40 channel in the range */ if (freq >= 5180 && freq <= 5240) center1 = 5210; else if (freq >= 5260 && freq <= 5320) center1 = 5290; else if (freq >= 5500 && freq <= 5560) center1 = 5530; else if (freq >= 5580 && freq <= 5640) center1 = 5610; else if (freq >= 5660 && freq <= 5720) center1 = 5690; else if (freq >= 5745 && freq <= 5805) center1 = 5775; break; case CHAN_WIDTH_160: /* * There are only two possible VHT160 channels */ if (freq >= 5180 && freq <= 5320) center1 = 5250; else if (freq >= 5180 && freq <= 5320) center1 = 5570; break; case CHAN_WIDTH_8080: printlog("VHT80+80 not supported"); break; default: printlog("%s is not VHT", channel_width_string(width, -1)); } return center1; } const char* channel_width_string(enum chan_width w, int ht40p) { switch (w) { case CHAN_WIDTH_UNSPEC: return "?"; case CHAN_WIDTH_20_NOHT: return "20 (no HT)"; case CHAN_WIDTH_20: return "HT20"; case CHAN_WIDTH_40: return ht40p < 0 ? "HT40" : ht40p ? "HT40+" : "HT40-"; case CHAN_WIDTH_80: return "VHT80"; case CHAN_WIDTH_160: return "VHT160"; case CHAN_WIDTH_8080: return "VHT80+80"; } return ""; } const char* channel_width_string_short(enum chan_width w, int ht40p) { switch (w) { case CHAN_WIDTH_UNSPEC: return "?"; case CHAN_WIDTH_20_NOHT: return "20g"; case CHAN_WIDTH_20: return "20"; case CHAN_WIDTH_40: return ht40p < 0 ? "40" : ht40p ? "40+" : "40-"; case CHAN_WIDTH_80: return "80"; case CHAN_WIDTH_160: return "160"; case CHAN_WIDTH_8080: return "80+80"; } return ""; } /* Note: ht40plus is only used for HT40 channel width, to distinguish between * HT40+ and HT40- */ bool channel_change(int idx, enum chan_width width, bool ht40plus) { unsigned int center1 = 0; if (width == CHAN_WIDTH_UNSPEC) width = channel_get_band_from_idx(idx).max_chan_width; switch (width) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: break; case CHAN_WIDTH_40: center1 = get_center_freq_ht40(channels.chan[idx].freq, ht40plus); break; case CHAN_WIDTH_80: case CHAN_WIDTH_160: center1 = get_center_freq_vht(channels.chan[idx].freq, width); break; default: printlog("%s not implemented", channel_width_string(width, -1)); break; } /* only 20 MHz channels don't need additional center freq, otherwise we fail here * quietly because the scanning code sometimes tries invalid HT40+/- channels */ if (center1 == 0 && !(width == CHAN_WIDTH_20_NOHT || width == CHAN_WIDTH_20)) return false; if (!ifctrl_iwset_freq(conf.ifname, channels.chan[idx].freq, width, center1)) { printlog("ERROR: Failed to set CH %d (%d MHz) %s center %d", channels.chan[idx].chan, channels.chan[idx].freq, channel_width_string(width, ht40plus), center1); return false; } printlog("Set CH %d (%d MHz) %s center %d after %ldms", channels.chan[idx].chan, channels.chan[idx].freq, channel_width_string(width, ht40plus), center1, (the_time.tv_sec - last_channelchange.tv_sec) * 1000 + (the_time.tv_nsec - last_channelchange.tv_nsec) / 1000000); conf.channel_idx = idx; conf.channel_width = width; conf.channel_ht40plus = ht40plus; conf.max_phy_rate = get_phy_thruput(width, channel_get_band_from_idx(idx).streams_rx); last_channelchange = the_time; return true; } bool channel_auto_change(void) { int new_idx; bool ret = true; int start_idx; if (conf.channel_idx == -1) return false; /* The current channel is still unknown for some * reason (mac80211 bug, busy physical interface, * etc.), it will be fixed when the first packet * arrives, see fixup_packet_channel(). * * Without this return, horst would busy-loop forever * (making the ui totally unresponsive) in the channel * changing code below because start_idx would be -1 * as well. Short-circuit exiting here is quite * logical though: it does not make any sense to scan * channels as long as the channel module is not * initialized properly. */ if (channel_get_remaining_dwell_time() > 0) return false; /* too early */ if (conf.do_change_channel) { start_idx = new_idx = conf.channel_idx; do { enum chan_width max_width = channel_get_band_from_idx(new_idx).max_chan_width; /* * For HT40 we visit the same channel twice, once with HT40+ * and once with HT40-. This is necessary to see the HT40+/- * data packets */ if (max_width == CHAN_WIDTH_40) { if (conf.channel_ht40plus) new_idx++; conf.channel_ht40plus = !conf.channel_ht40plus; // toggle } else { new_idx++; } if (new_idx >= channels.num_channels || new_idx >= MAX_CHANNELS || (conf.channel_max && channel_get_chan(new_idx) > conf.channel_max)) { new_idx = 0; max_width = channel_get_band_from_idx(new_idx).max_chan_width; conf.channel_ht40plus = true; } ret = channel_change(new_idx, max_width, conf.channel_ht40plus); /* try setting different channels in case we get errors only * on some channels (e.g. ipw2200 reports channel 14 but cannot * be set to use it). stop if we tried all channels */ } while (ret != 1 && new_idx != start_idx); } return ret; } char* channel_get_string(int idx) { static char buf[32]; struct chan_freq* c = &channels.chan[idx]; snprintf(buf, sizeof(buf), "%-3d: %d HT40%s%s", c->chan, c->freq, get_center_freq_ht40(c->freq, true) ? "+" : "", get_center_freq_ht40(c->freq, false) ? "-" : ""); return buf; } bool channel_init(void) { /* get available channels */ ifctrl_iwget_freqlist(conf.if_phy, &channels); conf.channel_initialized = 1; printf("Got %d Bands, %d Channels:\n", channels.num_bands, channels.num_channels); for (int i = 0; i < channels.num_channels && i < MAX_CHANNELS; i++) printf("%s\n", channel_get_string(i)); if (channels.num_bands <= 0 || channels.num_channels <= 0) return false; if (conf.channel_set_num > 0) { /* configured values */ printf("Setting configured channel %d\n", conf.channel_set_num); int ini_idx = channel_find_index_from_chan(conf.channel_set_num); if (!channel_change(ini_idx, conf.channel_set_width, conf.channel_set_ht40plus)) return false; } else { if (conf.if_freq <= 0) { /* this happens when we have not been able to change * the original interface to monitor mode and we added * an additional monitor (horstX) interface */ printf("Could not get current channel of interface\n"); conf.max_phy_rate = get_phy_thruput(channels.band[0].max_chan_width, channels.band[0].streams_rx); return true; // not failure } conf.channel_idx = channel_find_index_from_freq(conf.if_freq); conf.channel_set_num = channel_get_chan(conf.channel_idx); /* try to set max width */ struct band_info b = channel_get_band_from_idx(conf.channel_idx); if (conf.channel_width != b.max_chan_width) { printlog("Try to set max channel width %s", channel_width_string(b.max_chan_width, -1)); // try both HT40+ and HT40- if necessary if (!channel_change(conf.channel_idx, b.max_chan_width, true) && !channel_change(conf.channel_idx, b.max_chan_width, false)) return false; } else { conf.channel_set_width = conf.channel_width; conf.channel_set_ht40plus = conf.channel_ht40plus; conf.max_phy_rate = get_phy_thruput(conf.channel_width, b.streams_rx); } } return true; } int channel_find_index_from_chan(int c) { int i = -1; for (i = 0; i < channels.num_channels && i < MAX_CHANNELS; i++) if (channels.chan[i].chan == c) return i; return -1; } int channel_find_index_from_freq(unsigned int f) { int i = -1; for (i = 0; i < channels.num_channels && i < MAX_CHANNELS; i++) if (channels.chan[i].freq == f) return i; return -1; } int channel_get_chan(int i) { if (i >= 0 && i < channels.num_channels && i < MAX_CHANNELS) return channels.chan[i].chan; else return -1; } int channel_get_freq(int idx) { if (idx >= 0 && idx < channels.num_channels && idx < MAX_CHANNELS) return channels.chan[idx].freq; else return -1; } bool channel_list_add(int freq) { if (channels.num_channels >= MAX_CHANNELS) return false; channels.chan[channels.num_channels].chan = ieee80211_freq2channel(freq); channels.chan[channels.num_channels].freq = freq; channels.num_channels++; return true; } int channel_get_num_channels(void) { return channels.num_channels; } int channel_get_num_bands(void) { return channels.num_bands; } int channel_get_idx_from_band_idx(int band, int idx) { if (band < 0 || band >= channels.num_bands) return -1; if (idx < 0 || idx >= channels.band[band].num_channels) return -1; if (band > 0) idx = idx + channels.band[0].num_channels; return idx; } const struct band_info* channel_get_band(int b) { if (b < 0 || b > channels.num_bands) return NULL; return &channels.band[b]; } bool channel_band_add(int num_channels, enum chan_width max_chan_width, unsigned char streams_rx, unsigned char streams_tx) { if (channels.num_bands >= MAX_BANDS) return false; channels.band[channels.num_bands].num_channels = num_channels; channels.band[channels.num_bands].max_chan_width = max_chan_width; channels.band[channels.num_bands].streams_rx = streams_rx; channels.band[channels.num_bands].streams_tx = streams_tx; channels.num_bands++; return true; } horst-version-5.0/channel.h000066400000000000000000000046411273543766500160100ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _CHANNEL_H_ #define _CHANNEL_H_ #include #include #define MAX_BANDS 2 #define MAX_CHANNELS 64 /* channel to frequency mapping */ struct chan_freq { int chan; unsigned int freq; }; enum chan_width { CHAN_WIDTH_UNSPEC, CHAN_WIDTH_20_NOHT, CHAN_WIDTH_20, CHAN_WIDTH_40, CHAN_WIDTH_80, CHAN_WIDTH_160, CHAN_WIDTH_8080, }; struct band_info { int num_channels; enum chan_width max_chan_width; unsigned char streams_rx; unsigned char streams_tx; }; struct channel_list { struct chan_freq chan[MAX_CHANNELS]; int num_channels; struct band_info band[MAX_BANDS]; int num_bands; }; bool channel_change(int idx, enum chan_width width, bool ht40plus); bool channel_auto_change(void); int channel_find_index_from_chan(int c); int channel_find_index_from_freq(unsigned int f); int channel_get_chan(int idx); int channel_get_freq(int idx); int channel_get_num_channels(); bool channel_init(void); bool channel_list_add(int freq); uint32_t channel_get_remaining_dwell_time(void); char* channel_get_string(int idx); /* Note: ht40p is used only for HT40 channels. If it should not be shown use -1 */ const char* channel_width_string(enum chan_width w, int ht40p); /* Note: ht40p is used only for HT40 channels. If it should not be shown use -1 */ const char* channel_width_string_short(enum chan_width w, int ht40p); int channel_get_num_bands(); int channel_get_idx_from_band_idx(int band, int idx); const struct band_info* channel_get_band(int b); bool channel_band_add(int num_channels, enum chan_width max_chan_width, unsigned char streams_rx, unsigned char streams_tx); #endif horst-version-5.0/conf_options.c000066400000000000000000000400441273543766500170700ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2014-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "main.h" #include "util.h" #include "wlan_util.h" #include "control.h" #include "conf_options.h" struct conf_option { int option; const char* name; int value_required; const char* default_value; bool (*func)(const char* value); }; static bool conf_quiet(__attribute__((unused)) const char* value) { conf.quiet = 1; return true; } #if DO_DEBUG static bool conf_debug(__attribute__((unused)) const char* value) { conf.debug = 1; return true; } #endif static bool conf_interface(const char* value) { strncpy(conf.ifname, value, IF_NAMESIZE); conf.ifname[IF_NAMESIZE] = '\0'; return true; } static bool conf_add_monitor(const char* value) { if (value != NULL && strcmp(value, "0") == 0) conf.add_monitor = 0; else { conf.add_monitor = 1; } return true; } static bool conf_outfile(const char* value) { dumpfile_open(value); return true; } static bool conf_node_timeout(const char* value) { conf.node_timeout = atoi(value); return true; } static bool conf_receive_buffer(const char* value) { conf.recv_buffer_size = atoi(value); return true; } static bool conf_channel_set(const char* value) { bool ht40plus = false; enum chan_width width = CHAN_WIDTH_20_NOHT; char* pos = strchr(value, '+'); if (pos != NULL) { width = CHAN_WIDTH_40; ht40plus = true; *pos = '\0'; } else if ((pos = strchr(value, '-')) != NULL) { width = CHAN_WIDTH_40; ht40plus = false; *pos = '\0'; } int n = atoi(value); if (conf.channel_initialized) channel_change(channel_find_index_from_chan(n), width, ht40plus); else { /* We have not yet initialized the channel module, channel will be * changed in channel_init(). */ conf.channel_set_num = n; conf.channel_set_width = width; conf.channel_set_ht40plus = ht40plus; } return true; } static bool conf_channel_scan(const char* value) { if (value != NULL && strcmp(value, "0") == 0) conf.do_change_channel = 0; else { conf.do_change_channel = 1; conf.display_view = 's'; // show spectrum view } return true; } /* * This configuration option (channel_scan_rounds=X) defines the number of * rounds horst scans the channel spectrum when the automatic channel scanning * is enabled (channel_scan=1). A scan round is considered to be complete when * the current channel is changed back to the initial channel. When horst goes * out of scan rounds, it quits. */ static bool conf_channel_scan_rounds(const char* value) { conf.channel_scan_rounds = atoi(value); return true; } static bool conf_channel_dwell(const char* value) { conf.channel_time = atoi(value) * 1000; return true; } static bool conf_channel_upper(const char* value) { conf.channel_max = atoi(value); return true; } static bool conf_display_interval(const char* value) { conf.display_interval = atoi(value) * 1000; return true; } static bool conf_display_view(const char* value) { if (strcasecmp(value, "history") == 0 || strcasecmp(value, "hist") == 0) conf.display_view = 'h'; else if (strcasecmp(value, "essid") == 0) conf.display_view = 'e'; else if (strcasecmp(value, "statistics") == 0 || strcasecmp(value, "stats") == 0) conf.display_view = 'a'; else if (strcasecmp(value, "spectrum") == 0 || strcasecmp(value, "spec") == 0) conf.display_view = 's'; return true; } static bool conf_server(const char* value) { if (value != NULL && strcmp(value, "0") == 0) conf.allow_client = 0; else conf.allow_client = 1; return true; } static bool conf_client(const char* value) { strncpy(conf.serveraddr, value, MAX_CONF_VALUE_STRLEN); conf.serveraddr[MAX_CONF_VALUE_STRLEN] = '\0'; return true; } static bool conf_port(const char* value) { conf.port = atoi(value); return true; } static bool conf_control_pipe(const char* value) { /* * Here it's a bit difficult because -X is used for two purposes: * 1) allow control pipe (-X with or without argument) * 2) set the name of the control pipe (-X with argument) which can * also be used in conjuction with -x * That's why we don't set a default value (as it would always allow control) * and especially handle the default name here and in control_send_command() */ if (value != NULL) strncpy(conf.control_pipe, value, MAX_CONF_VALUE_STRLEN); else strncpy(conf.control_pipe, DEFAULT_CONTROL_PIPE, MAX_CONF_VALUE_STRLEN); conf.control_pipe[MAX_CONF_VALUE_STRLEN] = '\0'; conf.allow_control = 1; return true; } static bool conf_filter_mac(const char* value) { static int n; if (n >= MAX_FILTERMAC) { printlog("Can only handle %d MAC filters", MAX_FILTERMAC); return false; } conf.do_macfilter = 1; convert_string_to_mac(value, conf.filtermac[n]); conf.filtermac_enabled[n] = 1; n++; return true; } static bool conf_filter_bssid(const char* value) { convert_string_to_mac(value, conf.filterbssid); return true; } static bool conf_filter_mode(const char* value) { if (conf.filter_mode == WLAN_MODE_ALL) conf.filter_mode = 0; if (strcmp(value, "ALL") == 0) conf.filter_mode = WLAN_MODE_ALL; else if (strcmp(value, "AP") == 0) conf.filter_mode |= WLAN_MODE_AP; else if (strcmp(value, "STA") == 0) conf.filter_mode |= WLAN_MODE_STA; else if (strcmp(value, "ADH") == 0 || strcmp(value, "IBSS") == 0) conf.filter_mode |= WLAN_MODE_IBSS; else if (strcmp(value, "PRB") == 0) conf.filter_mode |= WLAN_MODE_PROBE; else if (strcmp(value, "WDS") == 0) conf.filter_mode |= WLAN_MODE_4ADDR; else if (strcmp(value, "UNKNOWN") == 0) conf.filter_mode |= WLAN_MODE_UNKNOWN; return true; } static bool conf_filter_pkt(const char* value) { int t, i; if (conf.filter_pkt == PKT_TYPE_ALL) { conf.filter_pkt = 0; conf.filter_badfcs = 0; conf.filter_stype[WLAN_FRAME_TYPE_MGMT] = 0; conf.filter_stype[WLAN_FRAME_TYPE_CTRL] = 0; conf.filter_stype[WLAN_FRAME_TYPE_DATA] = 0; } if (strcmp(value, "ALL") == 0) { conf.filter_pkt = PKT_TYPE_ALL; conf.filter_badfcs = 1; conf.filter_stype[WLAN_FRAME_TYPE_MGMT] = 0xffff; conf.filter_stype[WLAN_FRAME_TYPE_CTRL] = 0xffff; conf.filter_stype[WLAN_FRAME_TYPE_DATA] = 0xffff; } else if (strcmp(value, "BADFCS") == 0) conf.filter_badfcs = 1; else if (strcmp(value, "CTRL") == 0 || strcmp(value, "CONTROL") == 0) conf.filter_stype[WLAN_FRAME_TYPE_CTRL] = 0xffff; else if (strcmp(value, "MGMT") == 0 || strcmp(value, "MANAGEMENT") == 0) conf.filter_stype[WLAN_FRAME_TYPE_MGMT] = 0xffff; else if (strcmp(value, "DATA") == 0) conf.filter_stype[WLAN_FRAME_TYPE_DATA] = 0xffff; else if (strcmp(value, "ARP") == 0) conf.filter_pkt |= PKT_TYPE_ARP; else if (strcmp(value, "IP") == 0) conf.filter_pkt |= PKT_TYPE_IP; else if (strcmp(value, "ICMP") == 0) conf.filter_pkt |= PKT_TYPE_ICMP; else if (strcmp(value, "UDP") == 0) conf.filter_pkt |= PKT_TYPE_UDP; else if (strcmp(value, "TCP") == 0) conf.filter_pkt |= PKT_TYPE_TCP; else if (strcmp(value, "OLSR") == 0) conf.filter_pkt |= PKT_TYPE_OLSR; else if (strcmp(value, "BATMAN") == 0) conf.filter_pkt |= PKT_TYPE_BATMAN; else if (strcmp(value, "MESHZ") == 0) conf.filter_pkt |= PKT_TYPE_MESHZ; for (t = 0; t < WLAN_NUM_TYPES; t++) { for (i = 0; i < WLAN_NUM_STYPES; i++) { if (strcasecmp(stype_names[t][i].name, value) == 0) { conf.filter_stype[t] |= BIT(i); return true; } } } return true; } static bool conf_mac_names(const char* value) { if (value != NULL) strncpy(conf.mac_name_file, value, MAX_CONF_VALUE_STRLEN); else strncpy(conf.mac_name_file, DEFAULT_MAC_NAME_FILE, MAX_CONF_VALUE_STRLEN); conf.mac_name_file[MAX_CONF_VALUE_STRLEN] = '\0'; conf.mac_name_lookup = 1; return true; } static struct conf_option conf_options[] = { /* C , NAME VALUE REQUIRED, DEFAULT CALLBACK */ { 'q', "quiet", 0, NULL, conf_quiet }, // NOT dynamic #if DO_DEBUG { 'D', "debug", 0, NULL, conf_debug }, // NOT dynamic #endif { 'i', "interface", 1, "wlan0", conf_interface }, // NOT dynamic { 'a', "add_monitor", 0, NULL, conf_add_monitor }, { 'd', "display_interval", 1, "100", conf_display_interval }, { 'V', "display_view", 1, NULL, conf_display_view }, { 'o', "outfile", 1, NULL, conf_outfile }, { 't', "node_timeout", 1, "60", conf_node_timeout }, { 'b', "receive_buffer", 1, NULL, conf_receive_buffer }, // NOT dynamic { 'C', "channel", 1, NULL, conf_channel_set }, { 's', "channel_scan", 0, NULL, conf_channel_scan }, { 0 , "channel_scan_rounds", 1, "-1", conf_channel_scan_rounds }, { 0 , "channel_dwell", 1, "250", conf_channel_dwell }, { 'u', "channel_upper", 1, NULL, conf_channel_upper }, { 'N', "server", 0, NULL, conf_server }, // NOT dynamic { 'n', "client", 1, NULL, conf_client }, // NOT dynamic { 'p', "port", 1, "4444", conf_port }, // NOT dynamic { 'X', "control_pipe", 2, NULL, conf_control_pipe }, // NOT dynamic { 'e', "filter_mac", 1, NULL, conf_filter_mac }, { 'B', "filter_bssid", 1, NULL, conf_filter_bssid }, { 'm', "filter_mode", 1, "ALL", conf_filter_mode }, { 'f', "filter_packet", 1, "ALL", conf_filter_pkt }, { 'M', "mac_names", 2, NULL, conf_mac_names }, }; /* * More possible config options: * * main view: * sort nodes by: signal, time, bssid, channel * spec view: * show nodes or bars */ /* * This handles command line options from getopt as well as options from the config file * In the first case 'c' is non-zero and name is NULL * In the second case 'c' is 0 and name is set * Value may be null in all cases */ bool config_handle_option(int c, const char* name, const char* value) { unsigned int i; char* end; for (i=0; i < sizeof(conf_options)/sizeof(struct conf_option); i++) { if (((c != 0 && conf_options[i].option == c) || (name != NULL && strcmp(conf_options[i].name, name) == 0)) && conf_options[i].func != NULL) { if (!conf.quiet) { if (value != NULL) printlog("Set '%s' = '%s'", conf_options[i].name, value); else printlog("Set '%s'", conf_options[i].name); } if (value != NULL) { /* split list values and call function multiple times */ while ((end = strchr(value, ',')) != NULL) { *end = '\0'; conf_options[i].func(value); value = end + 1; } } /* call function */ return conf_options[i].func(value); } } if (name != NULL) printlog("Ignoring unknown config option '%s' = '%s'", name, value); return false; } static void config_read_file(const char* filename) { FILE* fp ; char line[255]; char name[MAX_CONF_NAME_STRLEN + 1]; char value[MAX_CONF_VALUE_STRLEN + 1]; int n; int linenum = 0; if ((fp = fopen(filename, "r")) == NULL) { printlog("Could not open config file '%s'", filename); return; } while (fgets(line, sizeof(line), fp) != NULL) { ++linenum; if (line[0] == '#' ) // comment continue; // Note: 200 below has to match MAX_CONF_VALUE_STRLEN // Note: 32 below has to match MAX_CONF_NAME_STRLEN n = sscanf(line, " %32[^= \n] = %200[^ \n]", name, value); if (n < 0) { // empty line continue; } else if (n == 0) { printlog("Config file has garbage on line %d, " "ignoring the line.", linenum); continue; } else if (n == 1) { // no value config_handle_option(0, name, NULL); } else { config_handle_option(0, name, value); } } fclose(fp); } static void config_apply_defaults(void) { unsigned int i; for (i=0; i < sizeof(conf_options)/sizeof(struct conf_option); i++) { if (conf_options[i].default_value != NULL) { conf_options[i].func(conf_options[i].default_value); } } } static char* config_get_getopt_string(char* buf, size_t maxlen, const char* add) { unsigned int pos = 0; unsigned int i; maxlen = maxlen - 1; // we use it as string index for (i=0; i < sizeof(conf_options)/sizeof(struct conf_option) && pos < maxlen; i++) { if (conf_options[i].option != 0 && pos < maxlen) { buf[pos++] = conf_options[i].option; if (conf_options[i].value_required && pos < maxlen) { buf[pos++] = ':'; } if (conf_options[i].value_required == 2 && pos < maxlen) { buf[pos++] = ':'; } } } buf[pos] = '\0'; if (add != NULL) { if (pos < maxlen && (maxlen - pos) >= strlen(add)) strncat(buf, add, (maxlen - pos)); else { printlog("Not enough space for getopt string!"); exit(1); } } return buf; } static void print_usage(const char* name) { printf("\nUsage: %s [-v] [-h] [-q] [-D] [-a] [-c file] [-i interface] [-t sec] [-d ms] [-V view] [-b bytes]\n" "\t\t[-s] [-u] [-N] [-n IP] [-p port] [-o file] [-X[name]] [-x command]\n" "\t\t[][-e MAC] [-f PKT_NAME] [-m MODE] [-B BSSID]\n\n" "General Options: Description (default value)\n" " -v\t\tshow version\n" " -h\t\tHelp\n" " -q\t\tQuiet, no output\n" #if DO_DEBUG " -D\t\tShow lots of debug output, no UI\n" #endif " -a\t\tAlways add virtual monitor interface\n" " -c \tConfig file (" CONFIG_FILE ")\n" " -C \tSet initial channel\n" " -i \tInterface name (wlan0)\n" " -t \tNode timeout in seconds (60)\n" " -d \tDisplay update interval in ms (100)\n" " -V view\tDisplay view: history|essid|statistics|spectrum\n" " -b \tReceive buffer size in bytes (not set)\n" " -M[filename]\tMAC address to host name mapping (/tmp/dhcp.leases)\n" "\nFeature Options:\n" " -s\t\t(Poor mans) Spectrum analyzer mode\n" " -u\t\tUpper channel limit\n\n" " -N\t\tAllow network connection, server mode (off)\n" " -n \tConnect to server with , client mode (off)\n" " -p \tPort number of server (4444)\n\n" " -o \tWrite packet info into 'filename'\n\n" " -X[filename]\tAllow control socket on 'filename' (/tmp/horst)\n" " -x \tSend control command\n" "\nFilter Options:\n" " Filters are generally 'positive' or 'inclusive' which means you define\n" " what you want to see, and everything else is getting filtered out.\n" " If a filter is not set it is inactive and nothing is filtered.\n" " Most filter options can be specified multiple times and will be combined\n" " -e \tSource MAC addresses (xx:xx:xx:xx:xx:xx), up to 9 times\n" " -f \tFilter packet types, multiple\n" " -m \tOperating mode: AP|STA|ADH|PRB|WDS|UNKNOWN, multiple\n" " -B \tBSSID (xx:xx:xx:xx:xx:xx), only one\n" "\n", name); } void config_parse_file_and_cmdline(int argc, char** argv) { char getopt_str[(sizeof(conf_options)/sizeof(struct conf_option))*2 + 10]; char* conf_filename = CONFIG_FILE; int c; config_get_getopt_string(getopt_str, sizeof(getopt_str), "hvc:x:"); /* first: apply default values */ config_apply_defaults(); /* * then: handle command line options which are not * configuration options ("hc:") */ while ((c = getopt(argc, argv, getopt_str)) > 0) { switch (c) { case 'c': printlog("Using config file '%s'", optarg); conf_filename = optarg; break; case 'v': printf("Version %s (build date: %s %s)\n", VERSION, __DATE__, __TIME__); exit(0); case 'h': case '?': print_usage(argv[0]); exit(0); } } /* read config file */ config_read_file(conf_filename); /* * get command line options which are configuration, to let them * override or add to the config file options */ optind = 1; while ((c = getopt(argc, argv, getopt_str)) > 0) { config_handle_option(c, NULL, optarg); } /* * and finally get command line options ("commands") which depend * on config options ("x:") */ optind = 1; while ((c = getopt(argc, argv, getopt_str)) > 0) { switch (c) { case 'x': control_send_command(optarg); exit(0); } } } horst-version-5.0/conf_options.h000066400000000000000000000020301273543766500170660ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2014-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _CONF_OPTIONS_H_ #define _CONF_OPTIONS_H_ #include void config_parse_file_and_cmdline(int argc, char** argv); bool config_handle_option(int c, const char* name, const char* value); #endif horst-version-5.0/config.h000066400000000000000000000003221273543766500156350ustar00rootroot00000000000000/* Generated by CCAN configurator */ #ifndef CCAN_CONFIG_H #define CCAN_CONFIG_H #ifndef _GNU_SOURCE #define _GNU_SOURCE /* Always use GNU extensions. */ #endif #define HAVE_TYPEOF 1 #endif /* CCAN_CONFIG_H */ horst-version-5.0/control.c000066400000000000000000000056101273543766500160500ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "main.h" #include "channel.h" #include "control.h" #include "conf_options.h" #define MAX_CMD 255 /* FIFO (named pipe) */ int ctlpipe = -1; void control_init_pipe(void) { mkfifo(conf.control_pipe, 0666); ctlpipe = open(conf.control_pipe, O_RDWR|O_NONBLOCK); } void control_send_command(const char* cmd) { int len = strlen(cmd); char new[len + 2]; char* pos; if (conf.control_pipe[0] == '\0') { strncpy(conf.control_pipe, DEFAULT_CONTROL_PIPE, MAX_CONF_VALUE_STRLEN); conf.control_pipe[MAX_CONF_VALUE_STRLEN] = '\0'; } while (access(conf.control_pipe, F_OK) < 0) { printlog("Waiting for control pipe '%s'...", conf.control_pipe); sleep(1); } ctlpipe = open(conf.control_pipe, O_WRONLY); if (ctlpipe < 0) err(1, "Could not open control socket '%s'", conf.control_pipe); /* always terminate command with newline */ strncpy(new, cmd, len); new[len] = '\n'; new[len+1] = '\0'; /* replace : with newline */ while ((pos = strchr(new, ';')) != NULL) { *pos = '\n'; } printlog("Sending command: %s", new); write(ctlpipe, new, len+1); close(ctlpipe); } static void parse_command(char* in) { char* cmd; char* val; cmd = strsep(&in, "="); val = in; //printlog("RECV CMD %s VAL %s", cmd, val); /* commands without value */ if (strcmp(cmd, "pause") == 0) { main_pause(1); } else if (strcmp(cmd, "resume") == 0) { main_pause(0); } else if (strcmp(cmd, "reset") == 0) { main_reset(); } else { /* handle the rest thru config options */ config_handle_option(0, cmd, val); } } void control_receive_command(void) { char buf[MAX_CMD]; char *pos = buf; char *end; int len; len = read(ctlpipe, buf, MAX_CMD); if (len > 0) { buf[len] = '\0'; /* we can receive multiple \n separated commands */ while ((end = strchr(pos, '\n')) != NULL) { *end = '\0'; parse_command(pos); pos = end + 1; } } } void control_finish(void) { if (ctlpipe == -1) return; close(ctlpipe); unlink(conf.control_pipe); ctlpipe = -1; } horst-version-5.0/control.h000066400000000000000000000020671273543766500160600ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _CONTROL_H_ #define _CONTROL_H_ #define DEFAULT_CONTROL_PIPE "/tmp/horst" extern int ctlpipe; void control_init_pipe(); void control_send_command(const char* cmd); void control_receive_command(); void control_finish(void); #endif horst-version-5.0/display-channel.c000066400000000000000000000124241273543766500174440ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* FILTER *******************/ #include #include "display.h" #include "main.h" #include "channel.h" #include "network.h" #define COL_BAND2 23 void update_channel_win(WINDOW *win) { int l = 2; box(win, 0 , 0); print_centered(win, 0, 39, " Channel Settings "); wattron(win, WHITE); for (int b = 0; b < channel_get_num_bands(); b++) { const struct band_info* bp = channel_get_band(b); int c = channel_get_idx_from_band_idx(b, 0); int col = channel_get_chan(c) > 14 ? COL_BAND2 : 2; wattron(win, A_BOLD); mvwprintw(win, 2, col, "%s: %s", col == 2 ? "2.4GHz" : "5GHz", channel_width_string(bp->max_chan_width, -1)); if (bp->streams_rx || bp->streams_tx) wprintw(win, " %dx%d", bp->streams_rx, bp->streams_tx); wattroff(win, A_BOLD); l = 3; for (int i = 0; (c = channel_get_idx_from_band_idx(b, i)) != -1; i++) { if (c == conf.channel_idx) wattron(win, CYAN); else wattron(win, WHITE); mvwprintw(win, l++, col, "%s", channel_get_string(c)); } } wattroff(win, WHITE); l = 18; wattron(win, A_BOLD); mvwprintw(win, l++, 2, "s: [%c] Scan", CHECKED(conf.do_change_channel)); wattroff(win, A_BOLD); mvwprintw(win, l++, 2, "d: Dwell: %d ms ", conf.channel_time/1000); mvwprintw(win, l++, 2, "u: Upper limit: %d ", conf.channel_max); l++; wattron(win, A_BOLD); mvwprintw(win, l++, 2, "m: Set channel: %d ", conf.channel_set_num); wattroff(win, A_BOLD); mvwprintw(win, l++, 2, "1: [%c] 20 (no HT)", CHECKED(conf.channel_set_width == CHAN_WIDTH_20_NOHT)); mvwprintw(win, l++, 2, "2: [%c] HT20", CHECKED(conf.channel_set_width == CHAN_WIDTH_20)); mvwprintw(win, l++, 2, "4: [%c] HT40-", CHECKED(conf.channel_set_width == CHAN_WIDTH_40 && !conf.channel_set_ht40plus)); mvwprintw(win, l++, 2, "5: [%c] HT40+", CHECKED(conf.channel_set_width == CHAN_WIDTH_40 && conf.channel_set_ht40plus)); mvwprintw(win, l++, 2, "8: [%c] VHT80", CHECKED(conf.channel_set_width == CHAN_WIDTH_80)); mvwprintw(win, l++, 2, "6: [%c] VHT160", CHECKED(conf.channel_set_width == CHAN_WIDTH_160)); print_centered(win, CHANNEL_WIN_HEIGHT-1, CHANNEL_WIN_WIDTH, "[ Press keys and ENTER to apply ]"); wrefresh(win); } bool channel_input(WINDOW *win, int c) { char buf[6]; int x; int new_idx = -1; switch (c) { case 's': case 'S': conf.do_change_channel = conf.do_change_channel ? 0 : 1; break; case 'd': case 'D': echo(); curs_set(1); mvwgetnstr(win, 19, 12, buf, 6); curs_set(0); noecho(); sscanf(buf, "%d", &x); conf.channel_time = x*1000; break; case 'u': case 'U': echo(); curs_set(1); mvwgetnstr(win, 20, 18, buf, 6); curs_set(0); noecho(); sscanf(buf, "%d", &x); conf.channel_max = x; break; case 'm': case 'M': echo(); curs_set(1); mvwgetnstr(win, 22, 18, buf, 3); curs_set(0); noecho(); sscanf(buf, "%d", &x); conf.channel_set_num = x; break; case '1': conf.channel_set_width = CHAN_WIDTH_20_NOHT; break; case '2': conf.channel_set_width = CHAN_WIDTH_20; break; case '4': conf.channel_set_width = CHAN_WIDTH_40; conf.channel_set_ht40plus = false; break; case '5': conf.channel_set_width = CHAN_WIDTH_40; conf.channel_set_ht40plus = true; break; case '8': conf.channel_set_width = CHAN_WIDTH_80; break; case '6': conf.channel_set_width = CHAN_WIDTH_160; break; case '\r': case KEY_ENTER: /* used to close win, too */ new_idx = channel_find_index_from_chan(conf.channel_set_num); if ((new_idx >= 0 && new_idx != conf.channel_idx) || conf.channel_set_width != conf.channel_width || conf.channel_set_ht40plus != conf.channel_ht40plus) { /* some setting changed */ if (conf.serveraddr[0] == '\0') { /* server */ if (!channel_change(new_idx, conf.channel_set_width, conf.channel_set_ht40plus)) { printlog("Channel %d %s is not available/allowed", conf.channel_set_num, channel_width_string(conf.channel_set_width, conf.channel_set_ht40plus)); /* reset UI */ conf.channel_set_width = conf.channel_width; conf.channel_set_ht40plus = conf.channel_ht40plus; } else { net_send_channel_config(); } } else { /* client */ conf.channel_idx = new_idx; conf.channel_width = conf.channel_set_width; conf.channel_ht40plus = conf.channel_set_ht40plus; printlog("Sending channel config to server"); net_send_channel_config(); } } return false; default: return false; /* didn't handle input */ } update_channel_win(win); return true; } horst-version-5.0/display-essid.c000066400000000000000000000045541273543766500171500ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* ESSID *******************/ #include #include "display.h" #include "main.h" #include "util.h" void update_essid_win(WINDOW *win) { int i; int line = 1; struct essid_info* e; struct node_info* n; werase(win); wattron(win, WHITE); wattroff(win, A_BOLD); box(win, 0 , 0); print_centered(win, 0, COLS, " ESSIDs "); mvwprintw(win, line++, 3, "NO. MODE SOURCE (BSSID) TSF (BINT) CH Sig E IP"); list_for_each(&essids.list, e, list) { if (line > LINES-3) break; wattron(win, WHITE | A_BOLD); mvwprintw(win, line, 2, "ESSID '%s'", e->essid ); if (e->split > 0) { wattron(win, RED); wprintw(win, " *** SPLIT ***"); } else wattron(win, GREEN); line++; i = 1; list_for_each(&e->nodes, n, essid_nodes) { if (line > LINES-3) break; if (n->last_seen > (the_time.tv_sec - conf.node_timeout / 2)) wattron(win, A_BOLD); else wattroff(win, A_BOLD); mvwprintw(win, line, 3, "%2d. %s %-17s", i++, (n->wlan_mode & WLAN_MODE_AP) ? "AP " : "IBSS", mac_name_lookup(n->last_pkt.wlan_src, 0)); wprintw(win, " (%s)", ether_sprintf(n->wlan_bssid)); wprintw(win, " %016llx", n->wlan_tsf); wprintw(win, " (%d)", n->wlan_bintval); if (n->wlan_bintval < 1000) wprintw(win, " "); wprintw(win, " %2d", n->wlan_channel); wprintw(win, " %3d", n->last_pkt.phy_signal); wprintw(win, " %s", n->wlan_wep ? "W" : " "); if (n->pkt_types & PKT_TYPE_IP) wprintw(win, " %s", ip_sprintf(n->ip_src)); line++; } } wnoutrefresh(win); } horst-version-5.0/display-filter.c000066400000000000000000000162031273543766500173200ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* FILTER *******************/ #include #include "display.h" #include "main.h" #include "util.h" #include "wlan_util.h" #include "wlan80211.h" #include "network.h" #define MAC_COL 2 #define MODE_COL 30 #define SECOND_ROW 19 #define THIRD_ROW 23 void update_filter_win(WINDOW *win) { int l, i, t, col = 2; box(win, 0 , 0); print_centered(win, 0, 57, " Edit Filters "); for (t = 0; t < WLAN_NUM_TYPES; t++) { l = 2; wattron(win, get_packet_type_color(WLAN_FRAME_FC(t, 0))); wattron(win, A_BOLD); if (t == 0) mvwprintw(win, l++, col, "m: [%c] Management", CHECKED(conf.filter_stype[t] & 0xffff)); else if (t == 1) mvwprintw(win, l++, col, "c: [%c] Control", CHECKED(conf.filter_stype[t] & 0xffff)); else mvwprintw(win, l++, col, "d: [%c] Data", CHECKED(conf.filter_stype[t] & 0xffff)); wattroff(win, A_BOLD); for (i = 0; i < WLAN_NUM_STYPES; i++) { if (stype_names[t][i].c != '-') mvwprintw(win, l++, col, "%c: [%c] %s", stype_names[t][i].c, CHECKED(conf.filter_stype[t] & BIT(i)), stype_names[t][i].name); } col += 19; } l = 15; wattron(win, RED); mvwprintw(win, l++, 21, "*: [%c] Bad FCS", CHECKED(conf.filter_badfcs)); wattroff(win, RED); wattron(win, A_BOLD); mvwprintw(win, l++, 21, "0: [%c] All Off", CHECKED(conf.filter_off)); wattroff(win, A_BOLD); l = SECOND_ROW-1; wattron(win, A_BOLD); mvwprintw(win, l++, 2, "Higher Level Protocols"); wattroff(win, A_BOLD); wattron(win, WHITE); mvwprintw(win, l++, 2, "r: [%c] ARP", CHECKED(conf.filter_pkt & PKT_TYPE_ARP)); mvwprintw(win, l++, 2, "M: [%c] ICMP/PING", CHECKED(conf.filter_pkt & PKT_TYPE_ICMP)); mvwprintw(win, l++, 2, "i: [%c] IP", CHECKED(conf.filter_pkt & PKT_TYPE_IP)); l = SECOND_ROW; mvwprintw(win, l++, 21, "V: [%c] UDP", CHECKED(conf.filter_pkt & PKT_TYPE_UDP)); mvwprintw(win, l++, 21, "W: [%c] TCP", CHECKED(conf.filter_pkt & PKT_TYPE_TCP)); l = SECOND_ROW; mvwprintw(win, l++, 40, "I: [%c] OLSR", CHECKED(conf.filter_pkt & PKT_TYPE_OLSR)); mvwprintw(win, l++, 40, "K: [%c] BATMAN", CHECKED(conf.filter_pkt & PKT_TYPE_BATMAN)); mvwprintw(win, l++, 40, "Z: [%c] Meshz", CHECKED(conf.filter_pkt & PKT_TYPE_MESHZ)); l = THIRD_ROW; wattron(win, A_BOLD); mvwprintw(win, l++, MAC_COL, "Source MAC Addresses"); wattroff(win, A_BOLD); for (i = 0; i < MAX_FILTERMAC; i++) { mvwprintw(win, l++, MAC_COL, "%d: [%c] %s", i+1, CHECKED(conf.filtermac_enabled[i]), ether_sprintf(conf.filtermac[i])); } l = THIRD_ROW; wattron(win, A_BOLD); mvwprintw(win, l++, MODE_COL, "BSSID"); wattroff(win, A_BOLD); mvwprintw(win, l++, MODE_COL, "_: [%c] %s", CHECKED(MAC_NOT_EMPTY(conf.filterbssid)), ether_sprintf(conf.filterbssid)); l++; wattron(win, A_BOLD); mvwprintw(win, l++, MODE_COL, "Mode"); wattroff(win, A_BOLD); mvwprintw(win, l++, MODE_COL, "!: [%c] Access Point", CHECKED(conf.filter_mode & WLAN_MODE_AP)); mvwprintw(win, l++, MODE_COL, "@: [%c] Station", CHECKED(conf.filter_mode & WLAN_MODE_STA)); mvwprintw(win, l++, MODE_COL, "#: [%c] IBSS (Ad-hoc)", CHECKED(conf.filter_mode & WLAN_MODE_IBSS)); mvwprintw(win, l++, MODE_COL, "$: [%c] Probe Request", CHECKED(conf.filter_mode & WLAN_MODE_PROBE)); mvwprintw(win, l++, MODE_COL, "%: [%c] WDS/4ADDR", CHECKED(conf.filter_mode & WLAN_MODE_4ADDR)); mvwprintw(win, l++, MODE_COL, "^: [%c] Unknown", CHECKED(conf.filter_mode & WLAN_MODE_UNKNOWN)); wattroff(win, WHITE); print_centered(win, ++l, FILTER_WIN_WIDTH, "[ Press key or ENTER ]"); wrefresh(win); } bool filter_input(WINDOW *win, int c) { char buf[18]; int i, t; switch (c) { case 'm': TOGGLE_BITSET(conf.filter_stype[WLAN_FRAME_TYPE_MGMT], 0xffff, uint16_t); break; case 'c': TOGGLE_BITSET(conf.filter_stype[WLAN_FRAME_TYPE_CTRL], 0xffff, uint16_t); break; case 'd': TOGGLE_BITSET(conf.filter_stype[WLAN_FRAME_TYPE_DATA], 0xffff, uint16_t); break; case 'r': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_ARP); break; case 'M': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_ICMP); break; case 'i': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_IP); break; case 'V': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_UDP); break; case 'W': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_TCP); break; case 'I': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_OLSR); break; case 'K': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_BATMAN); break; case 'Z': TOGGLE_BIT(conf.filter_pkt, PKT_TYPE_MESHZ); break; case '!': TOGGLE_BIT(conf.filter_mode, WLAN_MODE_AP); break; case '@': TOGGLE_BIT(conf.filter_mode, WLAN_MODE_STA); break; case '#': TOGGLE_BIT(conf.filter_mode, WLAN_MODE_IBSS); break; case '$': TOGGLE_BIT(conf.filter_mode, WLAN_MODE_PROBE); break; case '%': TOGGLE_BIT(conf.filter_mode, WLAN_MODE_4ADDR); break; case '^': TOGGLE_BIT(conf.filter_mode, WLAN_MODE_UNKNOWN); break; case '_': echo(); print_centered(win, FILTER_WIN_HEIGHT-1, FILTER_WIN_WIDTH, "[ Enter new BSSID and ENTER ]"); mvwprintw(win, THIRD_ROW + 1, MODE_COL + 4, ">"); mvwgetnstr(win, THIRD_ROW + 1, MODE_COL + 7, buf, 17); noecho(); convert_string_to_mac(buf, conf.filterbssid); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = c - '1'; if (MAC_NOT_EMPTY(conf.filtermac[i]) && conf.filtermac_enabled[i]) { conf.filtermac_enabled[i] = 0; } else { echo(); print_centered(win, FILTER_WIN_HEIGHT-1, FILTER_WIN_WIDTH, "[ Enter new MAC %d and ENTER ]", i+1); mvwprintw(win, THIRD_ROW + 1 + i, MAC_COL + 4, ">"); mvwgetnstr(win, THIRD_ROW + 1 + i, MAC_COL + 7, buf, 17); noecho(); /* just enable old MAC if user pressed return only */ if (*buf == '\0' && MAC_NOT_EMPTY(conf.filtermac[i])) conf.filtermac_enabled[i] = 1; else { convert_string_to_mac(buf, conf.filtermac[i]); if (MAC_NOT_EMPTY(conf.filtermac[i])) conf.filtermac_enabled[i] = true; } } break; case '0': conf.filter_off = conf.filter_off ? 0 : 1; break; case '*': conf.filter_badfcs = conf.filter_badfcs ? 0 : 1; break; default: for (t = 0; t < WLAN_NUM_TYPES; t++) { for (i = 0; i < WLAN_NUM_STYPES; i++) { if (stype_names[t][i].c == c) { TOGGLE_BIT(conf.filter_stype[t], BIT(i)); goto out; } } } return false; // not found } out: /* recalculate filter flag */ conf.do_macfilter = 0; for (i = 0; i < MAX_FILTERMAC; i++) { if (conf.filtermac_enabled[i]) conf.do_macfilter = 1; } net_send_filter_config(); update_filter_win(win); return true; } horst-version-5.0/display-help.c000066400000000000000000000042611273543766500167640ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* HELP *******************/ #include #include "display.h" #include "main.h" #include "wlan_util.h" void update_help_win(WINDOW *win) { int i, t, col, l; werase(win); wattron(win, WHITE); box(win, 0 , 0); print_centered(win, 0, COLS, " Help "); print_centered(win, 2, COLS, "HORST - Horsts OLSR Radio Scanning Tool (or)"); print_centered(win, 3, COLS, "HORST - Highly Optimized Radio Scanning Tool"); print_centered(win, 5, COLS, "Version " VERSION " (build date " __DATE__ " " __TIME__ ")"); print_centered(win, 6, COLS, "(C) 2005-2016 Bruno Randolf, Licensed under the GPLv2"); mvwprintw(win, 8, 2, "Known IEEE802.11 Packet Types:"); for (t = 0; t < WLAN_NUM_TYPES; t++) { wattron(win, A_BOLD); if (t == 0) { l = 10; col = 4; mvwprintw(win, l++, 2, "MANAGEMENT FRAMES"); } else if (t == 1) { l++; mvwprintw(win, l++, 2, "CONTROL FRAMES"); } else { l = 10; col = 47; mvwprintw(win, l++, 45, "DATA FRAMES"); } wattroff(win, A_BOLD); for (i = 0; i < WLAN_NUM_STYPES; i++) { if (stype_names[t][i].c != '-') mvwprintw(win, l++, col, "%c %-6s %s", stype_names[t][i].c, stype_names[t][i].name, stype_names[t][i].desc); } } wattron(win, WHITE); print_centered(win, 39, COLS, "For more info read the man page or check http://br1.einfach.org/horst/"); wrefresh(win); } horst-version-5.0/display-history.c000066400000000000000000000063721273543766500175420ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* HISTORY *******************/ #include #include "display.h" #include "main.h" #include "util.h" #include "wlan_util.h" #define SIGN_POS LINES-17 #define TYPE_POS SIGN_POS+1 #define RATE_POS LINES-2 void update_history_win(WINDOW *win) { int i; int col = COLS-2; int sig, rat; if (col > MAX_HISTORY) col = 4 + MAX_HISTORY; werase(win); wattron(win, WHITE); box(win, 0 , 0); print_centered(win, 0, COLS, " Signal/Rate History "); mvwhline(win, SIGN_POS, 1, ACS_HLINE, col); mvwhline(win, SIGN_POS+2, 1, ACS_HLINE, col); mvwvline(win, 1, 4, ACS_VLINE, LINES-3); wattron(win, GREEN); mvwprintw(win, 2, 1, "dBm"); mvwprintw(win, normalize_db(30, SIGN_POS - 1) + 1, 1, "-30"); mvwprintw(win, normalize_db(40, SIGN_POS - 1) + 1, 1, "-40"); mvwprintw(win, normalize_db(50, SIGN_POS - 1) + 1, 1, "-50"); mvwprintw(win, normalize_db(60, SIGN_POS - 1) + 1, 1, "-60"); mvwprintw(win, normalize_db(70, SIGN_POS - 1) + 1, 1, "-70"); mvwprintw(win, normalize_db(80, SIGN_POS - 1) + 1, 1, "-80"); mvwprintw(win, normalize_db(90, SIGN_POS - 1) + 1, 1, "-90"); mvwprintw(win, SIGN_POS-1, 1, "-99"); mvwprintw(win, 1, col-6, "Signal"); wattron(win, CYAN); mvwprintw(win, TYPE_POS, 1, "TYP"); mvwprintw(win, 2, col-11, "Packet Type"); wattron(win, A_BOLD); wattron(win, BLUE); mvwprintw(win, 3, col-4, "Rate"); mvwprintw(win, RATE_POS-12, 1, "300"); mvwprintw(win, RATE_POS-11, 1, "275"); mvwprintw(win, RATE_POS-10, 1, "250"); mvwprintw(win, RATE_POS-9, 1, "225"); mvwprintw(win, RATE_POS-8, 1, "200"); mvwprintw(win, RATE_POS-7, 1, "175"); mvwprintw(win, RATE_POS-6, 1, "150"); mvwprintw(win, RATE_POS-5, 1, "125"); mvwprintw(win, RATE_POS-4, 1, "100"); mvwprintw(win, RATE_POS-3, 1, " 75"); mvwprintw(win, RATE_POS-2, 1, " 50"); mvwprintw(win, RATE_POS-1, 1, " 25"); wattroff(win, A_BOLD); i = hist.index - 1; while (col > 4 && hist.signal[i] != 0) { sig = normalize_db(-hist.signal[i], SIGN_POS - 1); wattron(win, ALLGREEN); mvwvline(win, sig + 1, col, ACS_BLOCK, SIGN_POS - sig - 1); wattron(win, get_packet_type_color(hist.type[i])); mvwprintw(win, TYPE_POS, col, "%c", \ get_packet_type_char(hist.type[i])); if (hist.retry[i]) mvwprintw(win, TYPE_POS+1, col, "r"); rat = hist.rate[i]/250; wattron(win, A_BOLD); wattron(win, BLUE); mvwvline(win, RATE_POS - rat, col, 'x', rat); wattroff(win, A_BOLD); i--; col--; if (i < 0) i = MAX_HISTORY-1; } wnoutrefresh(win); } horst-version-5.0/display-main.c000066400000000000000000000363611273543766500167660ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* MAIN / OVERVIEW *******************/ #include #include #include "display.h" #include "main.h" #include "util.h" #include "wlan80211.h" #include "wlan_util.h" #include "olsr_header.h" #include "batman_adv_header-14.h" #include "listsort.h" #include "channel.h" static WINDOW *sort_win = NULL; static WINDOW *dump_win = NULL; static WINDOW *list_win = NULL; static WINDOW *stat_win = NULL; static int do_sort = 'n'; /* pointer to the sort function */ static int(*sortfunc)(const struct list_node*, const struct list_node*) = NULL; /* sizes of split window (list_win & status_win) */ static int win_split; static int stat_height; static struct ewma usen_avg; static struct ewma bpsn_avg; /******************* UTIL *******************/ void print_dump_win(const char *str, int refresh) { wattron(dump_win, RED); wprintw(dump_win, str); wattroff(dump_win, RED); if (refresh) wrefresh(dump_win); else wnoutrefresh(dump_win); } /******************* SORTING *******************/ static int compare_nodes_signal(const struct list_node *p1, const struct list_node *p2) { struct node_info* n1 = list_entry(p1, struct node_info, list); struct node_info* n2 = list_entry(p2, struct node_info, list); if (n1->last_pkt.phy_signal > n2->last_pkt.phy_signal) return -1; else if (n1->last_pkt.phy_signal == n2->last_pkt.phy_signal) return 0; else return 1; } static int compare_nodes_time(const struct list_node *p1, const struct list_node *p2) { struct node_info* n1 = list_entry(p1, struct node_info, list); struct node_info* n2 = list_entry(p2, struct node_info, list); if (n1->last_seen > n2->last_seen) return -1; else if (n1->last_seen == n2->last_seen) return 0; else return 1; } static int compare_nodes_channel(const struct list_node *p1, const struct list_node *p2) { struct node_info* n1 = list_entry(p1, struct node_info, list); struct node_info* n2 = list_entry(p2, struct node_info, list); if (n1->wlan_channel < n2->wlan_channel) return 1; else if (n1->wlan_channel == n2->wlan_channel) return 0; else return -1; } static int compare_nodes_bssid(const struct list_node *p1, const struct list_node *p2) { struct node_info* n1 = list_entry(p1, struct node_info, list); struct node_info* n2 = list_entry(p2, struct node_info, list); return -memcmp(n1->wlan_bssid, n2->wlan_bssid, MAC_LEN); } static bool sort_input(int c) { switch (c) { case 'n': case 'N': sortfunc = NULL; break; case 's': case 'S': sortfunc = compare_nodes_signal; break; case 't': case 'T': sortfunc = compare_nodes_time; break; case 'c': case 'C': sortfunc = compare_nodes_channel; break; case 'b': case 'B': sortfunc = compare_nodes_bssid; break; } switch (c) { case 'n': case 'N': case 's': case 'S': case 't': case 'T': case 'c': case 'C': case 'b': case 'B': do_sort = c; /* no break */ case '\r': case KEY_ENTER: delwin(sort_win); sort_win = NULL; update_display(NULL); return true; } return false; } static void show_sort_win(void) { if (sort_win == NULL) { sort_win = newwin(1, COLS-2, win_split - 2, 1); wattron(sort_win, BLACKONWHITE); mvwhline(sort_win, 0, 0, ' ', COLS); mvwprintw(sort_win, 0, 0, " -> Sort by s:Signal t:Time b:BSSID c:Channel n:Don't sort [current: %c]", do_sort); wrefresh(sort_win); } } /******************* WINDOWS *******************/ #define STAT_WIDTH 11 #define STAT_START 4 static void update_status_win(struct packet_info* p) { int sig, siga, bps, dps, pps, rps, bpsn, usen; float use, rpsp = 0.0; int max_stat_bar = stat_height - STAT_START; struct channel_info* chan = NULL; if (p != NULL) werase(stat_win); wattron(stat_win, WHITE); mvwvline(stat_win, 0, 0, ACS_VLINE, stat_height); get_per_second(stats.bytes, stats.duration, stats.packets, stats.retries, &bps, &dps, &pps, &rps); bps *= 8; bpsn = normalize(bps, conf.max_phy_rate * 100000 / 3 * 2, max_stat_bar); use = dps * 1.0 / 10000; /* usec, in percent */ usen = normalize(use, 100, max_stat_bar); if (pps) rpsp = rps * 100.0 / pps; ewma_add(&usen_avg, usen); ewma_add(&bpsn_avg, bpsn); if (p != NULL) { sig = normalize_db(-p->phy_signal, max_stat_bar); if (p->pkt_chan_idx > 0) chan = &spectrum[p->pkt_chan_idx]; if (chan != NULL && chan->packets >= 8) siga = normalize_db(ewma_read(&chan->signal_avg), max_stat_bar); else siga = sig; wattron(stat_win, GREEN); mvwprintw(stat_win, 0, 1, "Sig: %5d", p->phy_signal); signal_average_bar(stat_win, sig, siga, STAT_START, 2, stat_height, 2); } wattron(stat_win, CYAN); mvwprintw(stat_win, 1, 1, "bps:%6s", kilo_mega_ize(bps)); general_average_bar(stat_win, bpsn, ewma_read(&bpsn_avg), stat_height, 5, 2, CYAN, ALLCYAN); wattron(stat_win, YELLOW); mvwprintw(stat_win, 2, 1, "Use:%5.1f%%", use); general_average_bar(stat_win, usen, ewma_read(&usen_avg), stat_height, 8, 2, YELLOW, ALLYELLOW); mvwprintw(stat_win, 3, 1, "Retry: %2.0f%%", rpsp); wnoutrefresh(stat_win); } #define COL_PKT 3 #define COL_CHAN COL_PKT + 7 #define COL_SIG COL_CHAN + 4 #define COL_RATE COL_SIG + 4 #define COL_SOURCE COL_RATE + 4 #define COL_MODE COL_SOURCE + 18 #define COL_WIDTH COL_MODE + 6 #define COL_ENC COL_WIDTH + 11 #define COL_ESSID COL_ENC + 6 #define COL_INFO COL_ESSID + 13 static char spin[4] = {'/', '-', '\\', '|'}; static void print_node_list_line(int line, struct node_info* n) { struct packet_info* p = &n->last_pkt; char* ssid = NULL; if (n->pkt_types & PKT_TYPE_OLSR) wattron(list_win, GREEN); if (n->last_seen > (the_time.tv_sec - conf.node_timeout / 2)) wattron(list_win, A_BOLD); else wattron(list_win, A_NORMAL); if (n->essid != NULL && n->essid->split > 0) wattron(list_win, RED); mvwprintw(list_win, line, 1, "%c", spin[n->pkt_count % 4]); mvwprintw(list_win, line, COL_PKT, "%.0f/%.0f%%", n->pkt_count * 100.0 / stats.packets, n->wlan_retries_all * 100.0 / n->pkt_count); if (n->wlan_channel) mvwprintw(list_win, line, COL_CHAN, "%3d", n->wlan_channel ); mvwprintw(list_win, line, COL_SIG, "%3d", -ewma_read(&n->phy_sig_avg)); mvwprintw(list_win, line, COL_RATE, "%3d", p->phy_rate/10); mvwprintw(list_win, line, COL_SOURCE, "%-17s", mac_name_lookup(p->wlan_src, 0)); mvwprintw(list_win, line, COL_WIDTH, "%-2s %-3s", get_80211std(n->wlan_chan_width, n->wlan_channel), (n->wlan_chan_width == CHAN_WIDTH_UNSPEC || n->wlan_chan_width == CHAN_WIDTH_20_NOHT) ? "20" : channel_width_string_short(n->wlan_chan_width, n->wlan_ht40plus)); if (n->wlan_rx_streams) wprintw(list_win, " %dx%d", n->wlan_tx_streams, n->wlan_rx_streams); wmove(list_win, line, COL_MODE-1); if (n->wlan_mode & WLAN_MODE_AP) { wprintw(list_win, " AP"); if (n->essid != NULL) ssid = n->essid->essid; } if (n->wlan_mode & WLAN_MODE_IBSS) { wprintw(list_win, " AD"); if (n->essid != NULL) ssid = n->essid->essid; } if (n->wlan_mode & WLAN_MODE_STA) { wprintw(list_win, " ST"); if (n->wlan_ap_node != NULL && n->wlan_ap_node->essid != NULL) ssid = n->wlan_ap_node->essid->essid; } if (n->wlan_mode & WLAN_MODE_PROBE) { wprintw(list_win, " PR"); ssid = p->wlan_essid; } if (n->wlan_mode & WLAN_MODE_4ADDR) { wprintw(list_win, " 4A"); } if (n->wlan_rsn && n->wlan_wpa) mvwprintw(list_win, line, COL_ENC, "WPA12"); else if (n->wlan_rsn) mvwprintw(list_win, line, COL_ENC, "WPA2"); else if (n->wlan_wpa) mvwprintw(list_win, line, COL_ENC, "WPA1"); else if (n->wlan_wep) mvwprintw(list_win, line, COL_ENC, "WEP?"); if (ssid != NULL) mvwprintw(list_win, line, COL_ESSID, "%s ", ssid); if (ssid == NULL || strlen(ssid) < 12) wmove(list_win, line, COL_INFO); if (n->pkt_types & PKT_TYPE_OLSR) wprintw(list_win, "OLSR N:%d ", n->olsr_neigh); if (n->pkt_types & PKT_TYPE_BATMAN) wprintw(list_win, "BATMAN %s", n->bat_gw ? "GW " : ""); if (n->pkt_types & (PKT_TYPE_MESHZ)) wprintw(list_win, "MC "); if (n->pkt_types & PKT_TYPE_IP) wprintw(list_win, "%s", ip_sprintf(n->ip_src)); wattroff(list_win, A_BOLD); wattroff(list_win, GREEN); wattroff(list_win, RED); } static void update_node_list_win(void) { struct node_info* n; int line = 0; werase(list_win); wattron(list_win, WHITE); box(list_win, 0 , 0); mvwprintw(list_win, 0, COL_PKT, "Pk/Re%%"); mvwprintw(list_win, 0, COL_CHAN, "Cha"); mvwprintw(list_win, 0, COL_SIG, "Sig"); mvwprintw(list_win, 0, COL_RATE, "RAT"); mvwprintw(list_win, 0, COL_SOURCE, "TRANSMITTER"); mvwprintw(list_win, 0, COL_MODE, "MODE"); mvwprintw(list_win, 0, COL_WIDTH, "ST-MHz-TxR"); mvwprintw(list_win, 0, COL_ENC, "ENCR"); mvwprintw(list_win, 0, COL_ESSID, "ESSID"); mvwprintw(list_win, 0, COL_INFO, "INFO"); /* reuse bottom line for information on other win */ mvwprintw(list_win, win_split - 1, 0, "Cha-Sig"); wprintw(list_win, "-RAT-TRANSMITTER"); mvwprintw(list_win, win_split - 1, 30, "(BSSID)"); mvwprintw(list_win, win_split - 1, 50, "TYPE"); mvwprintw(list_win, win_split - 1, 57, "INFO"); mvwprintw(list_win, win_split - 1, COLS-10, "LiveStatus"); if (sortfunc) listsort(&nodes.n, sortfunc); list_for_each(&nodes, n, list) { if (conf.filter_mode != 0 && (n->wlan_mode & conf.filter_mode) == 0) continue; line++; if (line >= win_split - 1) break; /* prevent overdraw of last line */ print_node_list_line(line, n); } if (essids.split_active > 0) { wattron(list_win, WHITEONRED); mvwhline(list_win, win_split - 2, 1, ' ', COLS - 2); print_centered(list_win, win_split - 2, COLS - 2, "*** IBSS SPLIT DETECTED!!! ESSID '%s' %d nodes ***", essids.split_essid->essid, essids.split_essid->num_nodes); wattroff(list_win, WHITEONRED); } wnoutrefresh(list_win); } void update_dump_win(struct packet_info* p) { if (!p) { redrawwin(dump_win); wnoutrefresh(dump_win); return; } wattron(dump_win, get_packet_type_color(p->wlan_type)); if (p->pkt_types & PKT_TYPE_IP) wattron(dump_win, A_BOLD); if (p->phy_flags & PHY_FLAG_BADFCS) wattron(dump_win, RED); wprintw(dump_win, "\n%3d ", p->wlan_channel); wprintw(dump_win, "%03d ", p->phy_signal); wprintw(dump_win, "%3d ", p->phy_rate/10); wprintw(dump_win, "%-17s ", mac_name_lookup(p->wlan_src, 0)); wprintw(dump_win, "(%s) ", ether_sprintf(p->wlan_bssid)); if (p->phy_flags & PHY_FLAG_BADFCS) { wprintw(dump_win, "*BADFCS* "); return; } if ((p->pkt_types & PKT_TYPE_BATMAN) && p->bat_packet_type == BAT_UNICAST) { /* unicast traffic can carry IP/ICMP which we show below */ wprintw(dump_win, "BATMAN "); } if (p->pkt_types & PKT_TYPE_OLSR) { wprintw(dump_win, "%-7s%s ", "OLSR", ip_sprintf(p->ip_src)); switch (p->olsr_type) { case HELLO_MESSAGE: wprintw(dump_win, "HELLO"); break; case TC_MESSAGE: wprintw(dump_win, "TC"); break; case MID_MESSAGE: wprintw(dump_win, "MID");break; case HNA_MESSAGE: wprintw(dump_win, "HNA"); break; case LQ_HELLO_MESSAGE: wprintw(dump_win, "LQ_HELLO"); break; case LQ_TC_MESSAGE: wprintw(dump_win, "LQ_TC"); break; default: wprintw(dump_win, "(%d)", p->olsr_type); } } else if ((p->pkt_types & PKT_TYPE_BATMAN) && p->bat_packet_type != BAT_UNICAST) { wprintw(dump_win, "BATMAN "); switch (p->bat_packet_type) { case BAT_OGM: wprintw(dump_win, "OGM"); break; case BAT_ICMP: wprintw(dump_win, "BAT_ICMP"); break; case BAT_BCAST: wprintw(dump_win, "BCAST"); break; case BAT_VIS: wprintw(dump_win, "VIS"); break; case BAT_UNICAST_FRAG: wprintw(dump_win, "FRAG"); break; case BAT_TT_QUERY: wprintw(dump_win, "TT_QUERY"); break; case BAT_ROAM_ADV: wprintw(dump_win, "ROAM_ADV"); break; default: wprintw(dump_win, "UNKNOWN %d", p->bat_packet_type); } } else if (p->pkt_types & PKT_TYPE_MESHZ) { wprintw(dump_win, "%-7s%s", p->tcpudp_port == 9256 ? "MC_NBR" : "MC_RT", ip_sprintf(p->ip_src)); wprintw(dump_win, " -> %s", ip_sprintf(p->ip_dst)); } else if (p->pkt_types & PKT_TYPE_UDP) { wprintw(dump_win, "%-7s%s", "UDP", ip_sprintf(p->ip_src)); wprintw(dump_win, " -> %s", ip_sprintf(p->ip_dst)); } else if (p->pkt_types & PKT_TYPE_TCP) { wprintw(dump_win, "%-7s%s", "TCP", ip_sprintf(p->ip_src)); wprintw(dump_win, " -> %s", ip_sprintf(p->ip_dst)); } else if (p->pkt_types & PKT_TYPE_ICMP) { wprintw(dump_win, "%-7s%s", "PING", ip_sprintf(p->ip_src)); wprintw(dump_win, " -> %s", ip_sprintf(p->ip_dst)); } else if (p->pkt_types & PKT_TYPE_IP) { wprintw(dump_win, "%-7s%s", "IP", ip_sprintf(p->ip_src)); wprintw(dump_win, " -> %s", ip_sprintf(p->ip_dst)); } else if (p->pkt_types & PKT_TYPE_ARP) { wprintw(dump_win, "%-7s", "ARP", ip_sprintf(p->ip_src)); } else { wprintw(dump_win, "%-7s", get_packet_type_name(p->wlan_type)); switch (p->wlan_type) { case WLAN_FRAME_DATA: case WLAN_FRAME_DATA_CF_ACK: case WLAN_FRAME_DATA_CF_POLL: case WLAN_FRAME_DATA_CF_ACKPOLL: case WLAN_FRAME_QDATA: case WLAN_FRAME_QDATA_CF_ACK: case WLAN_FRAME_QDATA_CF_POLL: case WLAN_FRAME_QDATA_CF_ACKPOLL: if ( p->wlan_wep == 1) wprintw(dump_win, "ENCRYPTED"); break; case WLAN_FRAME_CTS: case WLAN_FRAME_RTS: case WLAN_FRAME_ACK: case WLAN_FRAME_BLKACK: case WLAN_FRAME_BLKACK_REQ: wprintw(dump_win, "%-17s", mac_name_lookup(p->wlan_dst, 0)); break; case WLAN_FRAME_BEACON: case WLAN_FRAME_PROBE_RESP: wprintw(dump_win, "'%s' %llx", p->wlan_essid, p->wlan_tsf); break; case WLAN_FRAME_PROBE_REQ: wprintw(dump_win, "'%s'", p->wlan_essid); break; } } if (p->wlan_retry) wprintw(dump_win, " [r]"); wattroff(dump_win, A_BOLD); } void update_main_win(struct packet_info *p) { update_node_list_win(); update_status_win(p); update_dump_win(p); wnoutrefresh(dump_win); if (sort_win != NULL) { redrawwin(sort_win); wnoutrefresh(sort_win); } } bool main_input(int key) { if (sort_win != NULL) return sort_input(key); switch(key) { case 'o': case 'O': show_sort_win(); return true; } return false; } void init_display_main(void) { win_split = LINES / 2 + 1; stat_height = LINES - win_split - 1; list_win = newwin(win_split, COLS, 0, 0); scrollok(list_win, FALSE); stat_win = newwin(stat_height, STAT_WIDTH, win_split, COLS - STAT_WIDTH); scrollok(stat_win, FALSE); dump_win = newwin(stat_height, COLS - STAT_WIDTH, win_split, 0); scrollok(dump_win, TRUE); ewma_init(&usen_avg, 1024, 8); ewma_init(&bpsn_avg, 1024, 8); } void resize_display_main(void) { win_split = LINES / 2 + 1; stat_height = LINES - win_split - 1; wresize(list_win, win_split, COLS); wresize(dump_win, stat_height, COLS - STAT_WIDTH); mvwin(dump_win, win_split, 0); wresize(stat_win, stat_height, STAT_WIDTH); mvwin(stat_win, win_split, COLS - STAT_WIDTH); } void clear_display_main(void) { werase(dump_win); werase(stat_win); } horst-version-5.0/display-spectrum.c000066400000000000000000000105631273543766500177000ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* POOR MAN's "SPECTRUM ANALYZER" *******************/ #include #include "display.h" #include "main.h" #include "util.h" #define CH_SPACE 6 #define SPEC_POS_Y 1 #define SPEC_HEIGHT (LINES - SPEC_POS_X - 2) #define SPEC_POS_X 6 static unsigned int show_nodes; void update_spectrum_win(WINDOW *win) { int i, sig, siga, use, usen, usean, nnodes; struct chan_node *cn; const char *id; werase(win); wattron(win, WHITE); box(win, 0 , 0); print_centered(win, 0, COLS, " \"Spectrum Analyzer\" "); mvwhline(win, SPEC_HEIGHT + 1, 1, ACS_HLINE, COLS - 2); mvwvline(win, SPEC_POS_Y, 4, ACS_VLINE, LINES - SPEC_POS_Y - 2); mvwprintw(win, SPEC_HEIGHT + 2, 1, "CHA"); wattron(win, BLUE); mvwprintw(win, SPEC_HEIGHT + 4, 1, "Nod"); wattron(win, YELLOW); mvwprintw(win, SPEC_HEIGHT + 5, 1, "Use"); for(i = 80; i > 0; i -= 20) { sig = normalize(i, 100, SPEC_HEIGHT); mvwprintw(win, SPEC_POS_Y + sig, 1, "%d%%", 100-i); } wattron(win, GREEN); mvwprintw(win, SPEC_HEIGHT + 3, 1, "Sig"); mvwprintw(win, SPEC_POS_Y + 1, 1, "dBm"); for(i = -30; i > -100; i -= 10) { sig = normalize_db(-i, SPEC_HEIGHT); mvwprintw(win, SPEC_POS_Y + sig, 1, "%d", i); } wattroff(win, GREEN); for (i = 0; i < channel_get_num_channels() && SPEC_POS_X + CH_SPACE*i+4 < COLS; i++) { mvwprintw(win, SPEC_HEIGHT + 2, SPEC_POS_X + CH_SPACE*i, "%02d", channel_get_chan(i)); wattron(win, GREEN); mvwprintw(win, SPEC_HEIGHT + 3, SPEC_POS_X + CH_SPACE*i, "%d", spectrum[i].signal); wattron(win, BLUE); mvwprintw(win, SPEC_HEIGHT + 4, SPEC_POS_X + CH_SPACE*i, "%d", spectrum[i].num_nodes); if (spectrum[i].signal != 0) { sig = normalize_db(-spectrum[i].signal, SPEC_HEIGHT); if (spectrum[i].packets > 8) siga = normalize_db( ewma_read(&spectrum[i].signal_avg), SPEC_HEIGHT); else siga = sig; signal_average_bar(win, sig, siga, SPEC_POS_Y, SPEC_POS_X + CH_SPACE*i, SPEC_HEIGHT, show_nodes ? 1 : 2); } /* usage in percent */ use = (spectrum[i].durations_last * 100.0) / conf.channel_time; wattron(win, YELLOW); mvwprintw(win, SPEC_HEIGHT + 5, SPEC_POS_X + CH_SPACE*i, "%d", use); wattroff(win, YELLOW); if (show_nodes) { wattron(win, BLUE); list_for_each(&spectrum[i].nodes, cn, chan_list) { if (cn->packets >= 8) sig = normalize_db(ewma_read(&cn->sig_avg), SPEC_HEIGHT); else sig = normalize_db(-cn->sig, SPEC_HEIGHT); if (cn->node->ip_src) { wattron(win, A_BOLD); id = ip_sprintf_short(cn->node->ip_src); } else id = mac_name_lookup(cn->node->last_pkt.wlan_src, 1); mvwprintw(win, SPEC_POS_Y + sig, SPEC_POS_X + CH_SPACE*i + 1, "%s", id); if (cn->node->ip_src) wattroff(win, A_BOLD); } wattroff(win, BLUE); } else { nnodes = spectrum[i].num_nodes; if (nnodes > SPEC_HEIGHT) nnodes = SPEC_HEIGHT; wattron(win, ALLBLUE); mvwvline(win, SPEC_POS_Y + SPEC_HEIGHT - nnodes, SPEC_POS_X + CH_SPACE*i + 2, ACS_BLOCK, nnodes); wattroff(win, ALLBLUE); usen = normalize(use, 100, SPEC_HEIGHT); use = (ewma_read(&spectrum[i].durations_avg) * 100.0) / conf.channel_time; usean = normalize(use, 100, SPEC_HEIGHT); general_average_bar(win, usen, usean, SPEC_POS_Y + SPEC_HEIGHT, SPEC_POS_X + CH_SPACE*i + 3, 1, YELLOW, ALLYELLOW); } } wnoutrefresh(win); } bool spectrum_input(WINDOW *win, int c) { switch (c) { case 'n': case 'N': show_nodes = show_nodes ? 0 : 1; break; default: return false; /* didn't handle input */ } update_spectrum_win(win); return true; } horst-version-5.0/display-statistics.c000066400000000000000000000122571273543766500202320ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /******************* STATISTICS *******************/ #include #include "display.h" #include "main.h" #include "util.h" #include "wlan_util.h" #define STAT_PACK_POS 9 #define STAT_BYTE_POS (STAT_PACK_POS + 9) #define STAT_BPP_POS (STAT_BYTE_POS + 9) #define STAT_PP_POS (STAT_BPP_POS + 6) #define STAT_BP_POS (STAT_PP_POS + 6) #define STAT_AIR_POS (STAT_BP_POS + 6) #define STAT_AIRG_POS (STAT_AIR_POS + 6) void update_statistics_win(WINDOW *win) { int i; int line; int bps, dps, pps, rps; float duration; werase(win); wattron(win, WHITE); box(win, 0 , 0); print_centered(win, 0, COLS, " Packet Statistics "); if (stats.packets == 0) { wnoutrefresh(win); return; /* avoid floating point exceptions */ } mvwprintw(win, 2, 2, "Packets: %d", stats.packets ); mvwprintw(win, 3, 2, "Bytes: %s (%d)", kilo_mega_ize(stats.bytes), stats.bytes ); mvwprintw(win, 4, 2, "Average: ~%d B/Pkt", stats.bytes / stats.packets); mvwprintw(win, 2, 40, "Retries: %3.1f%% (%d)", stats.retries * 100.0 / stats.packets, stats.retries); get_per_second(stats.bytes, stats.duration, stats.packets, stats.retries, &bps, &dps, &pps, &rps); bps = bps * 8; mvwprintw(win, 3, 40, "Total bit/sec: %s (%d)", kilo_mega_ize(bps), bps); wattron(win, A_BOLD); mvwprintw(win, 4, 40, "Total Usage: %3.1f%% (%d)", dps * 1.0 / 10000, dps ); /* usec in % */ wattroff(win, A_BOLD); line = 6; mvwprintw(win, line, STAT_PACK_POS, " Packets"); mvwprintw(win, line, STAT_BYTE_POS, " Bytes"); mvwprintw(win, line, STAT_BPP_POS, "~B/P"); mvwprintw(win, line, STAT_PP_POS, "Pkts%%"); mvwprintw(win, line, STAT_BP_POS, "Byte%%"); wattron(win, A_BOLD); mvwprintw(win, line, STAT_AIR_POS, "Usage%%"); mvwprintw(win, line++, 2, "RATE"); wattroff(win, A_BOLD); mvwhline(win, line++, 2, '-', COLS-4); for (i = 1; i < MAX_RATES && line < LINES - 2; i++) { if (stats.packets_per_rate[i] > 0) { wattron(win, A_BOLD); if (i <= 12) mvwprintw(win, line, 2, "%3dM", rate_index_to_rate(i)/10); else mvwprintw(win, line, 2, "MCS%d", i - 12); wattroff(win, A_BOLD); mvwprintw(win, line, STAT_PACK_POS, "%8d", stats.packets_per_rate[i]); mvwprintw(win, line, STAT_BYTE_POS, "%8s", kilo_mega_ize(stats.bytes_per_rate[i])); mvwprintw(win, line, STAT_BPP_POS, "%4d", stats.bytes_per_rate[i] / stats.packets_per_rate[i]); mvwprintw(win, line, STAT_PP_POS, "%2.1f", stats.packets_per_rate[i] * 100.0 / stats.packets); mvwprintw(win, line, STAT_BP_POS, "%2.1f", stats.bytes_per_rate[i] * 100.0 / stats.bytes); wattron(win, A_BOLD); duration = stats.duration_per_rate[i] * 100.0 / stats.duration; mvwprintw(win, line, STAT_AIR_POS, "%2.1f", duration); mvwhline(win, line, STAT_AIRG_POS, '*', normalize(duration, 100, COLS - STAT_AIRG_POS - 2)); wattroff(win, A_BOLD); line++; } } line++; if (line < LINES - 2) { mvwprintw(win, line, STAT_PACK_POS, " Packets"); mvwprintw(win, line, STAT_BYTE_POS, " Bytes"); mvwprintw(win, line, STAT_BPP_POS, "~B/P"); mvwprintw(win, line, STAT_PP_POS, "Pkts%%"); mvwprintw(win, line, STAT_BP_POS, "Byte%%"); wattron(win, A_BOLD); mvwprintw(win, line, STAT_AIR_POS, "Usage%%"); mvwprintw(win, line++, 2, "TYPE"); wattroff(win, A_BOLD); } if (line < LINES - 2) mvwhline(win, line++, 2, '-', COLS - 4); for (i = 0; i < MAX_FSTYPE && line < LINES - 2; i++) { if (stats.packets_per_type[i] > 0) { wattron(win, get_packet_type_color(i)); wattron(win, A_BOLD); mvwprintw(win, line, 2, "%s", get_packet_type_name(i)); wattroff(win, A_BOLD); mvwprintw(win, line, STAT_PACK_POS, "%8d", stats.packets_per_type[i]); mvwprintw(win, line, STAT_BYTE_POS, "%8s", kilo_mega_ize(stats.bytes_per_type[i])); mvwprintw(win, line, STAT_BPP_POS, "%4d", stats.bytes_per_type[i] / stats.packets_per_type[i]); mvwprintw(win, line, STAT_PP_POS, "%2.1f", stats.packets_per_type[i] * 100.0 / stats.packets); mvwprintw(win, line, STAT_BP_POS, "%2.1f", stats.bytes_per_type[i] * 100.0 / stats.bytes); wattron(win, A_BOLD); if (stats.duration > 0) duration = stats.duration_per_type[i] * 100.0 / stats.duration; else duration = 100.0; mvwprintw(win, line, STAT_AIR_POS, "%2.1f", duration); mvwhline(win, line, STAT_AIRG_POS, '*', normalize(duration, 100, COLS - STAT_AIRG_POS - 2)); wattroff(win, A_BOLD); line++; } } wnoutrefresh(win); } horst-version-5.0/display.c000066400000000000000000000275031273543766500160420ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "util.h" #include "display.h" #include "main.h" #include "wlan80211.h" #include "channel.h" static WINDOW *conf_win = NULL; static WINDOW *show_win = NULL; static int conf_win_current; static int show_win_current; static struct timespec last_time; static int display_resize_needed = 0; /******************* HELPERS *******************/ void get_per_second(unsigned long bytes, unsigned long duration, unsigned long packets, unsigned long retries, int *bps, int *dps, int *pps, int *rps) { static struct timespec last; static unsigned long last_bytes, last_dur, last_pkts, last_retr; static int last_bps, last_dps, last_pps, last_rps; float timediff; /* reacalculate only every second or more */ timediff = (the_time.tv_sec + the_time.tv_nsec/1000000000.0) - (last.tv_sec + last.tv_nsec/1000000000.0); if (timediff >= 1.0) { last_dps = (1.0*(duration - last_dur)) / timediff; last_bps = (1.0*(bytes - last_bytes)) / timediff; last_pps = (1.0*(packets - last_pkts)) / timediff; last_rps = (1.0*(retries - last_retr)) / timediff; last = the_time; last_dur = duration; last_bytes = bytes; last_pkts = packets; last_retr = retries; } *bps = last_bps; *dps = last_dps; *pps = last_pps; *rps = last_rps; } void __attribute__ ((format (printf, 4, 5))) print_centered(WINDOW* win, int line, int cols, const char *fmt, ...) { char* buf; va_list ap; buf = malloc(cols); if (buf == NULL) return; va_start(ap, fmt); vsnprintf(buf, cols, fmt, ap); va_end(ap); mvwprintw(win, line, cols / 2 - strlen(buf) / 2, buf); free(buf); } int get_packet_type_color(int type) { if (type == 1) /* special case for bad FCS */ return RED; switch (WLAN_FRAME_TYPE(type)) { case WLAN_FRAME_TYPE_DATA: return BLUE; case WLAN_FRAME_TYPE_CTRL: return WHITE; case WLAN_FRAME_TYPE_MGMT: return CYAN; } return YELLOW; } void signal_average_bar(WINDOW *win, int val, int avg, int y, int x, int height, int width) { int i; if (avg <= val) { wattron(win, ALLGREEN); for (i = 0; i < width; i++) mvwvline(win, y + avg, x + i, ACS_BLOCK, val - avg); wattron(win, A_BOLD); for (i = 0; i < width; i++) mvwvline(win, y + val, x + i, '=', height - val); } else { wattron(win, GREEN); wattron(win, A_BOLD); for (i = 0; i < width; i++) mvwvline(win, y + val, x + i, '=', avg - val); wattron(win, ALLGREEN); for (i = 0; i < width; i++) mvwvline(win, y + avg, x + i, '=', height - avg); } wattroff(win, A_BOLD); wattroff(win, ALLGREEN); } void general_average_bar(WINDOW *win, int val, int avg, int y, int x, int width, short color, short color_avg) { int i; if (avg >= val) { wattron(win, color_avg); for (i = 0; i < width; i++) mvwvline(win, y - avg, x + i, ACS_BLOCK, avg - val); wattron(win, A_BOLD); for (i = 0; i < width; i++) mvwvline(win, y - val, x + i, '=', val); } else { wattron(win, color); wattron(win, A_BOLD); for (i = 0; i < width; i++) mvwvline(win, y - val, x + i, '=', val - avg); wattron(win, color_avg); for (i = 0; i < width; i++) mvwvline(win, y - avg, x + i, '=', avg); } wattroff(win, A_BOLD); wattroff(win, color_avg); } /******************* RESIZE *******************/ static void resize_display_all(void) { struct winsize winsz; /* get new window size */ winsz.ws_col = winsz.ws_row = 0; ioctl(0, TIOCGWINSZ, &winsz); /* ioctl on STDIN */ if (winsz.ws_col && winsz.ws_row) resizeterm(winsz.ws_row, winsz.ws_col); COLS = winsz.ws_col; LINES = winsz.ws_row; resize_display_main(); if (show_win) wresize(show_win, LINES-1, COLS); if (conf_win) { if (conf_win_current == 'f') mvwin(conf_win, LINES/2-12, COLS/2-28); else if (conf_win_current == 'c') mvwin(conf_win, LINES/2-5, COLS/2-20); } } static void window_change_handler(__attribute__((unused)) int sig) { display_resize_needed = 1; } /******************* STATUS *******************/ static void update_clock(time_t* sec) { static char buf[9]; strftime(buf, 9, "%H:%M:%S", localtime(sec)); wattron(stdscr, BLACKONWHITE); mvwprintw(stdscr, LINES-1, COLS-9, "|%s", buf); wattroff(stdscr, BLACKONWHITE); wnoutrefresh(stdscr); } static void update_mini_status(void) { wattron(stdscr, BLACKONWHITE); mvwprintw(stdscr, LINES-1, COLS-31, conf.paused ? "|=" : "|>"); if (!conf.filter_off && (conf.do_macfilter || conf.filter_pkt != PKT_TYPE_ALL || conf.filter_mode != WLAN_MODE_ALL)) mvwprintw(stdscr, LINES-1, COLS-29, "|F"); else mvwprintw(stdscr, LINES-1, COLS-29, "| "); mvwprintw(stdscr, LINES-1, COLS-27, "|Ch%03d@%s", channel_get_chan(conf.channel_idx), channel_width_string_short(conf.channel_width, conf.channel_ht40plus)); wattroff(stdscr, BLACKONWHITE); wnoutrefresh(stdscr); } static void update_menu(void) { wattron(stdscr, BLACKONWHITE); mvwhline(stdscr, LINES-1, 0, ' ', COLS); #define KEYMARK A_UNDERLINE attron(KEYMARK); printw("Q"); attroff(KEYMARK); printw("uit "); attron(KEYMARK); printw("P"); attroff(KEYMARK); printw("ause "); attron(KEYMARK); printw("R"); attroff(KEYMARK); printw("eset "); attron(KEYMARK); printw("H"); attroff(KEYMARK); printw("ist "); attron(KEYMARK); printw("E"); attroff(KEYMARK); printw("SSID St"); attron(KEYMARK); printw("a"); attroff(KEYMARK); printw("ts "); attron(KEYMARK); printw("S"); attroff(KEYMARK); printw("pec "); attron(KEYMARK); printw("F"); attroff(KEYMARK); printw("ilt "); attron(KEYMARK); printw("C"); attroff(KEYMARK); printw("han "); attron(KEYMARK); printw("?"); attroff(KEYMARK); printw(" "); if (show_win == NULL) { printw("s"); attron(KEYMARK); printw("O"); attroff(KEYMARK); printw("rt"); } if (show_win != NULL && show_win_current == 's') { attron(KEYMARK); printw("N"); attroff(KEYMARK); printw("odes"); } #undef KEYMARK mvwprintw(stdscr, LINES-1, COLS-17, "|%7s", conf.serveraddr[0] != '\0' ? conf.serveraddr : conf.ifname); wattroff(stdscr, BLACKONWHITE); update_mini_status(); update_clock(&the_time.tv_sec); } /******************* WINDOW MANAGEMENT / UPDATE *******************/ static void update_show_win(void) { if (show_win_current == 'e') update_essid_win(show_win); else if (show_win_current == 'h') update_history_win(show_win); else if (show_win_current == 'a') update_statistics_win(show_win); else if (show_win_current == 's') update_spectrum_win(show_win); else if (show_win_current == '?') update_help_win(show_win); } static void show_window(int which) { if (show_win != NULL && show_win_current == which) { delwin(show_win); show_win = NULL; show_win_current = 0; update_menu(); return; } if (show_win == NULL) { show_win = newwin(LINES-1, COLS, 0, 0); scrollok(show_win, FALSE); } show_win_current = which; update_show_win(); update_menu(); } static void show_conf_window(int key) { if (conf_win != NULL && (conf_win_current == key || key == '\r' || key == KEY_ENTER || key == 27)) { delwin(conf_win); conf_win = NULL; conf_win_current = 0; return; } if (conf_win == NULL) { if (key == 'f') { conf_win = newwin(FILTER_WIN_HEIGHT, FILTER_WIN_WIDTH, LINES/2-FILTER_WIN_HEIGHT/2, COLS/2-FILTER_WIN_WIDTH/2); update_filter_win(conf_win); } else if (key == 'c') { conf_win = newwin(CHANNEL_WIN_HEIGHT, CHANNEL_WIN_WIDTH, LINES/2-CHANNEL_WIN_HEIGHT/2, COLS/2-CHANNEL_WIN_WIDTH/2); update_channel_win(conf_win); } scrollok(conf_win, FALSE); conf_win_current = key; } } void update_display_clock(void) { /* helper to update just the clock every second */ if (the_time.tv_sec > last_time.tv_sec) { update_clock(&the_time.tv_sec); doupdate(); } } void display_log(const char *string) { print_dump_win(string, show_win == NULL); } void update_display(struct packet_info* pkt) { /* * update only in specific intervals to save CPU time * if pkt is NULL we want to force an update */ if (pkt != NULL && the_time.tv_sec == last_time.tv_sec && (the_time.tv_nsec - last_time.tv_nsec) / 1000 < conf.display_interval ) { /* just add the line to dump win so we don't loose it */ update_dump_win(pkt); return; } if (display_resize_needed == 1) { resize_display_all(); display_resize_needed = 0; } update_menu(); /* update clock every second */ if (the_time.tv_sec > last_time.tv_sec) update_clock(&the_time.tv_sec); last_time = the_time; if (show_win != NULL) update_show_win(); else update_main_win(pkt); if (conf_win != NULL) { redrawwin(conf_win); wnoutrefresh(conf_win); } /* only one redraw */ doupdate(); } /******************* INPUT *******************/ void handle_user_input(void) { int key; key = getch(); /* if windows are active pass the input to them first. if they handle * it they will return 1. if not we handle the input below */ if (conf_win != NULL) { if (conf_win_current == 'f') if (filter_input(conf_win, key)) return; if (conf_win_current == 'c') if (channel_input(conf_win, key)) return; } if (show_win != NULL && show_win_current == 's') if (spectrum_input(show_win, key)) return; if (show_win == NULL) { if (main_input(key)) return; } switch(key) { case ' ': case 'p': case 'P': main_pause(conf.paused = conf.paused ? 0 : 1); break; case 'q': case 'Q': exit(0); case 'r': case 'R': main_reset(); break; /* big windows */ case '?': case 'e': case 'E': case 'h': case 'H': case 'a': case 'A': case 's': case 'S': show_window(tolower(key)); break; /* config windows */ case 'f': case 'F': case 'c': case 'C': case '\r': case KEY_ENTER: /* used to close win */ case 27 : /* ESC */ show_conf_window(tolower(key)); break; } update_display(NULL); } /******************* INIT *******************/ void init_display(void) { initscr(); start_color(); /* Start the color functionality */ use_default_colors(); keypad(stdscr, TRUE); nonl(); /* tell curses not to do NL->CR/NL on output */ cbreak(); /* take input chars one at a time, no wait for \n */ curs_set(0); /* don't show cursor */ noecho(); nodelay(stdscr, TRUE); ESCDELAY = 25; /* we don't use ESC sequences */ init_pair(1, COLOR_WHITE, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_RED, COLOR_BLACK); init_pair(4, COLOR_CYAN, COLOR_BLACK); init_pair(5, COLOR_BLUE, COLOR_BLACK); init_pair(6, COLOR_BLACK, COLOR_WHITE); init_pair(7, COLOR_MAGENTA, COLOR_BLACK); init_pair(8, COLOR_GREEN, COLOR_GREEN); init_pair(9, COLOR_RED, COLOR_RED); init_pair(10, COLOR_BLUE, COLOR_BLUE); init_pair(11, COLOR_CYAN, COLOR_CYAN); init_pair(12, COLOR_YELLOW, COLOR_BLACK); init_pair(13, COLOR_YELLOW, COLOR_YELLOW); init_pair(14, COLOR_WHITE, COLOR_RED); /* COLOR_BLACK COLOR_RED COLOR_GREEN COLOR_YELLOW COLOR_BLUE COLOR_MAGENTA COLOR_CYAN COLOR_WHITE */ erase(); init_display_main(); if (conf.display_view != 0) show_window(conf.display_view); update_menu(); update_display(NULL); signal(SIGWINCH, window_change_handler); conf.display_initialized = 1; } void finish_display(void) { endwin(); } void display_clear(void) { clear_display_main(); } horst-version-5.0/display.h000066400000000000000000000057221273543766500160460ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _DISPLAY_H_ #define _DISPLAY_H_ #include #define WHITE COLOR_PAIR(1) #define GREEN COLOR_PAIR(2) #define RED COLOR_PAIR(3) #define CYAN COLOR_PAIR(4) #define BLUE COLOR_PAIR(5) #define BLACKONWHITE COLOR_PAIR(6) #define MAGENTA COLOR_PAIR(7) #define ALLGREEN COLOR_PAIR(8) #define ALLRED COLOR_PAIR(9) #define ALLBLUE COLOR_PAIR(10) #define ALLCYAN COLOR_PAIR(11) #define YELLOW COLOR_PAIR(12) #define ALLYELLOW COLOR_PAIR(13) #define WHITEONRED COLOR_PAIR(14) #define CHECKED(_exp) (_exp) ? '*' : ' ' #define FILTER_WIN_WIDTH 56 #define FILTER_WIN_HEIGHT 35 #define CHANNEL_WIN_WIDTH 41 #define CHANNEL_WIN_HEIGHT 31 struct packet_info; struct node_info; void get_per_second(unsigned long bytes, unsigned long duration, unsigned long packets, unsigned long retries, int *bps, int *dps, int *pps, int *rps); void __attribute__ ((format (printf, 4, 5))) print_centered(WINDOW* win, int line, int cols, const char *fmt, ...); int get_packet_type_color(int type); void signal_average_bar(WINDOW *win, int sig, int siga, int y, int x, int height, int width); void general_average_bar(WINDOW *win, int val, int avg, int y, int x, int width, short color, short color_avg); void update_display(struct packet_info* pkg); void update_display_clock(void); void display_log(const char *string); void handle_user_input(void); void init_display(void); void finish_display(void); void display_clear(void); /* main windows are special */ void init_display_main(void); void clear_display_main(void); void update_main_win(struct packet_info *pkt); void update_dump_win(struct packet_info* pkt); bool main_input(int c); void print_dump_win(const char *str, int refresh); void resize_display_main(void); /* smaller config windows */ void update_filter_win(WINDOW *win); void update_channel_win(WINDOW *win); bool filter_input(WINDOW *win, int c); bool channel_input(WINDOW *win, int c); /* "standard" windows */ void update_spectrum_win(WINDOW *win); void update_statistics_win(WINDOW *win); void update_essid_win(WINDOW *win); void update_history_win(WINDOW *win); void update_help_win(WINDOW *win); bool spectrum_input(WINDOW *win, int c); #endif horst-version-5.0/essid.c000066400000000000000000000072321273543766500155010ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "main.h" #include "util.h" #include "wlan80211.h" #include "essid.h" static void update_essid_split_status(struct essid_info* e) { struct node_info* n; unsigned char* last_bssid = NULL; e->split = 0; /* essid can't be split if it only contains 1 node */ if (e->num_nodes <= 1 && essids.split_essid == e) { essids.split_active = 0; essids.split_essid = NULL; return; } /* check for split */ list_for_each(&e->nodes, n, essid_nodes) { DEBUG("SPLIT node %p src %s", n, ether_sprintf(n->last_pkt.wlan_src)); DEBUG(" bssid %s\n", ether_sprintf(n->wlan_bssid)); if (n->wlan_mode & WLAN_MODE_AP) continue; if (last_bssid && memcmp(last_bssid, n->wlan_bssid, MAC_LEN) != 0) { e->split = 1; DEBUG("SPLIT *** DETECTED!!!\n"); } last_bssid = n->wlan_bssid; } /* if a split occurred on this essid, record it */ if (e->split > 0) { DEBUG("SPLIT *** active\n"); essids.split_active = 1; essids.split_essid = e; } else if (e == essids.split_essid) { DEBUG("SPLIT *** ok now\n"); essids.split_active = 0; essids.split_essid = NULL; } } void remove_node_from_essid(struct node_info* n) { DEBUG("SPLIT remove node from old essid\n"); list_del(&n->essid_nodes); n->essid->num_nodes--; update_essid_split_status(n->essid); /* delete essid if it has no more nodes */ if (n->essid->num_nodes == 0) { DEBUG("SPLIT essid empty, delete\n"); list_del(&n->essid->list); free(n->essid); } n->essid = NULL; } void update_essids(struct packet_info* p, struct node_info* n) { struct essid_info* e; if (n == NULL || (p->phy_flags & PHY_FLAG_BADFCS)) return; /* ignore */ /* only check beacons and probe response frames */ if ((p->wlan_type != WLAN_FRAME_BEACON) && (p->wlan_type != WLAN_FRAME_PROBE_RESP)) return; DEBUG("SPLIT check ibss '%s' node %s ", p->wlan_essid, ether_sprintf(p->wlan_src)); DEBUG("bssid %s\n", ether_sprintf(p->wlan_bssid)); /* find essid if already recorded */ list_for_each(&essids.list, e, list) { if (strncmp(e->essid, p->wlan_essid, WLAN_MAX_SSID_LEN) == 0) { DEBUG("SPLIT essid found\n"); break; } } /* if not add new essid */ if (&e->list == &essids.list.n) { DEBUG("SPLIT essid not found, adding new\n"); e = malloc(sizeof(struct essid_info)); strncpy(e->essid, p->wlan_essid, WLAN_MAX_SSID_LEN); e->essid[WLAN_MAX_SSID_LEN-1] = '\0'; e->num_nodes = 0; e->split = 0; list_head_init(&e->nodes); list_add_tail(&essids.list, &e->list); } /* if node had another essid before, remove it there */ if (n->essid != NULL && n->essid != e) remove_node_from_essid(n); /* new node */ if (n->essid == NULL) { DEBUG("SPLIT node not found, adding new %s\n", ether_sprintf(p->wlan_src)); list_add_tail(&e->nodes, &n->essid_nodes); e->num_nodes++; n->essid = e; } update_essid_split_status(e); } horst-version-5.0/essid.h000066400000000000000000000017441273543766500155100ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ESSID_H_ #define _ESSID_H_ void remove_node_from_essid(struct node_info* n); void update_essids(struct packet_info* p, struct node_info* n); #endif horst-version-5.0/horst.1000066400000000000000000000362121273543766500154470ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH HORST 8 "July 22, 2015" .\" Please adjust this date whenever revising the manpage. .SH NAME horst \- Highly Optimized Radio Scanning Tool .SH SYNOPSIS .B horst .RB [\| \-v \|] .RB [\| \-h \|] .RB [\| \-q \|] .RB [\| \-D \|] .RB [\| \-a \|] .RB [\| \-c .IR file \|] .RB [\| \-C .IR channel \|] .RB [\| \-i .IR interface \|] .RB [\| \-t .IR sec \|] .RB [\| \-V .IR view \|] .RB [\| \-d .IR ms \|] .RB [\| \-b .IR bytes \|] .RB [\| \-M .IR file \|] .RB [\| \-s \|] .RB [\| \-u \|] .RB [\| \-N \|] .RB [\| \-n .IR IP \|] .RB [\| \-p .IR port \|] .RB [\| \-o .IR file \|] .RB [\| \-X .IR name \|] .RB [\| \-x .IR command \|] .RB [\| \-e .IR mac \|] .RB [\| \-f .IR pkt_name \|] .RB [\| \-m .IR mode \|] .RB [\| \-B .IR BSSID \|] .SH DESCRIPTION \fBhorst\fP is a small, lightweight IEEE802.11 wireless LAN analyzer with a text interface. Its basic function is similar to tcpdump, Wireshark or Kismet, but it's much smaller and shows different, aggregated information which is not easily available from other tools. It is mainly targeted at debugging wireless LANs with a focus on ad\-hoc (IBSS) mode in larger mesh networks. It can be useful to get a quick overview of what's going on on all wireless LAN channels and to identify problems. .IP \[bu] 2 Shows signal values per station. .IP \[bu] 2 Calculates channel utilization ("usage") by adding up the amount of time the packets actually occupy the medium. .IP \[bu] 2 "Spectrum Analyzer" shows signal levels and usage per channel. .IP \[bu] 2 Text-based "graphical" packet history, with signal, packet type and physical rate .IP \[bu] 2 Shows all stations per ESSID and the live TSF per node as it is counting. .IP \[bu] 2 Detects IBSS "splits" (same ESSID but different BSSID \- this is a common driver problem). .IP \[bu] 2 Statistics of packets/bytes per physical rate and per packet type. .IP \[bu] 2 Has some support for mesh protocols (OLSR and batman). .IP \[bu] 2 Can filter specific packet types, source MAC addresses or BSSIDs. .IP \[bu] 2 Client/server support for monitoring on remote nodes. .IP \[bu] 2 Can be controlled via a named pipe. .TP See MONITOR MODE below for more information about the network interface setup. .SH OPTIONS .TP .BI \-v Show version. .TP .BI \-h Show summary of options. .TP .BI \-q Quiet mode. Don't show user interface. This is only useful in conjunction when running in server mode (\-C) or writing to a file (\-o). .TP .BI \-D Show lot's of debugging output, including a full package dump. Only available when compiled with DEBUG=1. .TP .BI \-a Always add virtual monitor interface. Don't try to set existing interface to monitor mode. .TP .BI \-c\ configfile Use configfile instead of the default "/etc/horst.conf". .TP .BI \-C\ channel Set inital channel (number not frequency). .TP .BI \-i\ intf Operate on the given network interface instead of the default "wlan0". .TP .BI \-t\ sec Timeout (remove) nodes after not receiving packets from them for this time in seconds (default: 60 sec). .TP .BI \-V\ view Display 'view'. Valid view names are "history", "hist", "essid", "statistics", "stats", "spectrum", "spec". .TP .BI \-d\ ms Display update interval. The default value of 100ms can be increased to reduce CPU load caused by redrawing the screen. .TP .BI \-b\ bytes Receive buffer size. The receive buffer size can be set to tune memory consumption and reduce lost packets under load. .TP .BI \-M\ filename MAC address to host name mapping file. The file can either be a dhcp.leases file from dnsmasq or contain mappings in the form "MACname" (e.g.: "00:01:02:03:04:05 test") line by line (default filename: /tmp/dhcp.leases). .TP .BI \-s Show a poor mans "spectrum analyzer". The same can be achieved by running \fBhorst\fP as normal and pressing the button 's' (Spec); then 'c' (Chan) and 'a' (Automatically change channel). .TP .BI \-u Upper channel limit for the automatic channel change. .TP .BI \-N Allow client connections. Server mode. Only one client connection is supported at the moment (default: off). .TP .BI \-n\ IP Connect to a \fBhorst\fP instance running in server-mode at the specified IP address. .TP .BI \-p\ port Use the specified port (default: 4444) for client/server connections. .TP .BI \-o\ filename Write a information about each received packet into file. Note that you can send to STDOUT by using \fB-o /dev/stdout\fP. See OUTPUT FILE FORMAT below. .TP .BI \-X Accept control commands on a named pipe (default /tmp/horst). .TP .BI \-X\ name Accept control commands on a named pipe with given name or set pipe name used with -x. .TP .BI \-x\ command Send control command to another \fBhorst\fP process who was started with -X and then exit. Multiple commands can be concatenated with ';'. Currently implemented commands are: .RS .IP pause \p Pause \fBhorst\fP processing .IP resume \p Resume \fBhorst\fP processing .IP reset \p Reset all history, statistics and views .IP channel=X Set channel channel number .IP channel_scan=X Automatically change channels (1 or 0) .IP channel_dwell=X Set channel dwell time when automatically changing channel (ms) .IP channel_upper=X Set max channel when automatically changing channel .IP outfile=X Write to outfile named X. If the file is already open, it is cleared and re-openend. If filename is not specified ("outfile=") any existing file is closed and no file is written. .RE .TP .BI \-e\ MAC Filter all MAC addresses except these, to show only packets originating from the specified MAC addresses. This option can be specified multiple times. .TP .BI \-f\ pkt_type Filter all packets except these. This option can be specified multiple times. For valid packet names see NAMES AND ABBREVIATIONS below. .TP .BI \-m\ (AP|STA|ADH|PRB|WDS|UNKNOWN) Only show/include packets and nodes of this mode. Note that the mode is infered by the information of packets we received and it may take some time until a node is properly classified. This option can be specified multiple times. .TP .BI \-B\ BSSID Only show/include packets which belong to the given BSSID. .SH TEXT USER INTERFACE The ncurses-based text interface tries to display a lot of information, so it may look confusing at first. Below we describe the different screens and options. .TP Main screen .RS The initial (main) screen is split into three parts. The upper area shows a list of aggregated "node" information, the most useful information about each sender which was discovered, one per line: .RS .TP .BI / \p "Spinner" to show activity .TP .BI Pk \p Percentage of this node's packets in relation to all received packets .TP .BI Re% \p Percentage of retried frames of all frames this node sent .TP .BI Cha \p Channel number .TP .BI Sig \p Signal value (RSSI) in dBm .TP .BI RAT \p Physical data rate .TP .BI TRANSMITTER MAC address of sender .TP .BI MODE \p Operating Mode (AP, AHD, PRB, STA, WDS), see "NAMES AND ABBREVIATIONS" .TP .BI ENCR \p Encryption (WPA1, WPA2, WEP) .TP .BI ESSID \p ESSID .TP .BI INFO \p Additional info like "BATMAN", IP address... .RE The lower area shows a scrolling list of packets as they come in: .RS .TP .BI Cha \p Channel number .TP .BI Sig \p Signal value (RSSI) in dBm .TP .BI RAT \p Physical data rate .TP .BI TRANSMITTER MAC address of sender .TP .BI BSSID \p BSSID .TP .BI TYPE \p Packet type, see "NAMES AND ABBREVIATIONS" .TP .BI INFO \p Additional info like ESSID, TFS, IP address... .RE The lower right box shows bar graphs for: .RS .TP .BI Signal of last received packet in green .TP .BI bps Bits per second of all received packets .TP .BI Usage Percentage of channel use .RE The lower edge is the menu and status bar, it shows which keys to press for other screens. The status shows ">" when \fBhorst\fP is running or "=" when it is paused, then "F" when any kind of filter is active, the Channel, the monitor interface in use and the time. .RE .TP Pause ('p' or ) Can be used to pause/resume \fBhorst\fP. When \fBhorst\fP is paused it will loose packets received in the mean time. .TP Reset ('r') Clears all history and aggregated statistical data. .TP History ('h') The history screen scrolls from right to left and shows a bar for each packet indicating the signal level. In the line below that, the packet type is indicated by one character (See NAMES AND ABBREVIATIONS below) and the rough physical data rate is indicated below that in blue. .TP ESSID ('e') The ESSID screen groups information by ESSID and shows the mode (AP, IBSS), the MAC address of the sender, the BSSID, the TSF, the beacon interval, the channel, the signal, a "W" when encrytoion is used and the IP address if known. .TP Statistics ('a') The statistics screen groups packets by physical rate and by packet type and shows other kinds of aggregated and statistical information based on packets. .TP Spectrum Analyzer ('s') The "poor mans spectrum analyzer" screen is only really useful when \fBhorst\fP is started with the -s option or the "Automatically change channel" option is selected in the "Chan" settings, or the config option channel_scan is set. It shows the available channels horizontally and vertical bars for each channel: .RS .IP \fBSignal\fP in green .IP \fBPhysical\fP rate in blue .IP \fBChannel\fP usage in orange/brown .RE By pressing the 'n' key, the display can be changed to show only the average signal level on each channel and the last 4 digits of the MAC address of the individual nodes at the level (height) they were received. This can give a quick graphical overview of the distance of nodes. .TP Filters ('f') This configuration dialog can be used to define the active filters. .TP Channel Settings ('c') This configuration dialog can be used to change the channel changing behaviour of \fBhorst\fP or to change to a different channel manually. .TP Sort ('o') Only active in the main screen, can be used to sort the node list in the upper area by Signal, Time, BSSID or Channel. .SH NAMES AND ABBREVIATIONS .TP 802.11 standard frames .TS ; cB s s l | l | l . Management frames _ a ASOCRQ Association request A ASOCRP Associaion response a REASRQ Reassociation request A REASRP Reassociation response p PROBRQ Probe request P PROBRP Probe response T TIMING Timing Advertisement B BEACON Beacon t ATIM ATIM D DISASC Disassociation u AUTH Authentication U DEAUTH Deauthentication C ACTION Action c ACTNOA Action No Ack .TE .TS ; cB s s l | l | l . Control frames _ w CTWRAP Control Wrapper b BACKRQ Block Ack Request B BACK Block Ack s PSPOLL PS-Poll R RTS RTS C CTS CTS K ACK ACK f CFEND CF-End f CFENDK CF-End + CF-Ack .TE .TS ; cB s s l | l | l . Data frames _ D DATA Data F DCFACK Data + CF-Ack F DCFPLL Data + CF-Poll F DCFKPL Data + CF-Ack + CF-Poll n NULL Null (no data) f CFACK CF-Ack (no data) f CFPOLL CF-Poll (no data) f CFCKPL CF-Ack + CF-Poll (no data) Q QDATA QoS Data F QDCFCK QoS Data + CF-Ack F QDCFPL QoS Data + CF-Poll F QDCFKP QoS Data + CF-Ack + CF-Poll N QDNULL QoS Null (no data) f QCFPLL QoS CF-Poll (no data) f QCFKPL QoS CF-Ack + CF-Poll (no data) * BADFCS Bad frame checksum .TE .TP Packet types Similar to 802.11 frames above but higher level and as a bit field (types can overlap, e.g. DATA + IP) and including more information, like IP, ARP, BATMAN, OLSR... .TS ; cB s s l | l | l . Packet types _ CTRL 0x000001 WLAN Control frame MGMT 0x000002 WLAN Management frame DATA 0x000004 WLAN Data frame BADFCS 0x000008 WLAN frame checksum (FCS) bad BEACON 0x000010 WLAN beacon frame PROBE 0x000020 WLAN probe request or response ASSOC 0x000040 WLAN associaction request/response frame AUTH 0x000080 WLAN authentication frame RTSCTS 0x000100 WLAN RTS or CTS ACK 0x000200 WLAN ACK or BlockACK NULL 0x000400 WLAN NULL Data frame QDATA 0x000800 WLAN QoS Data frame (WME/WMM) ARP 0x001000 ARP packet IP 0x002000 IP packet ICMP 0x004000 IP ICMP packet UDP 0x008000 IP UDP TCP 0x010000 IP TCP OLSR 0x020000 OLSR protocol BATMAN 0x040000 BATMAND Layer3 or BATMAN-ADV Layer 2 frame MESHZ 0x080000 MeshCruzer protocol .TE .TP Operating modes Bit field of operating mode type which is infered from received packets. Modes may overlap, i.e. it is common to see STA and PRB at the same time. .TS ; cB s s l | l | l . Operating modes _ AP 0x01 Access Point (AP) ADH 0x02 Ad-hoc node STA 0x04 Station (AP client) PRB 0x08 Sent PROBE requests WDS 0x10 WDS or 4 Address frames UNKNOWN 0x20 Unknown e.g. RTS/CTS or ACK .TE .SH MONITOR MODE To capture and analyze 802.11 traffic, the interface needs to be in monitor mode. You can either setup the interface manually beforehand or let \fBhorst\fP setup it automatically at startup. Usually, root privileges are required to modify an interface setup. \fBhorst\fP should work with any wireleass LAN card and driver which supports monitor mode, with either "prism2" or "radiotap" headers. This includes most modern mac80211-based drivers. If the interface is not in monitor mode at startup, \fBhorst\fP first tries to put the interface in monitor mode. If it fails (for example when the interface is already in use), a new virtual monitor interface (horst0) is added and used instead. The virtual monitor interface is removed when \fBhorst\fP exits. Note that changing the channel via a virtual monitor interface is not allowed by the wireless driver, so options -C and -s do not work when virtual monitor interface is used. Examples of how to setup an interface manually: .TP Using iw: .nf iw wlan0 interface add mon0 type monitor or sudo iw wlan1 set type monitor sudo iw wlan1 set channel 6 .fi .TP Using iwconfig: .nf iwconfig wlan0 mode monitor iwconfig wlan0 channel 1 ifconfig wlan0 up .fi .TP Using madwifi: wlanconfig wlan0 create wlandev wifi0 wlanmode monitor .TP Using hostap: .nf iwconfig wlan0 mode monitor iwpriv wlan0 monitor_type 1 .fi .SH NOTES Signal values and ranges may differ between wireless drivers and versions. .SH OUTPUT FILE FORMAT The format of the output file (-o flag) is a comma separated list of the following fields in the following order, one packet each line. .TP timestamp Local time, including microseconds (e.g. 2015-05-16 15:05:44.338806 +0300) .TP packet_type 802.11 MAC packet type name as defined in the section "NAMES AND ABBREVIATIONS". .TP wlan_src Source MAC address .TP wlan_dst Destination MAC address .TP wlan_bssid BSSID .TP pkt_types Higher level packet name as defined in section "NAMES AND ABBREVIATIONS". .TP phy_signal Signal strength in dBm .TP wlan_len Packet length (MAC) .TP phy_rate Physical data rate .TP phy_freq Received while tuned to this frequency. .TP wlan_tsf TFS timer value .TP wlan_essid ESSID, network name .TP wlan_mode Operating modes as defined in "NAMES AND ABBREVIATIONS". .TP wlan_channel Channel number .TP wlan_wep Encryption in use .TP wlan_wpa WPA1 Encryption in use .TP wlan_rsn RSN (WPA2) Encryption in use .TP ip_src IP source address (if available) .TP ip_dst IP destionation address (if available) .SH SEE ALSO .BR horst.conf (5), .BR tcpdump (1), .BR wireshark (1), .BR kismet (1), .BR README, .BI http://br1.einfach.org/tech/horst .SH AUTHOR \fBhorst\fP was written by Bruno Randolf . .PP This manual page was written by Antoine Beaupré , for the Debian project (and may be used by others). horst-version-5.0/horst.conf000066400000000000000000000014661273543766500162370ustar00rootroot00000000000000### [horst] configuration file # # Available options: # # quiet # debug # add_monitor # interface = interface name (wlan0) # display_view = history|essid|statistics|spectrum # display_interval = milliseconds (100) # outfile = file name for packet dumps # node_timeout = seconds (60) # receive_buffer = bytes # channel = channel number # channel_scan # channel_scan_rounds = the number of times the channel spectrum is scanned (-1) # channel_dwell = milliseconds (250) # channel_upper = channel number # server # client = server IP # port = port number # control_pipe = name # filter_mac = MAC address (up to 9 times) # filter_mode = [AP|STA|ADH|PRB|WDS|UNKNOWN] # filter_packet = [CTRL|MGMT|DATA|BADFCS|BEACON|PROBE|ASSOC|AUTH|RTS|ACK|NULL|QDATA|ARP|IP|ICMP|UDP|TCP|OLSR|BATMAN|MESHZ] # filter_bssid = MAC address (BSSID) horst-version-5.0/horst.conf.5000066400000000000000000000100111273543766500163640ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH HORST.CONF 5 "October 18, 2015" .\" Please adjust this date whenever revising the manpage. .SH NAME horst.conf \- configuration file for \fBhorst(1)\fP .SH SYNOPSIS /etc/horst.conf .SH DESCRIPTION The file \fI/etc/horst.conf\fP is the default configuration file. The configuration file consists of zero or more lines. Each line must be either \fBEMPTY\fP, \fBCOMMENT\fP or \fBOPTION\fP and must end with a new line character ('\\n', ASCII 0xa). \fBEMPTY\fP lines must have zero or more spaces (' ', ASCII 0x20). \fBCOMMENT\fP lines must begin with a hash sign ('#', ASCII 0x23). \fBOPTION\fP lines must be in the following form: .RS option[=value] .RE Options and their acceptable values are described below in section \fBOPTIONS\fP. Lines not matching the format described above will cause \fBhorst\fP to exit with non-zero status. .SH OPTIONS .IP channel=CHANNEL_NUMBER Set the initial channel number to which \fBhorst\fP tunes the radio at startup. .IP channel_dwell=MILLISECONDS Set the time \fBhorst\fP scans each channel when channel_scan=1 is defined. .IP channel_scan Make \fBhorst\fP change/scan channels automatically in ascending numeric order. .IP channel_scan_rounds=N Set the number of rounds \fBhorst\fP scans the channel range when channel_scan=1 is defined. When N is reached, \fBhorst\fP exits with zero status. If N is a negative number, \fBhorst\fP scans the channel range infinitely. .IP channel_upper=CHANNEL_NUMBER Set the highest channel \fBhorst\fP will change to when channel_scan=1 is defined. When CHANNEL_NUMBER is reached, the next channel will be 1. .IP client=SERVER_ADDRESS Run \fBhorst\fP in client mode and connect to a server running in SERVER_ADDRESS. .IP control_pipe=FILEPATH Accept control commands on a named pipe. .IP display_interval=MILLISECONDS Set the refresh interval of the user interface display. This option can be used to reduce CPU load by using longer intervals. .IP display_view=history|essid|statistics|spectrum Set the initial display view. .IP filter_bssid=BSSID[,BSSID]... Ignore all packets except packets belonging to BSSID. .IP filter_mac=MAC_ADDRESS[,MAC_ADDRESS]... Ignore all packets except packets originating from MAC_ADDRESS. .IP filter_mode=MODE[,MODE]... Ignore all packets/nodes except packets/nodes of mode MODE. .IP filter_packet=PACKET_TYPE[,PACKET_TYPE]... Ignore all packets except packets of type PACKET_TYPE. .IP interface=INTERFACE_NAME Set the wireless interface which \fBhorst\fP uses to monitor the radio spectrum. If interface INTERFACE_NAME is not already in monitor mode at startup, \fBhorst\fP first tries to put the interface in monitor mode and if it fails, a temporary virtual monitor interface is added and used instead. The name of the temporary virtual monitor interface is in the form horst[0-9]+. The virtual interface created by \fBhorst\fP is deleted at exit. .IP add_monitor Always add virtual monitor interface. Don't try to set existing interface to monitor mode. .IP mac_names=FILEPATH The file containing a mapping from MAC addresses to host names. The file can either be a dhcp.leases file from dnsmasq or contain mappings in the form "MACname" (e.g.: "00:01:02:03:04:05 test") line by line. .IP node_timeout=SECONDS Set the time after nodes will be removed if no frames have been received from them. .IP outfile=FILEPATH Write information about each received packet to FILEPATH. .IP port=PORT_NUMBER Set the port \fBhorst\fP listens to when run in server mode or the port which \fBhorst\fP connects to when run in client mode. .IP quiet \p Make \fBhorst\fP less verbose and suppress the user interface. .IP receive_buffer=BYTES Set the size of the receive buffer. This option can be used to tune memory consumption and reduce packet loss under high load. .IP server \p Run \fBhorst\fP in server mode. .SH SEE ALSO .BR horst (1) horst-version-5.0/horst.sh000077500000000000000000000014311273543766500157170ustar00rootroot00000000000000#!/bin/sh cd `dirname $0` if grep -q ath[0-9]: /proc/net/dev ; then BASE=wifi0 if [ -n "$1" ] && [ -z "${1#wifi[0-9]}" ] ; then BASE=$1 shift fi WLDEV=mon0 wlanconfig $WLDEV create wlandev $BASE wlanmode monitor >/dev/null ip link set dev $WLDEV up ./horst -i $WLDEV $* ip link set dev $WLDEV down wlanconfig $WLDEV destroy exit 0 elif [ -n "$1" ]; then # find the phy for device $1 PHY=`iw dev | awk "/phy#[0-9]+/ {phy=\\$1} /Interface/ {if (\\$2 == \"$1\") {sub(\"#\", \"\", phy); print phy; exit}}"` if [ -n "$PHY" ] ; then shift WLDEV=${PHY}mon0 iw phy $PHY interface add $WLDEV type monitor flags fcsfail otherbss ip link set dev $WLDEV up ./horst -i $WLDEV $* ip link set dev $WLDEV down iw dev $WLDEV del exit 0 fi fi ./horst $* horst-version-5.0/ieee80211_util.c000066400000000000000000000123761273543766500167370ustar00rootroot00000000000000/* copied from linux wireless-2.6/net/mac80211/util.c */ /* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * Copyright 2014-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * utilities for mac80211 */ #include #include #include #include "ieee80211_util.h" #include "wlan80211.h" #include "main.h" #include "util.h" /* from mac80211/ieee80211_i.c, slightly modified */ /** * ieee80211_is_erp_rate - Check if a rate is an ERP rate * @phymode: The PHY-mode for this rate (MODE_IEEE80211...) * @rate: Transmission rate to check, in 100 kbps * * Check if a given rate is an Extended Rate PHY (ERP) rate. */ static inline bool ieee80211_is_erp_rate(int phymode, int rate) { if (phymode & PHY_FLAG_G) { if (rate != 10 && rate != 20 && rate != 55 && rate != 110) { DEBUG("erp\n"); return true; } } DEBUG("no erp\n"); return false; } static int get_cw_time(int cw_min, int cw_max, int retries, int slottime) { int cw = pow(2, (cw_min + retries)) - 1; cw_max = pow(2, cw_max) - 1; if(cw > cw_max) cw = cw_max; DEBUG("CW min %d max %d ret %d = %d\n", cw_min, cw_max, retries, cw); return (cw * slottime) / 2; } static const unsigned char ieee802_1d_to_ac[8] = { 0, 1, 1, 0, 2, 2, 3, 3 }; /* BE BK VI VO */ static const unsigned char ac_to_aifs[4] = { 3, 7, 2, 2}; static const unsigned char ac_to_cwmin[4] = { 4, 4, 3, 2}; static const unsigned int ac_to_cwmax[4] = { 10, 10, 4, 3}; /* from mac80211/util.c, modified */ int ieee80211_frame_duration(int phymode, size_t len, int rate, int short_preamble, int shortslot, int type, char qos_class, int retries) { int dur; bool erp; int sifs, slottime; static int last_was_cts; erp = ieee80211_is_erp_rate(phymode, rate); /* calculate duration (in microseconds, rounded up to next higher * integer if it includes a fractional microsecond) to send frame of * len bytes (does not include FCS) at the given rate. Duration will * also include SIFS. * * rate is in 100 kbps, so divident is multiplied by 10 in the * DIV_ROUND_UP() operations. */ DEBUG("DUR mode %d, len %d, rate %d, shortpre %d shortslot %d type %x UP %d\n", phymode, (int)len, rate, short_preamble, shortslot, type, qos_class); if (phymode == PHY_FLAG_A || erp) { DEBUG("OFDM\n"); /* * OFDM: * * N_DBPS = DATARATE x 4 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) * (16 = SIGNAL time, 6 = tail bits) * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext * * T_SYM = 4 usec * 802.11a - 17.5.2: aSIFSTime = 16 usec * 802.11g - 19.8.4: aSIFSTime = 10 usec + * signal ext = 6 usec */ sifs = 16; /* SIFS + signal ext */ slottime = 9; dur = 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */ dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */ dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, 4 * rate); /* T_SYM x N_SYM */ } else { DEBUG("CCK\n"); /* * 802.11b or 802.11g with 802.11b compatibility: * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. * * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 * aSIFSTime = 10 usec * aPreambleLength = 144 usec or 72 usec with short preamble * aPLCPHeaderLength = 48 usec or 24 usec with short preamble */ sifs = 10; /* aSIFSTime = 10 usec */ slottime = shortslot ? 9 : 20; dur = short_preamble ? (72 + 24) : (144 + 48); dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); } if (type == WLAN_FRAME_CTS || type == WLAN_FRAME_ACK) { //TODO: also fragments DEBUG("DUR SIFS\n"); dur += sifs; } else if (type == WLAN_FRAME_BEACON) { /* TODO: which AIFS and CW should be used for beacons? */ dur += sifs + (2 * slottime); /* AIFS */ dur += (slottime * 1) / 2; /* contention */ } else if (WLAN_FRAME_IS_DATA(type) && last_was_cts) { DEBUG("DUR LAST CTS\n"); dur += sifs; } else if (type == WLAN_FRAME_QDATA) { unsigned char ac = ieee802_1d_to_ac[(unsigned char)qos_class]; dur += sifs + (ac_to_aifs[ac] * slottime); /* AIFS */ dur += get_cw_time(ac_to_cwmin[ac], ac_to_cwmax[ac], retries, slottime); DEBUG("DUR AIFS %d CWMIN %d AC %d, UP %d\n", ac_to_aifs[ac], ac_to_cwmin[ac], ac, qos_class); } else { DEBUG("DUR DIFS\n"); dur += sifs + (2 * slottime); /* DIFS */ dur += get_cw_time(4, 10, retries, slottime); } if (type == WLAN_FRAME_CTS) { DEBUG("SET CTS\n"); last_was_cts = 1; } else last_was_cts = 0; /* TODO: Add EIFS (SIFS + ACKTXTIME) to frames with CRC errors, if we can get them */ DEBUG("DUR %d\n", dur); return dur; } int ieee80211_freq2channel(int freq) { if (freq == 2484) return 14; else if (freq < 2484) return (freq - 2407) / 5; else if (freq >= 4910 && freq <= 4980) return (freq - 4000) / 5; else return (freq - 5000) / 5; } int ieee80211_channel2freq(int channel) { if (channel == 14) return 2484; else if (channel < 14) return 5 * channel + 2407; else if (channel >= 182 && channel <= 196) return 5 * channel + 4000; else return 5 * channel + 5000; } horst-version-5.0/ieee80211_util.h000066400000000000000000000015371273543766500167410ustar00rootroot00000000000000/* copied from linux wireless-2.6/net/mac80211/util.c */ /* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * Copyright 2014-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * utilities for mac80211 */ #ifndef _IEEE80211_UTIL_H_ #define _IEEE80211_UTIL_H_ #include int ieee80211_frame_duration(int phymode, size_t len, int rate, int short_preamble, int ackcts, int shortslot, char qos_class, int retries); int ieee80211_freq2channel(int freq); int ieee80211_channel2freq(int channel); #endif horst-version-5.0/ifctrl-dummy.c000066400000000000000000000040531273543766500170040ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2015-2016 Bruno Randolf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ifctrl.h" #include "main.h" bool ifctrl_init(void) { return true; }; void ifctrl_finish(void) { }; bool ifctrl_iwadd_monitor(__attribute__((unused)) const char *interface, __attribute__((unused))const char *monitor_interface) { printlog("add monitor: not implemented"); return false; }; bool ifctrl_iwdel(__attribute__((unused)) const char *interface) { printlog("iwdel: not implemented"); return false; }; bool ifctrl_iwset_monitor(__attribute__((unused)) const char *interface) { printlog("set monitor: not implemented"); return false; }; bool ifctrl_iwset_freq(__attribute__((unused)) const char *const interface, __attribute__((unused)) unsigned int freq, __attribute__((unused)) enum chan_width width, __attribute__((unused)) unsigned int center1) { printlog("set freq: not implemented"); return false; }; bool ifctrl_iwget_interface_info(__attribute__((unused)) const char *interface) { printlog("get interface info: not implemented"); return false; }; bool ifctrl_iwget_freqlist(__attribute__((unused)) int phy, __attribute__((unused)) struct channel_list* channels) { printlog("get freqlist: not implemented"); return false; }; bool ifctrl_is_monitor(void) { return true; }; horst-version-5.0/ifctrl-ioctl.c000066400000000000000000000017371273543766500167710ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "ifctrl.h" bool ifctrl_flags(const char *const interface, bool up, bool promisc) { int fd; struct ifreq ifreq; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return false; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, interface, IF_NAMESIZE - 1); if (ioctl(fd, SIOCGIFFLAGS, &ifreq) == -1) { warn("Could not get flags for %s", interface); int orig_errno = errno; close(fd); errno = orig_errno; return false; } if (up) ifreq.ifr_flags |= IFF_UP; else ifreq.ifr_flags &= ~IFF_UP; if (promisc) ifreq.ifr_flags |= IFF_PROMISC; else ifreq.ifr_flags &= ~IFF_PROMISC; if (ioctl(fd, SIOCSIFFLAGS, &ifreq) == -1) { warn("Could not set flags for %s", interface); int orig_errno = errno; close(fd); errno = orig_errno; return false; } if (close(fd)) return false; return true; } horst-version-5.0/ifctrl-nl80211.c000066400000000000000000000301361273543766500166570ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2015 Tuomas Räsänen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define _GNU_SOURCE /* necessary for libnl-tiny */ #include #include #include #include #include #include #include #include #include #include #include "ifctrl.h" #include "main.h" #include "wlan_util.h" #ifndef NL80211_GENL_NAME #define NL80211_GENL_NAME "nl80211" #endif static struct nl_sock *sock = NULL; static struct nl_cache *cache = NULL; static struct genl_family *family = NULL; static bool nl80211_init(void) { int err; sock = nl_socket_alloc(); if (!sock) { fprintf(stderr, "failed to allocate netlink socket\n"); goto out; } err = genl_connect(sock); if (err) { nl_perror(err, "failed to make generic netlink connection"); goto out; } err = genl_ctrl_alloc_cache(sock, &cache); if (err) { nl_perror(err, "failed to allocate netlink controller cache"); goto out; } family = genl_ctrl_search_by_name(cache, NL80211_GENL_NAME); if (!family) { fprintf(stderr, "failed to find nl80211\n"); goto out; } return true; out: genl_family_put(family); nl_cache_free(cache); nl_socket_free(sock); return false; } static void nl80211_finish(void) { nl_socket_free(sock); genl_family_put(family); nl_cache_free(cache); } static bool nl80211_msg_prepare(struct nl_msg **const msgp, const enum nl80211_commands cmd, const char *const interface) { struct nl_msg *msg = nlmsg_alloc(); if (!msg) { fprintf(stderr, "failed to allocate netlink message\n"); return false; } if (!genlmsg_put(msg, 0, 0, genl_family_get_id(family), 0, 0 /*flags*/, cmd, 0)) { fprintf(stderr, "failed to add generic netlink headers\n"); goto nla_put_failure; } if (interface) { //TODO: PHY commands don't need interface name but wiphy index unsigned int if_index = if_nametoindex(interface); if (!if_index) { fprintf(stderr, "interface %s does not exist\n", interface); goto nla_put_failure; } NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_index); } *msgp = msg; return true; nla_put_failure: nlmsg_free(msg); return false; } static int nl80211_ack_cb(__attribute__((unused)) struct nl_msg *msg, void *arg) { int *ret = arg; *ret = 0; /* set "ACK" */ return NL_STOP; } static int nl80211_finish_cb(__attribute__((unused)) struct nl_msg *msg, void *arg) { int *ret = arg; *ret = 0; /* set "ACK" */ return NL_SKIP; } static int nl80211_err_cb(__attribute__((unused)) struct sockaddr_nl *nla, struct nlmsgerr *nlerr, __attribute__((unused)) void *arg) { int *ret = arg; /* as we want to treat the error like other errors from recvmsg, and * print it with nl_perror, we need to convert the error code to libnl * error codes like it is done in the verbose error handler of libnl */ *ret = -nl_syserr2nlerr(nlerr->error); return NL_STOP; } static int nl80211_default_cb(__attribute__((unused)) struct nl_msg *msg, __attribute__((unused)) void *arg) { return NL_SKIP; } /** * send message, free msg, receive reply and wait for ACK */ static bool nl80211_send_recv(struct nl_sock *const sock, struct nl_msg *const msg, nl_recvmsg_msg_cb_t cb_func, void* cb_arg) { int err; struct nl_cb *cb; /* set up callback */ cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { fprintf(stderr, "failed to allocate netlink callback\n"); return false; } if (cb_func != NULL) nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_func, cb_arg); else nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_default_cb, NULL); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl80211_ack_cb, &err); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl80211_finish_cb, &err); nl_cb_err(cb, NL_CB_CUSTOM, nl80211_err_cb, &err); err = nl_send_auto_complete(sock, msg); nlmsg_free(msg); if (err <= 0) { nl_perror(err, "failed to send netlink message"); return false; } /* * wait for reply message *and* ACK, or error * * Note that err is set by the handlers above. This is done because we * receive two netlink messages, one with the result (and handled by * cb_func) and another one with ACK. We are only done when we received * the ACK or an error! */ err = 1; while (err > 0) nl_recvmsgs(sock, cb); nl_cb_put(cb); if (err < 0) { nl_perror(err, "nl80211 message failed"); return false; } return true; } /** * send message, free msg and wait for ACK */ static bool nl80211_send(struct nl_sock *const sock, struct nl_msg *const msg) { return nl80211_send_recv(sock, msg, NULL, NULL); /* frees msg */ } static struct nlattr** nl80211_parse(struct nl_msg *msg) { struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); static struct nlattr *attr[NL80211_ATTR_MAX + 1]; nla_parse(attr, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); return attr; } /* * ifctrl interface */ bool ifctrl_init(void) { return nl80211_init(); } void ifctrl_finish(void) { nl80211_finish(); } bool ifctrl_iwadd_monitor(const char *const interface, const char *const monitor_interface) { struct nl_msg *msg; if (!nl80211_msg_prepare(&msg, NL80211_CMD_NEW_INTERFACE, interface)) return false; NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, monitor_interface); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); return nl80211_send(sock, msg); /* frees msg */ nla_put_failure: fprintf(stderr, "failed to add attribute to netlink message\n"); nlmsg_free(msg); return false; } bool ifctrl_iwdel(const char *const interface) { struct nl_msg *msg; if (!nl80211_msg_prepare(&msg, NL80211_CMD_DEL_INTERFACE, interface)) return false; return nl80211_send(sock, msg); /* frees msg */ } bool ifctrl_iwset_monitor(const char *const interface) { struct nl_msg *msg; if (!nl80211_msg_prepare(&msg, NL80211_CMD_SET_INTERFACE, interface)) return false; NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); return nl80211_send(sock, msg); /* frees msg */ nla_put_failure: fprintf(stderr, "failed to add attribute to netlink message\n"); nlmsg_free(msg); return false; } bool ifctrl_iwset_freq(const char *const interface, unsigned int freq, enum chan_width width, unsigned int center1) { struct nl_msg *msg; int nl_width = NL80211_CHAN_WIDTH_20_NOHT; if (!nl80211_msg_prepare(&msg, NL80211_CMD_SET_CHANNEL, interface)) return false; switch (width) { case CHAN_WIDTH_UNSPEC: case CHAN_WIDTH_20_NOHT: nl_width = NL80211_CHAN_WIDTH_20_NOHT; break; case CHAN_WIDTH_20: nl_width = NL80211_CHAN_WIDTH_20; break; case CHAN_WIDTH_40: nl_width = NL80211_CHAN_WIDTH_40; break; case CHAN_WIDTH_80: nl_width = NL80211_CHAN_WIDTH_80; break; case CHAN_WIDTH_160: nl_width = NL80211_CHAN_WIDTH_160; break; case CHAN_WIDTH_8080: nl_width = NL80211_CHAN_WIDTH_80P80; break; } NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, nl_width); if (center1) NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center1); return nl80211_send(sock, msg); /* frees msg */ nla_put_failure: fprintf(stderr, "failed to add attribute to netlink message\n"); nlmsg_free(msg); return false; } static int nl80211_get_interface_info_cb(struct nl_msg *msg, __attribute__((unused)) void *arg) { struct nlattr **tb = nl80211_parse(msg); if (tb[NL80211_ATTR_WIPHY_FREQ]) conf.if_freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { int nlw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]); switch (nlw) { case NL80211_CHAN_WIDTH_20_NOHT: conf.channel_width = CHAN_WIDTH_20_NOHT; break; case NL80211_CHAN_WIDTH_20: conf.channel_width = CHAN_WIDTH_20; break; case NL80211_CHAN_WIDTH_40: conf.channel_width = CHAN_WIDTH_40; break; case NL80211_CHAN_WIDTH_80: conf.channel_width = CHAN_WIDTH_80; break; case NL80211_CHAN_WIDTH_160: conf.channel_width = CHAN_WIDTH_160; break; case NL80211_CHAN_WIDTH_80P80: conf.channel_width = CHAN_WIDTH_8080; break; default: conf.channel_width = CHAN_WIDTH_UNSPEC; break; } } if (conf.channel_width == CHAN_WIDTH_40 && tb[NL80211_ATTR_CENTER_FREQ1]) { unsigned int center1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); conf.channel_ht40plus = center1 > conf.if_freq; } if (tb[NL80211_ATTR_IFTYPE]) conf.if_type = nla_get_u32(tb[NL80211_ATTR_IFTYPE]); if (tb[NL80211_ATTR_WIPHY]) conf.if_phy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); return NL_SKIP; } bool ifctrl_iwget_interface_info(const char *const interface) { struct nl_msg *msg; bool ret; if (!nl80211_msg_prepare(&msg, NL80211_CMD_GET_INTERFACE, interface)) return false; ret = nl80211_send_recv(sock, msg, nl80211_get_interface_info_cb, NULL); /* frees msg */ if (!ret) fprintf(stderr, "failed to get interface info\n"); return ret; } static int nl80211_get_freqlist_cb(struct nl_msg *msg, void *arg) { int bands_remain, freqs_remain, i = 0, b = 0; struct nlattr **attr = nl80211_parse(msg); struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1]; struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1]; struct nlattr *band, *freq; struct channel_list* list = arg; nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain) { nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band), nla_len(band), NULL); list->band[b].max_chan_width = CHAN_WIDTH_20_NOHT; /* default */ if (bands[NL80211_BAND_ATTR_HT_CAPA]) { uint16_t cap = nla_get_u16(bands[NL80211_BAND_ATTR_HT_CAPA]); if (cap & WLAN_IE_HT_CAPAB_INFO_CHAN_WIDTH_40) list->band[b].max_chan_width = CHAN_WIDTH_40; else list->band[b].max_chan_width = CHAN_WIDTH_20; } if (bands[NL80211_BAND_ATTR_HT_MCS_SET] && nla_len(bands[NL80211_BAND_ATTR_HT_MCS_SET]) == 16) { ht_streams_from_mcs_set(nla_data(bands[NL80211_BAND_ATTR_HT_MCS_SET]), &list->band[b].streams_rx, &list->band[b].streams_tx); } if (bands[NL80211_BAND_ATTR_VHT_CAPA]) { uint32_t vht = nla_get_u32(bands[NL80211_BAND_ATTR_VHT_CAPA]); list->band[b].max_chan_width = chan_width_from_vht_capab(vht); } if (bands[NL80211_BAND_ATTR_VHT_MCS_SET] && nla_len(bands[NL80211_BAND_ATTR_VHT_MCS_SET]) == 8) { vht_streams_from_mcs_set(nla_data(bands[NL80211_BAND_ATTR_VHT_MCS_SET]), &list->band[b].streams_rx, &list->band[b].streams_tx); } nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS], freqs_remain) { nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX, nla_data(freq), nla_len(freq), NULL); if (!freqs[NL80211_FREQUENCY_ATTR_FREQ] || freqs[NL80211_FREQUENCY_ATTR_DISABLED]) continue; channel_list_add(nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ])); if (++i >= MAX_CHANNELS) goto end; } list->band[b].num_channels = b == 0 ? i : i - list->band[0].num_channels; if (++b >= MAX_BANDS) goto end; } end: list->num_channels = i; list->num_bands = b; return NL_SKIP; } bool ifctrl_iwget_freqlist(int phy, struct channel_list* channels) { struct nl_msg *msg; bool ret; if (!nl80211_msg_prepare(&msg, NL80211_CMD_GET_WIPHY, NULL)) return false; NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy); ret = nl80211_send_recv(sock, msg, nl80211_get_freqlist_cb, channels); /* frees msg */ if (!ret) fprintf(stderr, "failed to get freqlist\n"); return ret; nla_put_failure: fprintf(stderr, "failed to add attribute to netlink message\n"); nlmsg_free(msg); return false; } bool ifctrl_is_monitor(void) { return conf.if_type == NL80211_IFTYPE_MONITOR; } horst-version-5.0/ifctrl-osx.m000066400000000000000000000150201273543766500164700ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2015 Bruno Randolf * Copyright (C) 2015 Jeromy Fu * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ifctrl.h" #include "main.h" #include "ieee80211_util.h" #import bool osx_set_freq(const char *interface, unsigned int freq) { int channel = ieee80211_freq2channel(freq); CWWiFiClient * wifiClient = [CWWiFiClient sharedWiFiClient]; NSString * interfaceName = [[NSString alloc] initWithUTF8String: interface]; CWInterface * currentInterface = [wifiClient interfaceWithName: interfaceName]; [interfaceName release]; NSSet * channels = [currentInterface supportedWLANChannels]; CWChannel * wlanChannel = nil; for (CWChannel * _wlanChannel in channels) { if ([_wlanChannel channelNumber] == channel) wlanChannel = _wlanChannel; } bool ret = true; if (wlanChannel != nil) { NSError *err = nil; BOOL result = [currentInterface setWLANChannel:wlanChannel error:&err]; if( !result ) { printlog("set channel %ld err: %s", (long)[wlanChannel channelNumber], [[err localizedDescription] UTF8String]); ret = false; } } return ret; } enum chan_width get_channel_width(CWChannelWidth width) { enum chan_width current_width = CHAN_WIDTH_UNSPEC; switch (width) { case kCWChannelWidth20MHz: current_width = CHAN_WIDTH_20; break; case kCWChannelWidth40MHz: current_width = CHAN_WIDTH_40; break; case kCWChannelWidth80MHz: current_width = CHAN_WIDTH_80; break; case kCWChannelWidth160MHz: current_width = CHAN_WIDTH_160; break; default: break; } return current_width; } int osx_get_channels(const char* devname, struct channel_list* channels) { CWWiFiClient * wifiClient = [CWWiFiClient sharedWiFiClient]; NSString * interfaceName = [[NSString alloc] initWithUTF8String: devname]; CWInterface * currentInterface = [wifiClient interfaceWithName: interfaceName]; [interfaceName release]; NSSet * supportedChannelsSet = [currentInterface supportedWLANChannels]; NSSortDescriptor * sort = [NSSortDescriptor sortDescriptorWithKey:@"channelNumber" ascending:YES]; NSArray * sortedChannels = [supportedChannelsSet sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]]; int i = 0; for (int i = 0; i < MAX_BANDS; ++i) { channels->band[i].num_channels = 0; channels->band[i].streams_rx = 0; channels->band[i].streams_tx = 0; channels->band[i].max_chan_width = CHAN_WIDTH_20; } channels->num_bands = MAX_BANDS; i = 0; NSInteger lastNum = -1; for( id eachChannel in sortedChannels ) { NSInteger num = [eachChannel channelNumber]; CWChannelBand band = [eachChannel channelBand]; CWChannelWidth width = [eachChannel channelWidth]; printlog("num: %ld, band: %ld, width: %ld", num, (long)band, (long)width); if (lastNum != num ) { channel_list_add(ieee80211_channel2freq(num)); } int bandIdx = -1; if( kCWChannelBand2GHz == band ) { bandIdx = 0; } else if( kCWChannelBand5GHz == band ) { bandIdx = 1; } if( bandIdx >= 0) { if (lastNum != num ) { ++(channels->band[bandIdx].num_channels); } enum chan_width w = get_channel_width(width); channels->band[bandIdx].max_chan_width = \ channels->band[bandIdx].max_chan_width < w ? w : channels->band[bandIdx].max_chan_width; } lastNum = num; if( ++i > MAX_CHANNELS) { break; } } printlog("band 0 channels: %d", channels->band[0].num_channels); printlog("band 1 channels: %d", channels->band[1].num_channels); return i; } bool ifctrl_init() { CWWiFiClient * wifiClient = [CWWiFiClient sharedWiFiClient]; NSString * interfaceName = [[NSString alloc] initWithUTF8String: conf.ifname]; CWInterface * currentInterface = [wifiClient interfaceWithName: interfaceName]; [interfaceName release]; [currentInterface disassociate]; return true; }; void ifctrl_finish() { CWWiFiClient * wifiClient = [CWWiFiClient sharedWiFiClient]; NSString * interfaceName = [[NSString alloc] initWithUTF8String: conf.ifname]; CWInterface * currentInterface = [wifiClient interfaceWithName: interfaceName]; [interfaceName release]; CWNetwork * _network = [[CWNetwork alloc] init]; [currentInterface associateToNetwork:_network password:nil error:nil]; }; bool ifctrl_iwadd_monitor(__attribute__((unused))const char *interface, __attribute__((unused))const char *monitor_interface) { printlog("add monitor: not implemented"); return false; }; bool ifctrl_iwdel(__attribute__((unused))const char *interface) { printlog("iwdel: not implemented"); return false; }; bool ifctrl_iwset_monitor(__attribute__((unused))const char *interface) { printlog("set monitor: not implemented"); return false; }; bool ifctrl_iwset_freq(__attribute__((unused))const char *interface, __attribute__((unused))unsigned int freq, __attribute__((unused))enum chan_width width, __attribute__((unused))unsigned int center) { if (osx_set_freq(interface, freq)) return true; return false; }; bool ifctrl_iwget_interface_info(__attribute__((unused))const char *interface) { printlog("get interface info: not implemented"); return false; }; bool ifctrl_iwget_freqlist(__attribute__((unused))int phy, struct channel_list* channels) { int num_channels = osx_get_channels(conf.ifname, channels); if (num_channels) return true; return false; }; bool ifctrl_is_monitor() { return true; }; horst-version-5.0/ifctrl-wext.c000066400000000000000000000104321273543766500166360ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "main.h" #include "util.h" extern int mon; /* monitoring socket */ static bool wext_set_freq(int fd, const char* devname, int freq) { struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); strncpy(iwr.ifr_name, devname, IFNAMSIZ - 1); iwr.ifr_name[IFNAMSIZ - 1] = '\0'; freq *= 100000; iwr.u.freq.m = freq; iwr.u.freq.e = 1; if (ioctl(fd, SIOCSIWFREQ, &iwr) < 0) { printlog("ERROR: wext set channel"); return false; } return true; } static int wext_get_freq(int fd, const char* devname) { struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); strncpy(iwr.ifr_name, devname, IFNAMSIZ - 1); iwr.ifr_name[IFNAMSIZ - 1] = '\0'; if (ioctl(fd, SIOCGIWFREQ, &iwr) < 0) return 0; DEBUG("FREQ %d %d\n", iwr.u.freq.m, iwr.u.freq.e); return iwr.u.freq.m; } static int wext_get_channels(int fd, const char* devname, struct channel_list* channels) { struct iwreq iwr; struct iw_range range; int i; int band0cnt = 0; int band1cnt = 0; memset(&iwr, 0, sizeof(iwr)); memset(&range, 0, sizeof(range)); strncpy(iwr.ifr_name, devname, IFNAMSIZ - 1); iwr.ifr_name[IFNAMSIZ - 1] = '\0'; iwr.u.data.pointer = (caddr_t) ⦥ iwr.u.data.length = sizeof(range); iwr.u.data.flags = 0; if (ioctl(fd, SIOCGIWRANGE, &iwr) < 0) { printlog("ERROR: wext get channel list"); return 0; } if (range.we_version_compiled < 16) { printlog("ERROR: wext version %d too old to get channels", range.we_version_compiled); return 0; } for (i = 0; i < range.num_frequency && i < MAX_CHANNELS; i++) { DEBUG(" Channel %.2d: %dMHz\n", range.freq[i].i, range.freq[i].m); channels->chan[i].chan = range.freq[i].i; /* different drivers return different frequencies * (e.g. ipw2200 vs mac80211) try to fix them up here */ if (range.freq[i].m > 100000000) channels->chan[i].freq = range.freq[i].m / 100000; else channels->chan[i].freq = range.freq[i].m; if (channels->chan[i].freq <= 2500) band0cnt++; else band1cnt++; } channels->num_channels = i; channels->num_bands = band1cnt > 0 ? 2 : 1; channels->band[0].num_channels = band0cnt; channels->band[1].num_channels = band1cnt; return i; } /* * ifctrl.h implementation */ bool ifctrl_init(void) { return true; }; void ifctrl_finish(void) { }; bool ifctrl_iwadd_monitor(__attribute__((unused)) const char *interface, __attribute__((unused)) const char *monitor_interface) { printlog("add monitor: not supported with WEXT"); return false; } bool ifctrl_iwdel(__attribute__((unused)) const char *interface) { printlog("del: not supported with WEXT"); return false; } bool ifctrl_iwset_monitor(__attribute__((unused)) const char *interface) { printlog("set monitor: not supported with WEXT"); return false; } bool ifctrl_iwset_freq(const char *const interface, unsigned int freq, __attribute__((unused)) enum chan_width width, __attribute__((unused)) unsigned int center1) { if (wext_set_freq(mon, interface, freq)) return true; return false; } bool ifctrl_iwget_interface_info(const char *interface) { conf.if_freq = wext_get_freq(mon, interface); if (conf.if_freq == 0) return false; return true; } bool ifctrl_iwget_freqlist(__attribute__((unused)) int phy, struct channel_list* channels) { if (wext_get_channels(mon, conf.ifname, channels)) return true; return false; } bool ifctrl_is_monitor(void) { return true; /* assume yes */ } horst-version-5.0/ifctrl.h000066400000000000000000000044331273543766500156620ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2015 Tuomas Räsänen * Copyright (C) 2015-2016 Bruno Randolf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IFCTRL_H #define IFCTRL_H #include #include "channel.h" bool ifctrl_init(); void ifctrl_finish(); /** * ifctrl_iwadd_monitor() - add virtual 802.11 monitor interface * * @interface: the name of the interface the monitor interface is attached to * @monitor_interface: the name of the new monitor interface * * Return true on success, false on error. */ bool ifctrl_iwadd_monitor(const char *interface, const char *monitor_interface); /** * ifctrl_iwdel() - delete 802.11 interface * * @interface: the name of the interface * * Return true on success, false on error. */ bool ifctrl_iwdel(const char *interface); /** * ifctrl_flags() - set interface flags: up/down, promisc * * @interface: the name of the interface * @up: up or down * @promisc: promiscuous mode or not * * Return true on success, false on error. */ bool ifctrl_flags(const char *interface, bool up, bool promisc); /** * ifctrl_iwset_monitor() - set 802.11 interface to monitor mode * * @interface: the name of the interface * * Return true on success, false on error. */ bool ifctrl_iwset_monitor(const char *interface); bool ifctrl_iwset_freq(const char *const interface, unsigned int freq, enum chan_width width, unsigned int center1); bool ifctrl_iwget_interface_info(const char *interface); bool ifctrl_iwget_freqlist(int phy, struct channel_list* channels); bool ifctrl_is_monitor(); #endif horst-version-5.0/licenses/000077500000000000000000000000001273543766500160275ustar00rootroot00000000000000horst-version-5.0/licenses/BSD-MIT000066400000000000000000000017771273543766500170250ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. horst-version-5.0/licenses/CC0000066400000000000000000000143571273543766500163310ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. horst-version-5.0/listsort.c000066400000000000000000000161741273543766500162620ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * This is based on work under the following license: * * This file is copyright 2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "ccan/list/list.h" #include "listsort.h" /* * sorting a linked list. * * The algorithm used is Mergesort, because that works really well * on linked lists, without requiring the O(N) extra space it needs * when you do it on arrays. * */ /* * This is the actual sort function. * It assumes head to be a pointer to the first list element, and not beeing a * list element itself (as common in the linux linked list implementation). * If the first element moves, head is adjusted accordingly. */ void listsort(struct list_node *head, int(*cmp)(const struct list_node*, const struct list_node*)) { struct list_node *list, *p, *q, *e, *tail, *oldhead; int insize, nmerges, psize, qsize, i; if (!head || head->next == head) return; list = head->next; insize = 1; while (1) { p = list; oldhead = list; /* used for circular linkage */ list = NULL; tail = NULL; nmerges = 0; /* count number of merges we do in this pass */ while (p) { nmerges++; /* there exists a merge to be done */ /* step `insize' places along from p */ q = p; psize = 0; for (i = 0; i < insize; i++) { psize++; q = (q->next == oldhead || q->next == head ? NULL : q->next); if (!q) break; } /* if q hasn't fallen off end, we have two lists to merge */ qsize = insize; /* now we have two lists; merge them */ while (psize > 0 || (qsize > 0 && q)) { /* decide whether next element of merge comes from p or q */ if (psize == 0) { /* p is empty; e must come from q. */ e = q; q = q->next; qsize--; if (q == oldhead || q == head) q = NULL; } else if (qsize == 0 || !q) { /* q is empty; e must come from p. */ e = p; p = p->next; psize--; if (p == oldhead || p == head) p = NULL; } else if (cmp(p,q) <= 0) { /* First element of p is lower (or same); * e must come from p. */ e = p; p = p->next; psize--; if (p == oldhead || p == head) p = NULL; } else { /* First element of q is lower; e must come from q. */ e = q; q = q->next; qsize--; if (q == oldhead || q == head) q = NULL; } /* add the next element to the merged list */ if (tail) tail->next = e; else list = e; /* Maintain reverse pointers */ e->prev = tail; tail = e; } /* now p has stepped `insize' places along, and q has too */ p = q; } tail->next = list; list->prev = tail; /* If we have done only one merge, we're finished. */ if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */ /* adjust head */ head->next = list; head->prev = list->prev; list->prev->next = head; list->prev = head; return; } /* Otherwise repeat, merging lists twice the size */ insize *= 2; } } #if 0 /* * Small test rig with three test orders. The list length 13 is * chosen because that means some passes will have an extra list at * the end and some will not. */ #include struct element { struct list_head list; int i; }; int elem_cmp(const struct list_head *a, const struct list_head *b) { struct element *ea = list_entry(a, struct element, list); struct element *eb = list_entry(b, struct element, list); //printf(" cmp %d - %d\n", ea->i, eb->i); return ea->i - eb->i; } #if 0 /* old outdated test */ int main(void) { #define n 13 struct element k[n], *head, *p; struct list_head* lh; int order[][n] = { { 0,1,2,3,4,5,6,7,8,9,10,11,12 }, { 6,2,8,4,11,1,12,7,3,9,5,0,10 }, { 12,11,10,9,8,7,6,5,4,3,2,1,0 }, }; int i, j; for (j = 0; j < n; j++) k[j].i = j; listsort(NULL, &elem_cmp); for (i = 0; i < sizeof(order)/sizeof(*order); i++) { int *ord = order[i]; head = &k[ord[0]]; for (j = 0; j < n; j++) { if (j == n-1) k[ord[j]].list.next = &k[ord[0]].list; else k[ord[j]].list.next = &k[ord[j+1]].list; if (j == 0) k[ord[j]].list.prev = &k[ord[n-1]].list; else k[ord[j]].list.prev = &k[ord[j-1]].list; } printf("before:"); p = head; do { printf(" %d", p->i); //if (p->list.next && p->list.next->prev != p->list) // printf(" [REVERSE LINK ERROR!]"); p = list_entry(p->list.next, struct element, list); } while (p != head); printf("\n"); lh = listsort(&head->list, &elem_cmp); head = list_entry(lh, struct element, list); printf(" after:"); p = head; do { printf(" %d", p->i); //if (p->next && p->next->prev != p) // printf(" [REVERSE LINK ERROR!]"); p = list_entry(p->list.next, struct element, list); } while (p != head); printf("\n"); } return 0; } #endif int main(void) { struct list_head lh; struct element e[5]; struct element* ep; list_head_init(&lh); e[0].i = 5; e[1].i = 2; e[2].i = 1; e[3].i = 3; e[4].i = 4; list_add_tail(&lh, &e[0].list); list_add_tail(&lh, &e[1].list); list_add_tail(&lh, &e[2].list); list_add_tail(&lh, &e[3].list); list_add_tail(&lh, &e[4].list); list_for_each(&lh, ep, list) { printf("%d ", ep->i); } printf("\n"); listsort(&lh, &elem_cmp); list_for_each(&lh, ep, list) { printf("%d ", ep->i); //printf(" [%p next %p prev %p]\n", &ep->list, ep->list.next, ep->list.prev); if (ep->list.next->prev != &ep->list) printf("* reverse link error!\n"); } printf("\n"); return 0; } #endif horst-version-5.0/listsort.h000066400000000000000000000017641273543766500162660ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _LISTSORT_H #define _LISTSORT_H struct list_head; void listsort(struct list_node *list, int(*cmp)(const struct list_node*, const struct list_node*)); #endif horst-version-5.0/main.c000066400000000000000000000533041273543766500153170ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "util.h" #include "capture.h" #include "protocol_parser.h" #include "network.h" #include "display.h" #include "wlan_util.h" #include "ieee80211_util.h" #include "control.h" #include "channel.h" #include "node.h" #include "essid.h" #include "conf_options.h" #include "ifctrl.h" struct list_head nodes; struct essid_meta_info essids; struct history hist; struct statistics stats; struct channel_info spectrum[MAX_CHANNELS]; struct node_names_info node_names; struct config conf; struct timespec the_time; int mon; /* monitoring socket */ static FILE* DF = NULL; /* receive packet buffer * * due to the way we receive packets the network (TCP connection) we have to * expect the reception of partial packet as well as the reception of several * packets at one. thus we implement a buffered receive where partially received * data stays in the buffer. * * we need two buffers: one for packet capture or receiving from the server and * another one for data the clients sends to the server. * * not sure if this is also an issue with local packet capture, but it is not * implemented there. * * size: max 80211 frame (2312) + space for prism2 header (144) * or radiotap header (usually only 26) + some extra */ static unsigned char buffer[2312 + 200]; static size_t buflen; /* for packets from client to server */ static unsigned char cli_buffer[500]; static size_t cli_buflen; /* for select */ static fd_set read_fds; static fd_set write_fds; static fd_set excpt_fds; static volatile sig_atomic_t is_sigint_caught; void __attribute__ ((format (printf, 1, 2))) printlog(const char *fmt, ...) { char buf[128]; va_list ap; va_start(ap, fmt); vsnprintf(&buf[1], 127, fmt, ap); va_end(ap); if (conf.quiet || conf.debug || !conf.display_initialized) fprintf(stderr, "%s\n", &buf[1]); else { /* fix up string for display log */ buf[0] = '\n'; display_log(buf); } } static void update_history(struct packet_info* p) { if (p->phy_signal == 0) return; hist.signal[hist.index] = p->phy_signal; hist.rate[hist.index] = p->phy_rate; hist.type[hist.index] = (p->phy_flags & PHY_FLAG_BADFCS) ? 1 : p->wlan_type; hist.retry[hist.index] = p->wlan_retry; hist.index++; if (hist.index == MAX_HISTORY) hist.index = 0; } static void update_statistics(struct packet_info* p) { int type = (p->phy_flags & PHY_FLAG_BADFCS) ? 1 : p->wlan_type; if (p->phy_rate_idx == 0) return; stats.packets++; stats.bytes += p->wlan_len; if (p->wlan_retry) stats.retries++; if (p->phy_rate_idx > 0 && p->phy_rate_idx < MAX_RATES) { stats.duration += p->pkt_duration; stats.packets_per_rate[p->phy_rate_idx]++; stats.bytes_per_rate[p->phy_rate_idx] += p->wlan_len; stats.duration_per_rate[p->phy_rate_idx] += p->pkt_duration; } if (type >= 0 && type < MAX_FSTYPE) { stats.packets_per_type[type]++; stats.bytes_per_type[type] += p->wlan_len; if (p->phy_rate_idx > 0 && p->phy_rate_idx < MAX_RATES) stats.duration_per_type[type] += p->pkt_duration; } } static void update_spectrum(struct packet_info* p, struct node_info* n) { struct channel_info* chan; struct chan_node* cn; if (p->pkt_chan_idx < 0) return; /* chan not found */ chan = &spectrum[p->pkt_chan_idx]; chan->signal = p->phy_signal; chan->packets++; chan->bytes += p->wlan_len; chan->durations += p->pkt_duration; ewma_add(&chan->signal_avg, -chan->signal); if (!n) { DEBUG("spec no node\n"); return; } /* add node to channel if not already there */ list_for_each(&chan->nodes, cn, chan_list) { if (cn->node == n) { DEBUG("SPEC node found %p\n", cn->node); break; } } if (cn->node != n) { DEBUG("SPEC node adding %p\n", n); cn = malloc(sizeof(struct chan_node)); cn->node = n; cn->chan = chan; ewma_init(&cn->sig_avg, 1024, 8); list_add_tail(&chan->nodes, &cn->chan_list); list_add_tail(&n->on_channels, &cn->node_list); chan->num_nodes++; n->num_on_channels++; } /* keep signal of this node as seen on this channel */ cn->sig = p->phy_signal; ewma_add(&cn->sig_avg, -cn->sig); cn->packets++; } void update_spectrum_durations(void) { /* also if channel was not changed, keep stats only for every channel_time. * display code uses durations_last to get a more stable view */ if (conf.channel_idx >= 0) { spectrum[conf.channel_idx].durations_last = spectrum[conf.channel_idx].durations; spectrum[conf.channel_idx].durations = 0; ewma_add(&spectrum[conf.channel_idx].durations_avg, spectrum[conf.channel_idx].durations_last); } } static void write_to_file(struct packet_info* p) { char buf[40]; int i; struct tm* ltm = localtime(&the_time.tv_sec); //timestamp, e.g. 2015-05-16 15:05:44.338806 +0300 i = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ltm); i += snprintf(buf + i, sizeof(buf) - i, ".%06ld", (long)(the_time.tv_nsec / 1000)); i += strftime(buf + i, sizeof(buf) - i, " %z", ltm); fprintf(DF, "%s, ", buf); fprintf(DF, "%s, %s, ", get_packet_type_name(p->wlan_type), ether_sprintf(p->wlan_src)); fprintf(DF, "%s, ", ether_sprintf(p->wlan_dst)); fprintf(DF, "%s, ", ether_sprintf(p->wlan_bssid)); fprintf(DF, "%x, %d, %d, %d, %d, ", p->pkt_types, p->phy_signal, p->wlan_len, p->phy_rate, p->phy_freq); fprintf(DF, "%016llx, ", (unsigned long long)p->wlan_tsf); fprintf(DF, "%s, %d, %d, %d, %d, %d, ", p->wlan_essid, p->wlan_mode, p->wlan_channel, p->wlan_wep, p->wlan_wpa, p->wlan_rsn); fprintf(DF, "%s, %s\n", ip_sprintf(p->ip_src), ip_sprintf(p->ip_dst)); fflush(DF); } /* return true if packet is filtered */ static bool filter_packet(struct packet_info* p) { int i; if (conf.filter_off) return false; /* if packets with bad FCS are not filtered, still we can not trust any * other header, so in any case return */ if (p->phy_flags & PHY_FLAG_BADFCS) { if (!conf.filter_badfcs) { stats.filtered_packets++; return true; } return false; } /* filter by WLAN frame type and also type 3 which is not defined */ i = WLAN_FRAME_TYPE(p->wlan_type); if (i == 3 || !(conf.filter_stype[i] & BIT(WLAN_FRAME_STYPE(p->wlan_type)))) { stats.filtered_packets++; return true; } /* filter by MODE (AP, IBSS, ...) this also filters packets where we * cannot associate a mode (ACK, RTS/CTS) */ if (conf.filter_mode != WLAN_MODE_ALL && ((p->wlan_mode & ~conf.filter_mode) || p->wlan_mode == 0)) { stats.filtered_packets++; return true; } /* filter higher level packet types */ if (conf.filter_pkt != PKT_TYPE_ALL && (p->pkt_types & ~conf.filter_pkt)) { stats.filtered_packets++; return true; } /* filter BSSID */ if (MAC_NOT_EMPTY(conf.filterbssid) && memcmp(p->wlan_bssid, conf.filterbssid, MAC_LEN) != 0) { stats.filtered_packets++; return true; } /* filter MAC adresses */ if (conf.do_macfilter) { for (i = 0; i < MAX_FILTERMAC; i++) { if (MAC_NOT_EMPTY(p->wlan_src) && conf.filtermac_enabled[i] && memcmp(p->wlan_src, conf.filtermac[i], MAC_LEN) == 0) { return false; } } stats.filtered_packets++; return true; } return false; } static void fixup_packet_channel(struct packet_info* p) { int i = -1; /* get channel index for packet */ if (p->phy_freq) { i = channel_find_index_from_freq(p->phy_freq); } /* if not found from pkt, best guess from config but it might be * unknown (-1) too */ if (i < 0) p->pkt_chan_idx = conf.channel_idx; else p->pkt_chan_idx = i; /* wlan_channel is only known for beacons and probe response, * otherwise we set it from the physical channel */ if (p->wlan_channel == 0 && p->pkt_chan_idx >= 0) p->wlan_channel = channel_get_chan(p->pkt_chan_idx); /* if current channel is unknown (this is a mac80211 bug), guess it from * the packet */ if (conf.channel_idx < 0 && p->pkt_chan_idx >= 0) conf.channel_idx = p->pkt_chan_idx; } void handle_packet(struct packet_info* p) { struct node_info* n = NULL; /* filter on server side only */ if (conf.serveraddr[0] == '\0' && filter_packet(p)) { if (!conf.quiet && !conf.paused && !conf.debug) update_display_clock(); return; } fixup_packet_channel(p); if (cli_fd != -1) net_send_packet(p); if (conf.dumpfile[0] != '\0' && !conf.paused && DF != NULL) write_to_file(p); if (conf.paused) return; /* we can't trust any fields except phy_* of packets with bad FCS */ if (!(p->phy_flags & PHY_FLAG_BADFCS)) { DEBUG("handle %s\n", get_packet_type_name(p->wlan_type)); n = node_update(p); if (n) p->wlan_retries = n->wlan_retries_last; p->pkt_duration = ieee80211_frame_duration( p->phy_flags & PHY_FLAG_MODE_MASK, p->wlan_len, p->phy_rate, p->phy_flags & PHY_FLAG_SHORTPRE, 0 /*shortslot*/, p->wlan_type, p->wlan_qos_class, p->wlan_retries); } update_history(p); update_statistics(p); update_spectrum(p, n); update_essids(p, n); if (!conf.quiet && !conf.debug) update_display(p); } static void local_receive_packet(int fd, unsigned char* buffer, size_t bufsize) { int len; struct packet_info p; DEBUG("\n===============================================================================\n"); len = recv_packet(fd, buffer, bufsize); #if DO_DEBUG if (conf.debug) { dump_packet(buffer, len); DEBUG("\n"); } #endif memset(&p, 0, sizeof(p)); if (!parse_packet(buffer, len, &p)) { DEBUG("parsing failed\n"); return; } handle_packet(&p); } static void receive_any(const sigset_t *const waitmask) { int ret, mfd; uint32_t usecs = UINT32_MAX; struct timespec ts; FD_ZERO(&read_fds); FD_ZERO(&write_fds); FD_ZERO(&excpt_fds); if (!conf.quiet && !conf.debug) FD_SET(0, &read_fds); FD_SET(mon, &read_fds); if (srv_fd != -1) FD_SET(srv_fd, &read_fds); if (cli_fd != -1) FD_SET(cli_fd, &read_fds); if (ctlpipe != -1) FD_SET(ctlpipe, &read_fds); usecs = min(channel_get_remaining_dwell_time(), 1000000); ts.tv_sec = usecs / 1000000; ts.tv_nsec = usecs % 1000000 * 1000; mfd = max(mon, srv_fd); mfd = max(mfd, ctlpipe); mfd = max(mfd, cli_fd) + 1; ret = pselect(mfd, &read_fds, &write_fds, &excpt_fds, &ts, waitmask); if (ret == -1 && errno == EINTR) /* interrupted */ return; if (ret == 0) { /* timeout */ if (!conf.quiet && !conf.debug) update_display_clock(); return; } else if (ret < 0) /* error */ err(1, "select()"); /* stdin */ if (FD_ISSET(0, &read_fds) && !conf.quiet && !conf.debug) handle_user_input(); /* local packet or client */ if (FD_ISSET(mon, &read_fds)) { if (conf.serveraddr[0] != '\0') net_receive(mon, buffer, &buflen, sizeof(buffer)); else local_receive_packet(mon, buffer, sizeof(buffer)); } /* server */ if (srv_fd > -1 && FD_ISSET(srv_fd, &read_fds)) net_handle_server_conn(); /* from client to server */ if (cli_fd > -1 && FD_ISSET(cli_fd, &read_fds)) net_receive(cli_fd, cli_buffer, &cli_buflen, sizeof(cli_buffer)); /* named pipe */ if (ctlpipe > -1 && FD_ISSET(ctlpipe, &read_fds)) control_receive_command(); } void free_lists(void) { int i; struct essid_info *e, *f; struct node_info *ni, *mi; struct chan_node *cn, *cn2; /* free node list */ list_for_each_safe(&nodes, ni, mi, list) { DEBUG("free node %s\n", ether_sprintf(ni->last_pkt.wlan_src)); list_del(&ni->list); free(ni); } /* free essids */ list_for_each_safe(&essids.list, e, f, list) { DEBUG("free essid '%s'\n", e->essid); list_del(&e->list); free(e); } /* free channel nodes */ for (i = 0; i < channel_get_num_channels(); i++) { list_for_each_safe(&spectrum[i].nodes, cn, cn2, chan_list) { DEBUG("free chan_node %p\n", cn); list_del(&cn->chan_list); cn->chan->num_nodes--; free(cn); } } } static void exit_handler(void) { free_lists(); if (conf.serveraddr[0] == '\0') { close_packet_socket(mon); } ifctrl_flags(conf.ifname, false, false); if (conf.monitor_added) ifctrl_iwdel(conf.ifname); if (DF != NULL) { fclose(DF); DF = NULL; } if (conf.allow_control) control_finish(); if (!conf.debug) net_finish(); if (!conf.quiet && !conf.debug) finish_display(); ifctrl_finish(); } static void sigint_handler(__attribute__((unused)) int sig) { /* Only set an atomic flag here to keep processing in the interrupt * context as minimal as possible (at least all unsafe functions are * prohibited, see signal(7)). The flag is handled in the mainloop. */ is_sigint_caught = 1; } static void sigpipe_handler(__attribute__((unused)) int sig) { /* ignore signal here - we will handle it after write failed */ } void init_spectrum(void) { int i; for (i = 0; i < MAX_CHANNELS; i++) { list_head_init(&spectrum[i].nodes); ewma_init(&spectrum[i].signal_avg, 1024, 8); ewma_init(&spectrum[i].durations_avg, 1024, 8); } } static void mac_name_file_read(const char* filename) { FILE* fp; char line[255]; char macs[18]; char name[18]; int idx = 0; int n; if ((fp = fopen(filename, "r")) == NULL) { printlog("Could not open mac name file '%s'", filename); return; } while (fgets(line, sizeof(line), fp) != NULL) { // first try dnsmasq dhcp.leases format n = sscanf(line, "%*s %17s %*s %17s", macs, name); if (n < 2) // if not MAC name n = sscanf(line, "%17s %17s", macs, name); if (n == 2) { convert_string_to_mac(macs, node_names.entry[idx].mac); strncpy(node_names.entry[idx].name, name, MAX_NODE_NAME_STRLEN); node_names.entry[idx].name[MAX_NODE_NAME_STRLEN] = '\0'; idx++; } } fclose(fp); node_names.count = idx; for (n = 0; n < node_names.count; n++) { printlog("MAC %s = %s", ether_sprintf(node_names.entry[n].mac), node_names.entry[n].name ); } } const char* mac_name_lookup(const unsigned char* mac, int shorten_mac) { int i; if (conf.mac_name_lookup) { for (i = 0; i < node_names.count; i++) { if (memcmp(node_names.entry[i].mac, mac, MAC_LEN) == 0) return node_names.entry[i].name; } } return shorten_mac ? ether_sprintf_short(mac) : ether_sprintf(mac); } static void generate_mon_ifname(char *const buf, const size_t buf_size) { unsigned int i; for (i=0;; ++i) { int len; len = snprintf(buf, buf_size, "horst%d", i); if (len < 0) err(1, "failed to generate monitor interface name"); if ((unsigned int) len >= buf_size) errx(1, "failed to generate a sufficiently short " "monitor interface name"); if (!if_nametoindex(buf)) break; /* interface does not exist yet, done */ } } int main(int argc, char** argv) { sigset_t workmask; sigset_t waitmask; struct sigaction sigint_action; struct sigaction sigpipe_action; list_head_init(&essids.list); list_head_init(&nodes); init_spectrum(); config_parse_file_and_cmdline(argc, argv); sigint_action.sa_handler = sigint_handler; sigemptyset(&sigint_action.sa_mask); sigint_action.sa_flags = 0; sigaction(SIGINT, &sigint_action, NULL); sigaction(SIGTERM, &sigint_action, NULL); sigaction(SIGHUP, &sigint_action, NULL); sigpipe_action.sa_handler = sigpipe_handler; sigaction(SIGPIPE, &sigpipe_action, NULL); atexit(exit_handler); clock_gettime(CLOCK_MONOTONIC, &stats.stats_time); clock_gettime(CLOCK_MONOTONIC, &the_time); conf.channel_idx = -1; if (conf.mac_name_lookup) mac_name_file_read(conf.mac_name_file); if (conf.allow_control) { printlog("Allowing control socket '%s'", conf.control_pipe); control_init_pipe(); } if (conf.serveraddr[0] != '\0') mon = net_open_client_socket(conf.serveraddr, conf.port); else { ifctrl_init(); ifctrl_iwget_interface_info(conf.ifname); /* if the interface is not already in monitor mode, try to set * it to monitor or create an additional virtual monitor interface */ if (conf.add_monitor || (!ifctrl_is_monitor() && !ifctrl_iwset_monitor(conf.ifname))) { char mon_ifname[IF_NAMESIZE]; generate_mon_ifname(mon_ifname, IF_NAMESIZE); if (!ifctrl_iwadd_monitor(conf.ifname, mon_ifname)) err(1, "failed to add virtual monitor interface"); printlog("INFO: A virtual interface '%s' will be used " "instead of '%s'.", mon_ifname, conf.ifname); strncpy(conf.ifname, mon_ifname, IF_NAMESIZE); conf.monitor_added = 1; /* Now we have a new monitor interface, proceed * normally. The interface will be deleted at exit. */ } if (!ifctrl_flags(conf.ifname, true, true)) err(1, "failed to bring interface '%s' up", conf.ifname); /* get info again, as chan width is only available on UP interfaces */ ifctrl_iwget_interface_info(conf.ifname); mon = open_packet_socket(conf.ifname, conf.recv_buffer_size); if (mon <= 0) err(1, "Couldn't open packet socket"); conf.arphrd = device_get_hwinfo(mon, conf.ifname, conf.my_mac_addr); if (conf.arphrd != ARPHRD_IEEE80211_PRISM && conf.arphrd != ARPHRD_IEEE80211_RADIOTAP) err(1, "interface '%s' is not in monitor mode", conf.ifname); if (!channel_init() && conf.quiet) err(1, "failed to change the initial channel number"); } printf("Max PHY rate: %d Mbps\n", conf.max_phy_rate/10); if (!conf.quiet && !conf.debug) init_display(); if (conf.serveraddr[0] == '\0' && conf.port && conf.allow_client) net_init_server_socket(conf.port); /* Race-free signal handling: * 1. block all handled signals while working (with workmask) * 2. receive signals *only* while waiting in pselect() (with waitmask) * 3. switch between these two masks atomically with pselect() */ if (sigemptyset(&workmask) == -1 || sigaddset(&workmask, SIGINT) == -1 || sigaddset(&workmask, SIGHUP) == -1 || sigaddset(&workmask, SIGTERM) == -1 || sigprocmask(SIG_BLOCK, &workmask, &waitmask) == -1) err(1, "failed to block signals: %m"); while (!conf.do_change_channel || conf.channel_scan_rounds != 0) { receive_any(&waitmask); if (is_sigint_caught) exit(1); clock_gettime(CLOCK_MONOTONIC, &the_time); node_timeout(); if (conf.serveraddr[0] == '\0') { /* server */ if (!conf.paused && channel_auto_change()) { net_send_channel_config(); update_spectrum_durations(); if (!conf.quiet && !conf.debug) update_display(NULL); if (channel_get_chan(conf.channel_idx) == conf.channel_set_num && conf.channel_scan_rounds > 0) --conf.channel_scan_rounds; } } } return 0; } void main_pause(int pause) { conf.paused = pause; printlog(conf.paused ? "- PAUSED -" : "- RESUME -"); } void main_reset(void) { if (!conf.quiet && !conf.debug) display_clear(); printlog("- RESET -"); free_lists(); essids.split_active = 0; essids.split_essid = NULL; memset(&hist, 0, sizeof(hist)); memset(&stats, 0, sizeof(stats)); memset(&spectrum, 0, sizeof(spectrum)); init_spectrum(); clock_gettime(CLOCK_MONOTONIC, &stats.stats_time); } void dumpfile_open(const char* name) { if (DF != NULL) { fclose(DF); DF = NULL; } if (name == NULL || strlen(name) == 0) { printlog("- Not writing outfile"); conf.dumpfile[0] = '\0'; return; } strncpy(conf.dumpfile, name, MAX_CONF_VALUE_STRLEN); conf.dumpfile[MAX_CONF_VALUE_STRLEN] = '\0'; DF = fopen(conf.dumpfile, "w"); if (DF == NULL) err(1, "Couldn't open dump file"); fprintf(DF, "TIME, WLAN TYPE, MAC SRC, MAC DST, BSSID, PACKET TYPES, SIGNAL, "); fprintf(DF, "LENGTH, PHY RATE, FREQUENCY, TSF, ESSID, MODE, CHANNEL, "); fprintf(DF, "WEP, WPA1, RSN (WPA2), IP SRC, IP DST\n"); printlog("- Writing to outfile %s", conf.dumpfile); } #if 0 void print_rate_duration_table(void) { int i; printf("LEN\t1M l\t1M s\t2M l\t2M s\t5.5M l\t5.5M s\t11M l\t11M s\t"); printf("6M\t9\t12M\t18M\t24M\t36M\t48M\t54M\n"); for (i=10; i<=2304; i+=10) { printf("%d:\t%d\t%d\t", i, ieee80211_frame_duration(PHY_FLAG_G, i, 10, 0, 0, IEEE80211_FTYPE_DATA, 0, 0), ieee80211_frame_duration(PHY_FLAG_G, i, 10, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); printf("%d\t%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 20, 0, 0, IEEE80211_FTYPE_DATA, 0, 0), ieee80211_frame_duration(PHY_FLAG_G, i, 20, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); printf("%d\t%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 55, 0, 0, IEEE80211_FTYPE_DATA, 0, 0), ieee80211_frame_duration(PHY_FLAG_G, i, 55, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); printf("%d\t%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 110, 0, 0, IEEE80211_FTYPE_DATA, 0, 0), ieee80211_frame_duration(PHY_FLAG_G, i, 110, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); printf("%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 60, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); printf("%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 90, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); printf("%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 120, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)), printf("%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 180, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)), printf("%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 240, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)), printf("%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 360, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); printf("%d\t", ieee80211_frame_duration(PHY_FLAG_G, i, 480, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)), printf("%d\n", ieee80211_frame_duration(PHY_FLAG_G, i, 540, 1, 0, IEEE80211_FTYPE_DATA, 0, 0)); } } #endif horst-version-5.0/main.h000066400000000000000000000233121273543766500153200ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _MAIN_H_ #define _MAIN_H_ #include #include #undef LIST_HEAD #include "ccan/list/list.h" #include "average.h" #include "channel.h" #include "wlan80211.h" #define VERSION "5.0-pre" #define CONFIG_FILE "/etc/horst.conf" #ifndef DO_DEBUG #define DO_DEBUG 0 #endif #if DO_DEBUG #define DEBUG(...) do { if (conf.debug) printf(__VA_ARGS__); } while (0) #else #define DEBUG(...) #endif /* #include conflicts with in ifctrl-wext and there does * not seem to be a better solution that to just define IF_NAMESIZE ourselves */ #define IF_NAMESIZE 16 #define MAC_LEN 6 #define MAX_CONF_VALUE_STRLEN 200 #define MAX_CONF_NAME_STRLEN 32 #define MAX_HISTORY 255 #define MAX_RATES 44 /* 12 legacy rates and 32 MCS */ #define MAX_FSTYPE 0xff #define MAX_FILTERMAC 9 #define MAX_NODE_NAME_STRLEN 18 #define MAX_NODE_NAMES 64 /* higher level packet types */ #define PKT_TYPE_ARP BIT(0) #define PKT_TYPE_IP BIT(1) #define PKT_TYPE_ICMP BIT(2) #define PKT_TYPE_UDP BIT(3) #define PKT_TYPE_TCP BIT(4) #define PKT_TYPE_OLSR BIT(5) #define PKT_TYPE_BATMAN BIT(6) #define PKT_TYPE_MESHZ BIT(7) #define PKT_TYPE_ALL (PKT_TYPE_ARP | PKT_TYPE_IP | PKT_TYPE_ICMP | \ PKT_TYPE_UDP | PKT_TYPE_TCP | \ PKT_TYPE_OLSR | PKT_TYPE_BATMAN | PKT_TYPE_MESHZ) #define WLAN_MODE_AP BIT(0) #define WLAN_MODE_IBSS BIT(1) #define WLAN_MODE_STA BIT(2) #define WLAN_MODE_PROBE BIT(3) #define WLAN_MODE_4ADDR BIT(4) #define WLAN_MODE_UNKNOWN BIT(5) #define WLAN_MODE_ALL (WLAN_MODE_AP | WLAN_MODE_IBSS | WLAN_MODE_STA | WLAN_MODE_PROBE | WLAN_MODE_4ADDR | WLAN_MODE_UNKNOWN) #define PHY_FLAG_SHORTPRE BIT(0) #define PHY_FLAG_BADFCS BIT(1) #define PHY_FLAG_A BIT(2) #define PHY_FLAG_B BIT(3) #define PHY_FLAG_G BIT(4) #define PHY_FLAG_MODE_MASK BIT(5) #ifndef ARPHRD_IEEE80211_RADIOTAP #define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */ #endif #ifndef ARPHRD_IEEE80211_PRISM #define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */ #endif #define DEFAULT_MAC_NAME_FILE "/tmp/dhcp.leases" struct packet_info { /* general */ unsigned int pkt_types; /* bitmask of packet types */ /* wlan phy (from radiotap) */ int phy_signal; /* signal strength (usually dBm) */ unsigned int phy_rate; /* physical rate * 10 (=in 100kbps) */ unsigned char phy_rate_idx; /* MCS index */ unsigned char phy_rate_flags; /* MCS flags */ unsigned int phy_freq; /* frequency from driver */ unsigned int phy_flags; /* A, B, G, shortpre */ /* wlan mac */ unsigned int wlan_len; /* packet length */ uint16_t wlan_type; /* frame control field */ unsigned char wlan_src[MAC_LEN]; /* transmitter (TA) */ unsigned char wlan_dst[MAC_LEN]; /* receiver (RA) */ unsigned char wlan_bssid[MAC_LEN]; char wlan_essid[WLAN_MAX_SSID_LEN]; uint64_t wlan_tsf; /* timestamp from beacon */ unsigned int wlan_bintval; /* beacon interval */ unsigned int wlan_mode; /* AP, STA or IBSS */ unsigned char wlan_channel; /* channel from beacon, probe */ enum chan_width wlan_chan_width; unsigned char wlan_tx_streams; unsigned char wlan_rx_streams; unsigned char wlan_qos_class; /* for QDATA frames */ unsigned int wlan_nav; /* frame NAV duration */ unsigned int wlan_seqno; /* sequence number */ /* flags */ unsigned int wlan_wep:1, /* WEP on/off */ wlan_retry:1, wlan_wpa:1, wlan_rsn:1, wlan_ht40plus:1; /* batman-adv */ unsigned char bat_version; unsigned char bat_packet_type; unsigned char bat_gw:1; /* IP */ unsigned int ip_src; unsigned int ip_dst; unsigned int tcpudp_port; unsigned int olsr_type; unsigned int olsr_neigh; unsigned int olsr_tc; /* calculated from other values */ unsigned int pkt_duration; /* packet "airtime" */ int pkt_chan_idx; /* received while on channel */ int wlan_retries; /* retry count for this frame */ }; struct essid_info; struct node_info { /* housekeeping */ struct list_node list; struct list_node essid_nodes; struct list_head on_channels; /* channels this node was seen on */ unsigned int num_on_channels; time_t last_seen; /* timestamp */ /* general packet info */ unsigned int pkt_types; /* bitmask of packet types we've seen */ unsigned int pkt_count; /* nr of packets seen */ /* wlan phy (from radiotap) */ int phy_sig_max; struct ewma phy_sig_avg; unsigned long phy_sig_sum; int phy_sig_count; /* wlan mac */ unsigned char wlan_bssid[MAC_LEN]; unsigned int wlan_channel; /* channel from beacon, probe frames */ unsigned int wlan_mode; /* AP, STA or IBSS */ uint64_t wlan_tsf; unsigned int wlan_bintval; unsigned int wlan_retries_all; unsigned int wlan_retries_last; unsigned int wlan_seqno; struct essid_info* essid; struct node_info* wlan_ap_node; enum chan_width wlan_chan_width; unsigned char wlan_tx_streams; unsigned char wlan_rx_streams; unsigned int wlan_wep:1, /* WEP active? */ wlan_wpa:1, wlan_rsn:1, wlan_ht40plus:1; /* batman */ unsigned char bat_gw:1; /* IP */ unsigned int ip_src; /* IP address (if known) */ unsigned int olsr_count; /* number of OLSR packets */ unsigned int olsr_neigh; /* number if OLSR neighbours */ unsigned int olsr_tc; /* unused */ struct packet_info last_pkt; }; extern struct list_head nodes; struct essid_info { struct list_node list; char essid[WLAN_MAX_SSID_LEN]; struct list_head nodes; unsigned int num_nodes; int split; }; struct essid_meta_info { struct list_head list; struct essid_info* split_essid; int split_active; }; extern struct essid_meta_info essids; struct history { int signal[MAX_HISTORY]; int rate[MAX_HISTORY]; unsigned int type[MAX_HISTORY]; unsigned int retry[MAX_HISTORY]; unsigned int index; }; extern struct history hist; struct statistics { unsigned long packets; unsigned long retries; unsigned long bytes; unsigned long duration; unsigned long packets_per_rate[MAX_RATES]; unsigned long bytes_per_rate[MAX_RATES]; unsigned long duration_per_rate[MAX_RATES]; unsigned long packets_per_type[MAX_FSTYPE]; unsigned long bytes_per_type[MAX_FSTYPE]; unsigned long duration_per_type[MAX_FSTYPE]; unsigned long filtered_packets; struct timespec stats_time; }; extern struct statistics stats; struct channel_info { int signal; struct ewma signal_avg; unsigned long packets; unsigned long bytes; unsigned long durations; unsigned long durations_last; struct ewma durations_avg; struct list_head nodes; unsigned int num_nodes; }; extern struct channel_info spectrum[MAX_CHANNELS]; /* helper for keeping lists of nodes for each channel * (a node can be on more than one channel) */ struct chan_node { struct node_info* node; struct channel_info* chan; struct list_node chan_list; /* list for nodes per channel */ struct list_node node_list; /* list for channels per node */ int sig; struct ewma sig_avg; unsigned long packets; }; struct node_names_info { struct node_name { unsigned char mac[MAC_LEN]; char name[MAX_NODE_NAME_STRLEN + 1]; } entry[MAX_NODE_NAMES]; int count; }; extern struct node_names_info node_names; struct config { char ifname[IF_NAMESIZE + 1]; int port; int quiet; int node_timeout; int channel_time; int channel_max; int channel_set_num; /* value we want to set */ enum chan_width channel_set_width; /* value we want to set */ enum chan_width channel_width; int channel_idx; /* index into channels array */ int channel_scan_rounds; int display_interval; char display_view; char dumpfile[MAX_CONF_VALUE_STRLEN + 1]; int recv_buffer_size; char serveraddr[MAX_CONF_VALUE_STRLEN + 1]; char control_pipe[MAX_CONF_VALUE_STRLEN + 1]; char mac_name_file[MAX_CONF_VALUE_STRLEN + 1]; unsigned char filtermac[MAX_FILTERMAC][MAC_LEN]; char filtermac_enabled[MAX_FILTERMAC]; unsigned char filterbssid[MAC_LEN]; unsigned int filter_pkt; uint16_t filter_stype[WLAN_NUM_TYPES]; /* one for MGMT, CTRL, DATA */ unsigned int filter_mode; unsigned int filter_off:1, filter_badfcs:1, do_change_channel:1, channel_ht40plus:1, /* channel is HT40+ */ channel_set_ht40plus:1, /* value we want to set */ allow_client:1, allow_control:1, debug:1, mac_name_lookup:1, add_monitor:1, /* this isn't exactly config, but wtf... */ do_macfilter:1, display_initialized:1, channel_initialized:1, monitor_added:1; int arphrd; // the device ARP type unsigned char my_mac_addr[MAC_LEN]; int paused; int if_type; int if_phy; unsigned int if_freq; unsigned int max_phy_rate; }; extern struct config conf; extern struct timespec the_time; void free_lists(void); void init_spectrum(void); void update_spectrum_durations(void); void handle_packet(struct packet_info* p); void __attribute__ ((format (printf, 1, 2))) printlog(const char *fmt, ...); void main_pause(int pause); void main_reset(void); void dumpfile_open(const char* name); const char* mac_name_lookup(const unsigned char* mac, int shorten_mac); #endif horst-version-5.0/network.c000066400000000000000000000415341273543766500160660ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "main.h" #include "util.h" #include "channel.h" #include "network.h" #include "display.h" extern struct config conf; int srv_fd = -1; int cli_fd = -1; static int netmon_fd; #define PROTO_VERSION 3 enum pkt_type { PROTO_PKT_INFO = 0, PROTO_CHAN_LIST = 1, PROTO_CONF_CHAN = 2, PROTO_CONF_FILTER = 3, }; struct net_header { unsigned char version; unsigned char type; } __attribute__ ((packed)); struct net_conf_chan { struct net_header proto; unsigned char do_change; unsigned char upper; char channel; #define NET_WIDTH_HT40PLUS 0x80 unsigned char width_ht40p; // use upper bit for HT40+- int dwell_time; } __attribute__ ((packed)); struct net_conf_filter { struct net_header proto; unsigned char filtermac[MAX_FILTERMAC][MAC_LEN]; char filtermac_enabled[MAX_FILTERMAC]; unsigned char filterbssid[MAC_LEN]; uint16_t filter_stype[WLAN_NUM_TYPES]; int filter_pkt; int filter_mode; #define NET_FILTER_OFF 0x01 #define NET_FILTER_BADFCS 0x02 unsigned char filter_flags; } __attribute__ ((packed)); struct net_band { unsigned char num_chans; unsigned char max_width; unsigned char streams_rx; unsigned char streams_tx; } __attribute__ ((packed)); struct net_chan_list { struct net_header proto; unsigned char num_bands; struct net_band band[2]; // always send both unsigned int freq[1]; } __attribute__ ((packed)); #define PKT_INFO_VERSION 2 struct net_packet_info { struct net_header proto; unsigned char version; /* general */ unsigned int pkt_types; /* bitmask of packet types */ /* wlan phy (from radiotap) */ int phy_signal; /* signal strength (usually dBm) */ unsigned int phy_rate; /* physical rate * 10 (= in 100kbps) */ unsigned char phy_rate_idx; unsigned char phy_rate_flags; unsigned int phy_freq; /* frequency */ unsigned int phy_flags; /* A, B, G, shortpre */ /* wlan mac */ unsigned int wlan_len; /* packet length */ unsigned int wlan_type; /* frame control field */ unsigned char wlan_src[MAC_LEN]; unsigned char wlan_dst[MAC_LEN]; unsigned char wlan_bssid[MAC_LEN]; char wlan_essid[WLAN_MAX_SSID_LEN]; uint64_t wlan_tsf; /* timestamp from beacon */ unsigned int wlan_bintval; /* beacon interval */ unsigned int wlan_mode; /* AP, STA or IBSS */ unsigned char wlan_channel; /* channel from beacon, probe */ unsigned char wlan_chan_width; unsigned char wlan_tx_streams; unsigned char wlan_rx_streams; unsigned char wlan_qos_class; /* for QDATA frames */ unsigned int wlan_nav; /* frame NAV duration */ unsigned int wlan_seqno; /* sequence number */ #define PKT_WLAN_FLAG_WEP 0x01 #define PKT_WLAN_FLAG_RETRY 0x02 #define PKT_WLAN_FLAG_WPA 0x04 #define PKT_WLAN_FLAG_RSN 0x08 #define PKT_WLAN_FLAG_HT40PLUS 0x10 /* bitfields are not portable - endianness is not guaranteed */ unsigned int wlan_flags; /* IP */ unsigned int ip_src; unsigned int ip_dst; unsigned int tcpudp_port; unsigned int olsr_type; unsigned int olsr_neigh; unsigned int olsr_tc; #define PKT_BAT_FLAG_GW 0x1 unsigned char bat_flags; unsigned char bat_pkt_type; } __attribute__ ((packed)); static bool net_write(int fd, unsigned char* buf, size_t len) { int ret; ret = write(fd, buf, len); if (ret == -1) { if (errno == EPIPE) { printlog("Client has closed"); close(fd); if (fd == cli_fd) cli_fd = -1; net_init_server_socket(conf.port); } else printlog("ERROR: in net_write"); return false; } return true; } void net_send_packet(struct packet_info *p) { struct net_packet_info np; np.proto.version = PROTO_VERSION; np.proto.type = PROTO_PKT_INFO; np.version = PKT_INFO_VERSION; np.pkt_types = htole32(p->pkt_types); np.phy_signal = htole32(p->phy_signal); np.phy_rate = htole32(p->phy_rate); np.phy_rate_idx = p->phy_rate_idx; np.phy_rate_flags = p->phy_rate_flags; np.phy_freq = htole32(p->phy_freq); np.phy_flags = htole32(p->phy_flags); np.wlan_len = htole32(p->wlan_len); np.wlan_type = htole32(p->wlan_type); memcpy(np.wlan_src, p->wlan_src, MAC_LEN); memcpy(np.wlan_dst, p->wlan_dst, MAC_LEN); memcpy(np.wlan_bssid, p->wlan_bssid, MAC_LEN); memcpy(np.wlan_essid, p->wlan_essid, WLAN_MAX_SSID_LEN); np.wlan_tsf = htole64(p->wlan_tsf); np.wlan_bintval = htole32(p->wlan_bintval); np.wlan_mode = htole32(p->wlan_mode); np.wlan_channel = p->wlan_channel; np.wlan_chan_width = p->wlan_chan_width; np.wlan_tx_streams = p->wlan_tx_streams; np.wlan_rx_streams = p->wlan_rx_streams; np.wlan_qos_class = p->wlan_qos_class; np.wlan_nav = htole32(p->wlan_nav); np.wlan_seqno = htole32(p->wlan_seqno); np.wlan_flags = 0; if (p->wlan_wep) np.wlan_flags |= PKT_WLAN_FLAG_WEP; if (p->wlan_retry) np.wlan_flags |= PKT_WLAN_FLAG_RETRY; if (p->wlan_wpa) np.wlan_flags |= PKT_WLAN_FLAG_WPA; if (p->wlan_rsn) np.wlan_flags |= PKT_WLAN_FLAG_RSN; if (p->wlan_ht40plus) np.wlan_flags |= PKT_WLAN_FLAG_HT40PLUS; np.wlan_flags = htole32(np.wlan_flags); np.ip_src = p->ip_src; np.ip_dst = p->ip_dst; np.tcpudp_port = htole32(p->tcpudp_port); np.olsr_type = htole32(p->olsr_type); np.olsr_neigh = htole32(p->olsr_neigh); np.olsr_tc = htole32(p->olsr_tc); np.bat_flags = 0; if (p->bat_gw) np.bat_flags |= PKT_BAT_FLAG_GW; np.bat_pkt_type = p->bat_packet_type; net_write(cli_fd, (unsigned char *)&np, sizeof(np)); } static int net_receive_packet(unsigned char *buffer, size_t len) { struct net_packet_info *np; struct packet_info p; if (len < sizeof(struct net_packet_info)) return 0; np = (struct net_packet_info *)buffer; if (np->phy_rate == 0) return 0; if (np->version != PKT_INFO_VERSION) return 0; memset(&p, 0, sizeof(p)); p.pkt_types = le32toh(np->pkt_types); p.phy_signal = le32toh(np->phy_signal); p.phy_rate = le32toh(np->phy_rate); p.phy_rate_idx = np->phy_rate_idx; p.phy_rate_flags= np->phy_rate_flags; p.phy_freq = le32toh(np->phy_freq); p.phy_flags = le32toh(np->phy_flags); p.wlan_len = le32toh(np->wlan_len); p.wlan_type = le32toh(np->wlan_type); memcpy(p.wlan_src, np->wlan_src, MAC_LEN); memcpy(p.wlan_dst, np->wlan_dst, MAC_LEN); memcpy(p.wlan_bssid, np->wlan_bssid, MAC_LEN); memcpy(p.wlan_essid, np->wlan_essid, WLAN_MAX_SSID_LEN); p.wlan_tsf = le64toh(np->wlan_tsf); p.wlan_bintval = le32toh(np->wlan_bintval); p.wlan_mode = le32toh(np->wlan_mode); p.wlan_channel = np->wlan_channel; p.wlan_chan_width = np->wlan_chan_width; p.wlan_tx_streams = np->wlan_tx_streams; p.wlan_rx_streams = np->wlan_rx_streams; p.wlan_qos_class = np->wlan_qos_class; p.wlan_nav = le32toh(np->wlan_nav); p.wlan_seqno = le32toh(np->wlan_seqno); np->wlan_flags = le32toh(np->wlan_flags); if (np->wlan_flags & PKT_WLAN_FLAG_WEP) p.wlan_wep = 1; if (np->wlan_flags & PKT_WLAN_FLAG_RETRY) p.wlan_retry = 1; if (np->wlan_flags & PKT_WLAN_FLAG_WPA) p.wlan_wpa = 1; if (np->wlan_flags & PKT_WLAN_FLAG_RSN) p.wlan_rsn = 1; if (np->wlan_flags & PKT_WLAN_FLAG_HT40PLUS) p.wlan_ht40plus = 1; p.ip_src = np->ip_src; p.ip_dst = np->ip_dst; p.tcpudp_port = le32toh(np->tcpudp_port); p.olsr_type = le32toh(np->olsr_type); p.olsr_neigh = le32toh(np->olsr_neigh); p.olsr_tc = le32toh(np->olsr_tc); if (np->bat_flags & PKT_BAT_FLAG_GW) p.bat_gw = 1; p.bat_packet_type = np->bat_pkt_type; handle_packet(&p); return sizeof(struct net_packet_info); } static void net_send_conf_chan(int fd) { struct net_conf_chan nc; nc.proto.version = PROTO_VERSION; nc.proto.type = PROTO_CONF_CHAN; nc.do_change = conf.do_change_channel; nc.upper = conf.channel_max; nc.channel = conf.channel_idx; nc.width_ht40p = conf.channel_width; if (conf.channel_ht40plus) nc.width_ht40p |= NET_WIDTH_HT40PLUS; nc.dwell_time = htole32(conf.channel_time); net_write(fd, (unsigned char *)&nc, sizeof(nc)); } static int net_receive_conf_chan(unsigned char *buffer, size_t len) { struct net_conf_chan *nc; if (len < sizeof(struct net_conf_chan)) return 0; nc = (struct net_conf_chan *)buffer; conf.do_change_channel = nc->do_change; conf.channel_max = nc->upper; conf.channel_time = le32toh(nc->dwell_time); enum chan_width width = nc->width_ht40p & ~NET_WIDTH_HT40PLUS; bool ht40p = !!(nc->width_ht40p & NET_WIDTH_HT40PLUS); if (nc->channel != conf.channel_idx || width != conf.channel_width || ht40p != conf.channel_ht40plus) { /* something changed */ if (cli_fd > -1) { /* server */ if (!channel_change(nc->channel, width, ht40p)) { printlog("Net Channel %d %s is not available/allowed", channel_get_chan(nc->channel), channel_width_string(width, ht40p)); net_send_channel_config(); } else { /* success: update UI */ conf.channel_set_num = channel_get_chan(nc->channel); conf.channel_set_width = width; conf.channel_set_ht40plus = ht40p; update_display(NULL); } } else { /* client */ conf.channel_idx = nc->channel; conf.channel_width = conf.channel_set_width = width; conf.channel_ht40plus = conf.channel_set_ht40plus = ht40p; conf.channel_set_num = channel_get_chan(nc->channel); update_spectrum_durations(); update_display(NULL); } } return sizeof(struct net_conf_chan); } static void net_send_conf_filter(int fd) { struct net_conf_filter nc; int i; nc.proto.version = PROTO_VERSION; nc.proto.type = PROTO_CONF_FILTER; for (i = 0; i < MAX_FILTERMAC; i++) { memcpy(nc.filtermac[i], conf.filtermac[i], MAC_LEN); nc.filtermac_enabled[i] = conf.filtermac_enabled[i]; } for (i = 0; i < WLAN_NUM_TYPES; i++) { nc.filter_stype[i] = htons(conf.filter_stype[i]); } memcpy(nc.filterbssid, conf.filterbssid, MAC_LEN); nc.filter_pkt = htole32(conf.filter_pkt); nc.filter_mode = htole32(conf.filter_mode); nc.filter_flags = 0; if (conf.filter_off) nc.filter_flags |= NET_FILTER_OFF; if (conf.filter_badfcs) nc.filter_flags |= NET_FILTER_BADFCS; net_write(fd, (unsigned char *)&nc, sizeof(nc)); } static int net_receive_conf_filter(unsigned char *buffer, size_t len) { struct net_conf_filter *nc; int i; if (len < sizeof(struct net_conf_filter)) return 0; nc = (struct net_conf_filter *)buffer; for (i = 0; i < MAX_FILTERMAC; i++) { memcpy(conf.filtermac[i], nc->filtermac[i], MAC_LEN); conf.filtermac_enabled[i] = nc->filtermac_enabled[i]; } for (i = 0; i < WLAN_NUM_TYPES; i++) { conf.filter_stype[i] = ntohs(nc->filter_stype[i]); } memcpy(conf.filterbssid, nc->filterbssid, MAC_LEN); conf.filter_pkt = le32toh(nc->filter_pkt); conf.filter_mode = le32toh(nc->filter_mode); conf.filter_off = !!(nc->filter_flags & NET_FILTER_OFF); conf.filter_badfcs = !!(nc->filter_flags & NET_FILTER_BADFCS); return sizeof(struct net_conf_filter); } static void net_send_chan_list(int fd) { char* buf; struct net_chan_list *nc; int i; buf = malloc(sizeof(struct net_chan_list) + sizeof(unsigned int) * (channel_get_num_channels() - 1)); if (buf == NULL) return; nc = (struct net_chan_list *)buf; nc->proto.version = PROTO_VERSION; nc->proto.type = PROTO_CHAN_LIST; nc->num_bands = channel_get_num_bands(); for (i = 0; i < nc->num_bands; i++) { const struct band_info* bp = channel_get_band(i); nc->band[i].num_chans = bp->num_channels; nc->band[i].max_width = bp->max_chan_width; nc->band[i].streams_rx = bp->streams_rx; nc->band[i].streams_tx = bp->streams_tx; } for (i = 0; i < channel_get_num_channels(); i++) { nc->freq[i] = htole32(channel_get_freq(i)); DEBUG("NET send freq %d %d\n", i, channel_get_freq(i)); } net_write(fd, (unsigned char *)buf, sizeof(struct net_chan_list) + sizeof(unsigned int) * (i - 1)); free(buf); } static int net_receive_chan_list(unsigned char *buffer, size_t len) { struct net_chan_list *nc; int num_chans = 0; if (len < sizeof(struct net_chan_list)) return 0; nc = (struct net_chan_list *)buffer; for (int i = 0; i < nc->num_bands; i++) { channel_band_add(nc->band[i].num_chans, nc->band[i].max_width, nc->band[i].streams_rx, nc->band[i].streams_tx); num_chans += nc->band[i].num_chans; } if (len < sizeof(struct net_chan_list) + sizeof(unsigned int) * (num_chans - 1)) return 0; for (int i = 0; i < num_chans; i++) { channel_list_add(le32toh(nc->freq[i])); DEBUG("NET recv freq %d %d\n", i, le32toh(nc->freq[i])); } init_spectrum(); return sizeof(struct net_chan_list) + sizeof(unsigned int) * (num_chans - 1); } static int try_receive_packet(unsigned char* buf, size_t len) { struct net_header *nh = (struct net_header *)buf; if (nh->version != PROTO_VERSION) { printlog("ERROR: protocol version %x", nh->version); return 0; } switch (nh->type) { case PROTO_PKT_INFO: len = net_receive_packet(buf, len); break; case PROTO_CHAN_LIST: len = net_receive_chan_list(buf, len); break; case PROTO_CONF_CHAN: len = net_receive_conf_chan(buf, len); break; case PROTO_CONF_FILTER: len = net_receive_conf_filter(buf, len); break; default: printlog("ERROR: unknown net packet type"); len = 0; } return len; /* the number of bytes we have consumed */ } int net_receive(int fd, unsigned char* buffer, size_t* buflen, size_t maxlen) { int len, consumed = 0; len = recv(fd, buffer + *buflen, maxlen - *buflen, MSG_DONTWAIT); if (len < 0) return 0; *buflen += len; while (*buflen > sizeof(struct net_header)) { len = try_receive_packet(buffer + consumed, *buflen); if (len == 0) break; *buflen -= len; consumed += len; } memmove(buffer, buffer + consumed, *buflen); return consumed; } void net_handle_server_conn(void) { struct sockaddr_in cin; socklen_t cinlen; cinlen = sizeof(cin); memset(&cin, 0, sizeof(struct sockaddr_in)); cli_fd = accept(srv_fd, (struct sockaddr*)&cin, &cinlen); printlog("Accepting client"); /* send initial config */ net_send_chan_list(cli_fd); net_send_conf_chan(cli_fd); net_send_conf_filter(cli_fd); /* we only accept one client, so close server socket */ close(srv_fd); srv_fd = -1; } void net_init_server_socket(int rport) { struct sockaddr_in sock_in; int reuse = 1; printlog("Initializing server port %d", rport); memset(&sock_in, 0, sizeof(struct sockaddr_in)); sock_in.sin_family = AF_INET; sock_in.sin_addr.s_addr = htonl(INADDR_ANY); sock_in.sin_port = htons(rport); if ((srv_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err(1, "Could not open server socket"); if (setsockopt(srv_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) err(1, "setsockopt SO_REUSEADDR"); if (bind(srv_fd, (struct sockaddr*)&sock_in, sizeof(sock_in)) < 0) err(1, "bind"); if (listen(srv_fd, 0) < 0) err(1, "listen"); } int net_open_client_socket(char* serveraddr, int rport) { struct addrinfo saddr; struct addrinfo *result, *rp; char rport_str[20]; int ret; snprintf(rport_str, 20, "%d", rport); printlog("Connecting to server %s port %s", serveraddr, rport_str); /* Obtain address(es) matching host/port */ memset(&saddr, 0, sizeof(struct addrinfo)); saddr.ai_family = AF_INET; saddr.ai_socktype = SOCK_STREAM; saddr.ai_flags = 0; saddr.ai_protocol = 0; ret = getaddrinfo(serveraddr, rport_str, &saddr, &result); if (ret != 0) { fprintf(stderr, "Could not resolve: %s\n", gai_strerror(ret)); exit(EXIT_FAILURE); } /* getaddrinfo() returns a list of address structures. * Try each address until we successfully connect. */ for (rp = result; rp != NULL; rp = rp->ai_next) { netmon_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (netmon_fd == -1) continue; if (connect(netmon_fd, rp->ai_addr, rp->ai_addrlen) != -1) break; /* Success */ close(netmon_fd); } if (rp == NULL) { /* No address succeeded */ freeaddrinfo(result); err(1, "Could not connect"); } freeaddrinfo(result); printlog("Connected to server %s", serveraddr); return netmon_fd; } void net_finish(void) { if (srv_fd != -1) close(srv_fd); if (cli_fd != -1) close(cli_fd); if (netmon_fd) close(netmon_fd); } void net_send_channel_config(void) { if (conf.serveraddr[0] != '\0') net_send_conf_chan(netmon_fd); else if (conf.allow_client && cli_fd > -1) net_send_conf_chan(cli_fd); } void net_send_filter_config(void) { if (conf.serveraddr[0] != '\0') net_send_conf_filter(netmon_fd); else if (conf.allow_client && cli_fd > -1) net_send_conf_filter(cli_fd); } horst-version-5.0/network.h000066400000000000000000000024651273543766500160730ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PROTOCOL_NETWORK_H_ #define _PROTOCOL_NETWORK_H_ #include struct packet_info; extern int srv_fd; extern int cli_fd; void net_init_server_socket(int rport); void net_handle_server_conn(void); void net_send_packet(struct packet_info *pkt); void net_send_channel_config(void); void net_send_filter_config(void); int net_receive(int fd, unsigned char* buffer, size_t* buflen, size_t maxlen); int net_open_client_socket(char* server, int rport); void net_finish(void); #endif horst-version-5.0/node.c000066400000000000000000000131271273543766500153170ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "main.h" #include "util.h" #include "wlan80211.h" #include "essid.h" static struct timespec last_nodetimeout; static void copy_nodeinfo(struct node_info* n, struct packet_info* p) { struct node_info* ap; memcpy(&(n->last_pkt), p, sizeof(struct packet_info)); // update timestamp n->last_seen = time(NULL); n->pkt_count++; n->pkt_types |= p->pkt_types; if (p->ip_src) n->ip_src = p->ip_src; if (p->wlan_mode) n->wlan_mode |= p->wlan_mode; if (p->olsr_tc) n->olsr_tc = p->olsr_tc; if (p->olsr_neigh) n->olsr_neigh = p->olsr_neigh; if (p->pkt_types & PKT_TYPE_OLSR) n->olsr_count++; if (p->bat_gw) n->bat_gw = 1; if (p->wlan_ht40plus) n->wlan_ht40plus = 1; if (p->wlan_tx_streams) n->wlan_tx_streams = p->wlan_tx_streams; if (p->wlan_rx_streams) n->wlan_rx_streams = p->wlan_rx_streams; if (p->wlan_bssid[0] != 0xff && !(p->wlan_bssid[0] == 0 && p->wlan_bssid[1] == 0 && p->wlan_bssid[2] == 0 && p->wlan_bssid[3] == 0 && p->wlan_bssid[4] == 0 && p->wlan_bssid[5] == 0)) { memcpy(n->wlan_bssid, p->wlan_bssid, MAC_LEN); if ((n->wlan_mode & WLAN_MODE_STA) && n->wlan_ap_node == NULL) { /* find AP node for this BSSID */ list_for_each(&nodes, ap, list) { if (memcmp(p->wlan_bssid, ap->last_pkt.wlan_src, MAC_LEN) == 0) { DEBUG("AP node found %p\n", ap); DEBUG("AP node ESSID %s\n", ap->essid != NULL ? ap->essid->essid : "unknown"); n->wlan_ap_node = ap; break; } } n->wlan_rsn = ap->wlan_rsn; n->wlan_wpa = ap->wlan_wpa; } } if ((p->wlan_type == WLAN_FRAME_BEACON) || (p->wlan_type == WLAN_FRAME_PROBE_RESP)) { n->wlan_tsf = p->wlan_tsf; n->wlan_bintval = p->wlan_bintval; n->wlan_wpa = p->wlan_wpa; n->wlan_rsn = p->wlan_rsn; // Channel is only really known for Beacon and Probe response n->wlan_channel = p->wlan_channel; } else if ((n->wlan_mode & WLAN_MODE_STA) && n->wlan_ap_node != NULL) { // for STA we can use the channel from the AP n->wlan_channel = n->wlan_ap_node->wlan_channel; } else if (n->wlan_channel == 0 && p->wlan_channel != 0) { // otherwise only override if channel was unknown n->wlan_channel = p->wlan_channel; } ewma_add(&n->phy_sig_avg, -p->phy_signal); n->phy_sig_sum += -p->phy_signal; n->phy_sig_count += 1; if (p->phy_signal > n->phy_sig_max || n->phy_sig_max == 0) n->phy_sig_max = p->phy_signal; if ((p->wlan_type == WLAN_FRAME_DATA) || (p->wlan_type == WLAN_FRAME_QDATA) || (p->wlan_type == WLAN_FRAME_AUTH) || (p->wlan_type == WLAN_FRAME_BEACON) || (p->wlan_type == WLAN_FRAME_PROBE_RESP) || (p->wlan_type == WLAN_FRAME_DATA_CF_ACK) || (p->wlan_type == WLAN_FRAME_DATA_CF_POLL) || (p->wlan_type == WLAN_FRAME_DATA_CF_ACKPOLL) || (p->wlan_type == WLAN_FRAME_QDATA_CF_ACK) || (p->wlan_type == WLAN_FRAME_QDATA_CF_POLL) || (p->wlan_type == WLAN_FRAME_QDATA_CF_ACKPOLL)) n->wlan_wep = p->wlan_wep; if (p->wlan_seqno != 0) { if (p->wlan_retry && p->wlan_seqno == n->wlan_seqno) { n->wlan_retries_all++; n->wlan_retries_last++; } else n->wlan_retries_last = 0; n->wlan_seqno = p->wlan_seqno; } if (p->wlan_chan_width > n->wlan_chan_width) n->wlan_chan_width = p->wlan_chan_width; } struct node_info* node_update(struct packet_info* p) { struct node_info* n; if (p->phy_flags & PHY_FLAG_BADFCS) return NULL; if (p->wlan_src[0] == 0 && p->wlan_src[1] == 0 && p->wlan_src[2] == 0 && p->wlan_src[3] == 0 && p->wlan_src[4] == 0 && p->wlan_src[5] == 0) return NULL; /* find node by wlan source address */ list_for_each(&nodes, n, list) { if (memcmp(p->wlan_src, n->last_pkt.wlan_src, MAC_LEN) == 0) { DEBUG("node found %p\n", n); break; } } /* not found */ if (&n->list == &nodes.n) { DEBUG("node adding\n"); n = malloc(sizeof(struct node_info)); memset(n, 0, sizeof(struct node_info)); n->essid = NULL; ewma_init(&n->phy_sig_avg, 1024, 8); list_head_init(&n->on_channels); list_add_tail(&nodes, &n->list); } copy_nodeinfo(n, p); return n; } void node_timeout(void) { struct node_info *n, *m, *n2, *m2; struct chan_node *cn, *cn2; if ((the_time.tv_sec - last_nodetimeout.tv_sec) < conf.node_timeout ) return; list_for_each_safe(&nodes, n, m, list) { if (n->last_seen < (the_time.tv_sec - conf.node_timeout)) { list_del(&n->list); if (n->essid != NULL) remove_node_from_essid(n); list_for_each_safe(&n->on_channels, cn, cn2, node_list) { list_del(&cn->node_list); list_del(&cn->chan_list); cn->chan->num_nodes--; free(cn); } /* remove AP pointers to this node */ list_for_each_safe(&nodes, n2, m2, list) { if (n2->wlan_ap_node == n) { DEBUG("remove AP ref\n"); n->wlan_ap_node = NULL; } } free(n); } } last_nodetimeout = the_time; } horst-version-5.0/node.h000066400000000000000000000016771273543766500153330ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _NODE_H_ #define _NODE_H_ struct node_info* node_update(struct packet_info* p); void node_timeout(void); #endif horst-version-5.0/olsr_header.h000066400000000000000000000123061273543766500166640ustar00rootroot00000000000000/* copied from olsr olsr_protocol.h */ /* * The olsr.org Optimized Link-State Routing daemon(olsrd) * Copyright (c) 2004, Andreas T�nnesen(andreto@olsr.org) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of olsr.org, olsrd nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Visit http://www.olsr.org for more information. * * If you find this software useful feel free to make a donation * to the project. For more information see the website or contact * the copyright holders. * * $Id: olsr_protocol.h,v 1.23 2007/11/08 22:47:41 bernd67 Exp $ */ #ifndef _OLSR_HEADER_H_ #define _OLSR_HEADER_H_ #include typedef uint8_t olsr_u8_t; typedef uint16_t olsr_u16_t; typedef uint32_t olsr_u32_t; /* from olsr olsr_protocol.h */ /*********************************************** * OLSR packet definitions * ***********************************************/ /* *Hello info */ struct hellinfo { olsr_u8_t link_code; olsr_u8_t reserved; olsr_u16_t size; olsr_u32_t neigh_addr[1]; /* neighbor IP address(es) */ } __attribute__ ((packed)); struct hellomsg { olsr_u16_t reserved; olsr_u8_t htime; olsr_u8_t willingness; struct hellinfo hell_info[1]; } __attribute__ ((packed)); /* * Topology Control packet */ struct neigh_info { olsr_u32_t addr; } __attribute__ ((packed)); struct tcmsg { olsr_u16_t ansn; olsr_u16_t reserved; struct neigh_info neigh[1]; } __attribute__ ((packed)); /* *Multiple Interface Declaration message */ /* * Defined as s struct for further expansion * For example: do we want to tell what type of interface * is associated whit each address? */ struct midaddr { olsr_u32_t addr; } __attribute__ ((packed)); struct midmsg { struct midaddr mid_addr[1]; } __attribute__ ((packed)); /* * Host and Network Association message */ struct hnapair { olsr_u32_t addr; olsr_u32_t netmask; } __attribute__ ((packed)); struct hnamsg { struct hnapair hna_net[1]; } __attribute__ ((packed)); /* * OLSR message (several can exist in one OLSR packet) */ struct olsrmsg { olsr_u8_t olsr_msgtype; olsr_u8_t olsr_vtime; olsr_u16_t olsr_msgsize; olsr_u32_t originator; olsr_u8_t ttl; olsr_u8_t hopcnt; olsr_u16_t seqno; union { struct hellomsg hello; struct tcmsg tc; struct hnamsg hna; struct midmsg mid; } message; } __attribute__ ((packed)); /* * Generic OLSR packet */ struct olsr { olsr_u16_t olsr_packlen; /* packet length */ olsr_u16_t olsr_seqno; struct olsrmsg olsr_msg[1]; /* variable messages */ } __attribute__ ((packed)); /* *Message Types */ #define HELLO_MESSAGE 1 #define TC_MESSAGE 2 #define MID_MESSAGE 3 #define HNA_MESSAGE 4 #define LQ_HELLO_MESSAGE 201 #define LQ_TC_MESSAGE 202 /* *Link Types */ #define UNSPEC_LINK 0 #define ASYM_LINK 1 #define SYM_LINK 2 #define LOST_LINK 3 #define HIDE_LINK 4 #define MAX_LINK 4 /* *Neighbor Types */ #define NOT_NEIGH 0 #define SYM_NEIGH 1 #define MPR_NEIGH 2 #define MAX_NEIGH 2 /* *Neighbor status */ #define NOT_SYM 0 #define SYM 1 // serialized IPv4 OLSR header struct olsr_header_v4 { olsr_u8_t type; olsr_u8_t vtime; olsr_u16_t size; olsr_u32_t orig; olsr_u8_t ttl; olsr_u8_t hops; olsr_u16_t seqno; }; // serialized LQ_HELLO struct lq_hello_info_header { olsr_u8_t link_code; olsr_u8_t reserved; olsr_u16_t size; }; struct lq_hello_header { olsr_u16_t reserved; olsr_u8_t htime; olsr_u8_t will; }; // serialized LQ_TC struct lq_tc_header { olsr_u16_t ansn; olsr_u16_t reserved; }; #endif horst-version-5.0/prism_header.h000066400000000000000000000057421273543766500170450ustar00rootroot00000000000000/* copied from madwifi net80211/ieee80211_monitor.h */ /*- * Copyright (c) 2005 John Bicket * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: ieee80211_monitor.h 2568 2007-07-09 13:37:26Z proski $ */ #ifndef _PRISM_HEADER_H_ #define _PRISM_HEADER_H_ #include enum { DIDmsg_lnxind_wlansniffrm = 0x00000044, DIDmsg_lnxind_wlansniffrm_hosttime = 0x00010044, DIDmsg_lnxind_wlansniffrm_mactime = 0x00020044, DIDmsg_lnxind_wlansniffrm_channel = 0x00030044, DIDmsg_lnxind_wlansniffrm_rssi = 0x00040044, DIDmsg_lnxind_wlansniffrm_sq = 0x00050044, DIDmsg_lnxind_wlansniffrm_signal = 0x00060044, DIDmsg_lnxind_wlansniffrm_noise = 0x00070044, DIDmsg_lnxind_wlansniffrm_rate = 0x00080044, DIDmsg_lnxind_wlansniffrm_istx = 0x00090044, DIDmsg_lnxind_wlansniffrm_frmlen = 0x000A0044 }; enum { P80211ENUM_msgitem_status_no_value = 0x00 }; enum { P80211ENUM_truth_false = 0x00, P80211ENUM_truth_true = 0x01 }; typedef struct { uint32_t did; uint16_t status; uint16_t len; uint32_t data; } p80211item_uint32_t; typedef struct { uint32_t msgcode; uint32_t msglen; #define WLAN_DEVNAMELEN_MAX 16 uint8_t devname[WLAN_DEVNAMELEN_MAX]; p80211item_uint32_t hosttime; p80211item_uint32_t mactime; p80211item_uint32_t channel; p80211item_uint32_t rssi; p80211item_uint32_t sq; p80211item_uint32_t signal; p80211item_uint32_t noise; p80211item_uint32_t rate; p80211item_uint32_t istx; p80211item_uint32_t frmlen; } wlan_ng_prism2_header; #define PRISM_HEADER_LEN sizeof(wlan_ng_prism2_header) #endif horst-version-5.0/protocol_parser.c000066400000000000000000000172401273543766500176070ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #define __FAVOR_BSD #include #include "olsr_header.h" #include "batman_header.h" #include "batman_adv_header-14.h" #include "main.h" #include "util.h" extern int wlan_parse_packet(unsigned char* buf, size_t len, struct packet_info* p); static int parse_llc(unsigned char* buf, size_t len, struct packet_info* p); static int parse_ip_header(unsigned char* buf, size_t len, struct packet_info* p); static int parse_udp_header(unsigned char* buf, size_t len, struct packet_info* p); static int parse_olsr_packet(unsigned char* buf, size_t len, struct packet_info* p); static int parse_batman_packet(unsigned char* buf, size_t len, struct packet_info* p); static int parse_batman_adv_packet(unsigned char* buf, size_t len, struct packet_info* p); static int parse_meshcruzer_packet(unsigned char* buf, size_t len, struct packet_info* p, int port); /* return true if we parsed enough = min ieee header */ bool parse_packet(unsigned char* buf, size_t len, struct packet_info* p) { int ret = wlan_parse_packet(buf, len, p); if (ret == 0) return true; else if (ret < 0) return false; len -= ret; buf += ret; ret = parse_llc(buf, len, p); if (ret <= 0) return true; len -= ret; buf += ret; ret = parse_ip_header(buf, len, p); if (ret <= 0) return true; len -= ret; buf += ret; parse_udp_header(buf, len, p); return true; } static int parse_llc(unsigned char* buf, size_t len, struct packet_info* p) { DEBUG("* parse LLC\n"); if (len < 6) return -1; /* check type in LLC header */ buf = buf + 6; if (ntohs(*((uint16_t*)buf)) == 0x4305) { DEBUG("BATMAN-ADV\n"); buf++; buf++; return parse_batman_adv_packet(buf, len - 8, p); } else { if (*buf != 0x08) return -1; buf++; if (*buf == 0x06) { /* ARP */ p->pkt_types |= PKT_TYPE_ARP; return 0; } if (*buf != 0x00) /* not IP */ return -1; buf++; DEBUG("* parse LLC left %zd\n", len - 8); return 8; } } static int parse_batman_adv_packet(unsigned char* buf, size_t len, struct packet_info* p) { struct batman_ogm_packet *bp; //batadv_ogm_packet bp = (struct batman_ogm_packet*)buf; p->pkt_types |= PKT_TYPE_BATMAN; p->bat_version = bp->version; p->bat_packet_type = bp->packet_type; DEBUG("parse bat len %zd type %d vers %d\n", len, bp->packet_type, bp->version); /* version 14 */ if (bp->version == 14) { switch (bp->packet_type) { case BAT_OGM: /* set GW flags only for "original" (not re-sent) OGMs */ if (bp->gw_flags != 0 && memcmp(bp->orig, p->wlan_src, MAC_LEN) == 0) p->bat_gw = 1; DEBUG("OGM %d %d\n", bp->gw_flags, p->bat_gw); return 0; case BAT_ICMP: DEBUG("ICMP\n"); break; case BAT_UNICAST: DEBUG("UNI %zu\n", sizeof(struct unicast_packet)); return sizeof(struct unicast_packet) + 14; case BAT_BCAST: DEBUG("BCAST\n"); break; case BAT_VIS: case BAT_UNICAST_FRAG: case BAT_TT_QUERY: case BAT_ROAM_ADV: break; } } return 0; } static int parse_ip_header(unsigned char* buf, size_t len, struct packet_info* p) { struct ip* ih; DEBUG("* parse IP\n"); if (len < sizeof(struct ip)) return -1; ih = (struct ip*)buf; DEBUG("*** IP SRC: %s\n", ip_sprintf(ih->ip_src.s_addr)); DEBUG("*** IP DST: %s\n", ip_sprintf(ih->ip_dst.s_addr)); p->ip_src = ih->ip_src.s_addr; p->ip_dst = ih->ip_dst.s_addr; p->pkt_types |= PKT_TYPE_IP; DEBUG("IP proto: %d\n", ih->ip_p); switch (ih->ip_p) { case IPPROTO_UDP: p->pkt_types |= PKT_TYPE_UDP; break; /* all others set the type and return. no more parsing */ case IPPROTO_ICMP: p->pkt_types |= PKT_TYPE_ICMP; return 0; case IPPROTO_TCP: p->pkt_types |= PKT_TYPE_TCP; return 0; } return ih->ip_hl * 4; } static int parse_udp_header(unsigned char* buf, size_t len, struct packet_info* p) { struct udphdr* uh; if (len < sizeof(struct udphdr)) return -1; uh = (struct udphdr*)buf; DEBUG("UPD dest port: %d\n", ntohs(uh->uh_dport)); p->tcpudp_port = ntohs(uh->uh_dport); buf = buf + 8; len = len - 8; if (p->tcpudp_port == 698) /* OLSR */ return parse_olsr_packet(buf, len, p); if (p->tcpudp_port == BAT_PORT) /* batman */ return parse_batman_packet(buf, len, p); if (p->tcpudp_port == 9256 || p->tcpudp_port == 9257 ) /* MeshCruzer */ return parse_meshcruzer_packet(buf, len, p, p->tcpudp_port); return 0; } static int parse_olsr_packet(unsigned char* buf, size_t len, struct packet_info* p) { struct olsr* oh; int number, msgtype; if (len < sizeof(struct olsr)) return -1; oh = (struct olsr*)buf; // TODO: more than one olsr messages can be in one packet msgtype = oh->olsr_msg[0].olsr_msgtype; DEBUG("OLSR msgtype: %d\n*** ", msgtype); p->pkt_types |= PKT_TYPE_OLSR; p->olsr_type = msgtype; //if (msgtype == LQ_HELLO_MESSAGE || msgtype == LQ_TC_MESSAGE ) // p->pkt_types |= PKT_TYPE_OLSR_LQ; if (msgtype == HELLO_MESSAGE) { number = (ntohs(oh->olsr_msg[0].olsr_msgsize) - 12) / sizeof(struct hellomsg); DEBUG("HELLO %d\n", number); p->olsr_neigh = number; } if (msgtype == LQ_HELLO_MESSAGE) { number = (ntohs(oh->olsr_msg[0].olsr_msgsize) - 16) / 12; DEBUG("LQ_HELLO %d (%d)\n", number, (ntohs(oh->olsr_msg[0].olsr_msgsize) - 16)); p->olsr_neigh = number; } #if 0 /* XXX: tc messages are relayed. so we would have to find the originating node (IP) and store the information there. skip for now */ if (msgtype == TC_MESSAGE) { number = (ntohs(oh->olsr_msg[0].olsr_msgsize)-12) / sizeof(struct tcmsg); DEBUG("TC %d\n", number); p->olsr_tc = number; } if (msgtype == LQ_TC_MESSAGE) { number = (ntohs(oh->olsr_msg[0].olsr_msgsize)-16) / 8; DEBUG("LQ_TC %d (%d)\n", number, (ntohs(oh->olsr_msg[0].olsr_msgsize)-16)); p->olsr_tc = number; } if (msgtype == HNA_MESSAGE) { /* same here, but we assume that nodes which relay a HNA with a default gateway know how to contact the gw, so have a indirect connection to a GW themselves */ struct hnapair* hna; number = (ntohs(oh->olsr_msg[0].olsr_msgsize) - 12) / sizeof(struct hnapair); DEBUG("HNA NUM: %d (%d) [%d]\n", number, ntohs(oh->olsr_msg[0].olsr_msgsize), (int)sizeof(struct hnapair) ); for (i = 0; i < number; i++) { hna = &(oh->olsr_msg[0].message.hna.hna_net[i]); DEBUG("HNA %s", ip_sprintf(hna->addr)); DEBUG("/%s\n", ip_sprintf(hna->netmask)); if (hna->addr == 0 && hna->netmask == 0) p->pkt_types |= PKT_TYPE_OLSR_GW; } } #endif /* done for good */ return 0; } static int parse_batman_packet(__attribute__((unused)) unsigned char* buf, __attribute__((unused)) size_t len, __attribute__((unused)) struct packet_info* p) { p->pkt_types |= PKT_TYPE_BATMAN; return 0; } static int parse_meshcruzer_packet(__attribute__((unused)) unsigned char* buf, __attribute__((unused)) size_t len, __attribute__((unused)) struct packet_info* p, __attribute__((unused)) int port) { p->pkt_types |= PKT_TYPE_MESHZ; return 0; } horst-version-5.0/protocol_parser.h000066400000000000000000000020171273543766500176100ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PROTOCOL_PARSER_H_ #define _PROTOCOL_PARSER_H_ #include #include struct packet_info; bool parse_packet(unsigned char* buf, size_t len, struct packet_info* p); #endif horst-version-5.0/radiotap/000077500000000000000000000000001273543766500160255ustar00rootroot00000000000000horst-version-5.0/radiotap/COPYING000066400000000000000000000014641273543766500170650ustar00rootroot00000000000000Copyright (c) 2007-2009 Andy Green Copyright (c) 2007-2009 Johannes Berg Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. horst-version-5.0/radiotap/platform.h000066400000000000000000000007141273543766500200240ustar00rootroot00000000000000#include #include #include "../util.h" #define le16_to_cpu le16toh #define le32_to_cpu le32toh #define get_unaligned(p) \ ({ \ struct packed_dummy_struct { \ typeof(*(p)) __val; \ } __attribute__((packed)) *__ptr = (void *) (p); \ \ __ptr->__val; \ }) #define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p))) #define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p))) horst-version-5.0/radiotap/radiotap.c000066400000000000000000000302351273543766500177770ustar00rootroot00000000000000/* * Radiotap parser * * Copyright 2007 Andy Green * Copyright 2009 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See COPYING for more details. */ #include "radiotap_iter.h" #include "platform.h" /* function prototypes and related defs are in radiotap_iter.h */ static const struct radiotap_align_size rtap_namespace_sizes[] = { [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, /* * add more here as they are defined in radiotap.h */ }; static const struct ieee80211_radiotap_namespace radiotap_ns = { .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]), .align_size = rtap_namespace_sizes, }; /** * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization * @iterator: radiotap_iterator to initialize * @radiotap_header: radiotap header to parse * @max_length: total length we can parse into (eg, whole packet length) * * Returns: 0 or a negative error code if there is a problem. * * This function initializes an opaque iterator struct which can then * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap * argument which is present in the header. It knows about extended * present headers and handles them. * * How to use: * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) * checking for a good 0 return code. Then loop calling * __ieee80211_radiotap_iterator_next()... it returns either 0, * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. * The iterator's @this_arg member points to the start of the argument * associated with the current argument index that is present, which can be * found in the iterator's @this_arg_index member. This arg index corresponds * to the IEEE80211_RADIOTAP_... defines. * * Radiotap header length: * You can find the CPU-endian total radiotap header length in * iterator->max_length after executing ieee80211_radiotap_iterator_init() * successfully. * * Alignment Gotcha: * You must take care when dereferencing iterator.this_arg * for multibyte types... the pointer is not aligned. Use * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. * * Example code: parse.c */ int ieee80211_radiotap_iterator_init( struct ieee80211_radiotap_iterator *iterator, struct ieee80211_radiotap_header *radiotap_header, int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) { /* must at least have the radiotap header */ if (max_length < (int)sizeof(struct ieee80211_radiotap_header)) return -EINVAL; /* Linux only supports version 0 radiotap format */ if (radiotap_header->it_version) return -EINVAL; /* sanity check for allowed length and radiotap length field */ if (max_length < get_unaligned_le16(&radiotap_header->it_len)) return -EINVAL; iterator->_rtheader = radiotap_header; iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); iterator->_arg_index = 0; iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); iterator->_reset_on_ext = 0; iterator->_next_bitmap = &radiotap_header->it_present; iterator->_next_bitmap++; iterator->_vns = vns; iterator->current_namespace = &radiotap_ns; iterator->is_radiotap_ns = 1; #ifdef RADIOTAP_SUPPORT_OVERRIDES iterator->n_overrides = 0; iterator->overrides = NULL; #endif /* find payload start allowing for extended bitmap(s) */ if (iterator->_bitmap_shifter & (1<_arg - (unsigned long)iterator->_rtheader + sizeof(uint32_t) > (unsigned long)iterator->_max_length) return -EINVAL; while (get_unaligned_le32(iterator->_arg) & (1 << IEEE80211_RADIOTAP_EXT)) { iterator->_arg += sizeof(uint32_t); /* * check for insanity where the present bitmaps * keep claiming to extend up to or even beyond the * stated radiotap header length */ if ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader + sizeof(uint32_t) > (unsigned long)iterator->_max_length) return -EINVAL; } iterator->_arg += sizeof(uint32_t); /* * no need to check again for blowing past stated radiotap * header length, because ieee80211_radiotap_iterator_next * checks it before it is dereferenced */ } iterator->this_arg = iterator->_arg; /* we are all initialized happily */ return 0; } static void find_ns(struct ieee80211_radiotap_iterator *iterator, uint32_t oui, uint8_t subns) { int i; iterator->current_namespace = NULL; if (!iterator->_vns) return; for (i = 0; i < iterator->_vns->n_ns; i++) { if (iterator->_vns->ns[i].oui != oui) continue; if (iterator->_vns->ns[i].subns != subns) continue; iterator->current_namespace = &iterator->_vns->ns[i]; break; } } #ifdef RADIOTAP_SUPPORT_OVERRIDES static int find_override(struct ieee80211_radiotap_iterator *iterator, int *align, int *size) { int i; if (!iterator->overrides) return 0; for (i = 0; i < iterator->n_overrides; i++) { if (iterator->_arg_index == iterator->overrides[i].field) { *align = iterator->overrides[i].align; *size = iterator->overrides[i].size; if (!*align) /* erroneous override */ return 0; return 1; } } return 0; } #endif /** * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg * @iterator: radiotap_iterator to move to next arg (if any) * * Returns: 0 if there is an argument to handle, * -ENOENT if there are no more args or -EINVAL * if there is something else wrong. * * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) * in @this_arg_index and sets @this_arg to point to the * payload for the field. It takes care of alignment handling and extended * present fields. @this_arg can be changed by the caller (eg, * incremented to move inside a compound argument like * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in * little-endian format whatever the endianess of your CPU. * * Alignment Gotcha: * You must take care when dereferencing iterator.this_arg * for multibyte types... the pointer is not aligned. Use * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. */ int ieee80211_radiotap_iterator_next( struct ieee80211_radiotap_iterator *iterator) { while (1) { int hit = 0; int pad, align, size, subns; uint32_t oui; /* if no more EXT bits, that's it */ if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && !(iterator->_bitmap_shifter & 1)) return -ENOENT; if (!(iterator->_bitmap_shifter & 1)) goto next_entry; /* arg not present */ /* get alignment/size of data */ switch (iterator->_arg_index % 32) { case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: case IEEE80211_RADIOTAP_EXT: align = 1; size = 0; break; case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: align = 2; size = 6; break; default: #ifdef RADIOTAP_SUPPORT_OVERRIDES if (find_override(iterator, &align, &size)) { /* all set */ } else #endif if (!iterator->current_namespace || iterator->_arg_index >= iterator->current_namespace->n_bits) { if (iterator->current_namespace == &radiotap_ns) return -ENOENT; align = 0; } else { align = iterator->current_namespace->align_size[iterator->_arg_index].align; size = iterator->current_namespace->align_size[iterator->_arg_index].size; } if (!align) { /* skip all subsequent data */ iterator->_arg = iterator->_next_ns_data; /* give up on this namespace */ iterator->current_namespace = NULL; goto next_entry; } break; } /* * arg is present, account for alignment padding * * Note that these alignments are relative to the start * of the radiotap header. There is no guarantee * that the radiotap header itself is aligned on any * kind of boundary. * * The above is why get_unaligned() is used to dereference * multibyte elements from the radiotap area. */ pad = ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader) & (align - 1); if (pad) iterator->_arg += align - pad; if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { int vnslen; if ((unsigned long)iterator->_arg + size - (unsigned long)iterator->_rtheader > (unsigned long)iterator->_max_length) return -EINVAL; oui = (*iterator->_arg << 16) | (*(iterator->_arg + 1) << 8) | *(iterator->_arg + 2); subns = *(iterator->_arg + 3); find_ns(iterator, oui, subns); vnslen = get_unaligned_le16(iterator->_arg + 4); iterator->_next_ns_data = iterator->_arg + size + vnslen; if (!iterator->current_namespace) size += vnslen; } /* * this is what we will return to user, but we need to * move on first so next call has something fresh to test */ iterator->this_arg_index = iterator->_arg_index; iterator->this_arg = iterator->_arg; iterator->this_arg_size = size; /* internally move on the size of this arg */ iterator->_arg += size; /* * check for insanity where we are given a bitmap that * claims to have more arg content than the length of the * radiotap section. We will normally end up equalling this * max_length on the last arg, never exceeding it. */ if ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader > (unsigned long)iterator->_max_length) return -EINVAL; /* these special ones are valid in each bitmap word */ switch (iterator->_arg_index % 32) { case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: iterator->_reset_on_ext = 1; iterator->is_radiotap_ns = 0; /* * If parser didn't register this vendor * namespace with us, allow it to show it * as 'raw. Do do that, set argument index * to vendor namespace. */ iterator->this_arg_index = IEEE80211_RADIOTAP_VENDOR_NAMESPACE; if (!iterator->current_namespace) hit = 1; goto next_entry; case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: iterator->_reset_on_ext = 1; iterator->current_namespace = &radiotap_ns; iterator->is_radiotap_ns = 1; goto next_entry; case IEEE80211_RADIOTAP_EXT: /* * bit 31 was set, there is more * -- move to next u32 bitmap */ iterator->_bitmap_shifter = get_unaligned_le32(iterator->_next_bitmap); iterator->_next_bitmap++; if (iterator->_reset_on_ext) iterator->_arg_index = 0; else iterator->_arg_index++; iterator->_reset_on_ext = 0; break; default: /* we've got a hit! */ hit = 1; next_entry: iterator->_bitmap_shifter >>= 1; iterator->_arg_index++; } /* if we found a valid arg earlier, return it now */ if (hit) return 0; } } horst-version-5.0/radiotap/radiotap.h000066400000000000000000000251531273543766500200070ustar00rootroot00000000000000/*- * Copyright (c) 2003, 2004 David Young. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of David Young may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ /* * Modifications to fit into the linux IEEE 802.11 stack, * Mike Kershaw (dragorn@kismetwireless.net) */ #ifndef IEEE80211RADIOTAP_H #define IEEE80211RADIOTAP_H #include /* Base version of the radiotap packet header data */ #define PKTHDR_RADIOTAP_VERSION 0 /* A generic radio capture format is desirable. There is one for * Linux, but it is neither rigidly defined (there were not even * units given for some fields) nor easily extensible. * * I suggest the following extensible radio capture format. It is * based on a bitmap indicating which fields are present. * * I am trying to describe precisely what the application programmer * should expect in the following, and for that reason I tell the * units and origin of each measurement (where it applies), or else I * use sufficiently weaselly language ("is a monotonically nondecreasing * function of...") that I cannot set false expectations for lawyerly * readers. */ /* The radio capture header precedes the 802.11 header. * All data in the header is little endian on all platforms. */ struct ieee80211_radiotap_header { uint8_t it_version; /* Version 0. Only increases * for drastic changes, * introduction of compatible * new fields does not count. */ uint8_t it_pad; uint16_t it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ uint32_t it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. * Additional extensions are made * by setting bit 31. */ }; /* Name Data type Units * ---- --------- ----- * * IEEE80211_RADIOTAP_TSFT __le64 microseconds * * Value in microseconds of the MAC's 64-bit 802.11 Time * Synchronization Function timer when the first bit of the * MPDU arrived at the MAC. For received frames, only. * * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap * * Tx/Rx frequency in MHz, followed by flags (see below). * * IEEE80211_RADIOTAP_FHSS uint16_t see below * * For frequency-hopping radios, the hop set (first byte) * and pattern (second byte). * * IEEE80211_RADIOTAP_RATE u8 500kb/s * * Tx/Rx data rate * * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from * one milliwatt (dBm) * * RF signal power at the antenna, decibel difference from * one milliwatt. * * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from * one milliwatt (dBm) * * RF noise power at the antenna, decibel difference from one * milliwatt. * * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) * * RF signal power at the antenna, decibel difference from an * arbitrary, fixed reference. * * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) * * RF noise power at the antenna, decibel difference from an * arbitrary, fixed reference point. * * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless * * Quality of Barker code lock. Unitless. Monotonically * nondecreasing with "better" lock strength. Called "Signal * Quality" in datasheets. (Is there a standard way to measure * this?) * * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless * * Transmit power expressed as unitless distance from max * power set at factory calibration. 0 is max power. * Monotonically nondecreasing with lower power levels. * * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) * * Transmit power expressed as decibel distance from max power * set at factory calibration. 0 is max power. Monotonically * nondecreasing with lower power levels. * * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from * one milliwatt (dBm) * * Transmit power expressed as dBm (decibels from a 1 milliwatt * reference). This is the absolute power level measured at * the antenna port. * * IEEE80211_RADIOTAP_FLAGS u8 bitmap * * Properties of transmitted and received frames. See flags * defined below. * * IEEE80211_RADIOTAP_ANTENNA u8 antenna index * * Unitless indication of the Rx/Tx antenna for this packet. * The first antenna is antenna 0. * * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap * * Properties of received frames. See flags defined below. * * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap * * Properties of transmitted frames. See flags defined below. * * IEEE80211_RADIOTAP_RTS_RETRIES u8 data * * Number of rts retries a transmitted frame used. * * IEEE80211_RADIOTAP_DATA_RETRIES u8 data * * Number of unicast retries a transmitted frame used. * * IEEE80211_RADIOTAP_MCS u8, u8, u8 unitless * * Contains a bitmap of known fields/flags, the flags, and * the MCS index. * * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitlesss * * Contains the AMPDU information for the subframe. */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, IEEE80211_RADIOTAP_FLAGS = 1, IEEE80211_RADIOTAP_RATE = 2, IEEE80211_RADIOTAP_CHANNEL = 3, IEEE80211_RADIOTAP_FHSS = 4, IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, IEEE80211_RADIOTAP_LOCK_QUALITY = 7, IEEE80211_RADIOTAP_TX_ATTENUATION = 8, IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, IEEE80211_RADIOTAP_DBM_TX_POWER = 10, IEEE80211_RADIOTAP_ANTENNA = 11, IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, IEEE80211_RADIOTAP_DB_ANTNOISE = 13, IEEE80211_RADIOTAP_RX_FLAGS = 14, IEEE80211_RADIOTAP_TX_FLAGS = 15, IEEE80211_RADIOTAP_RTS_RETRIES = 16, IEEE80211_RADIOTAP_DATA_RETRIES = 17, IEEE80211_RADIOTAP_MCS = 19, IEEE80211_RADIOTAP_AMPDU_STATUS = 20, /* valid in every it_present bitmap, even vendor namespaces */ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, IEEE80211_RADIOTAP_EXT = 31 }; /* Channel flags. */ #define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ #define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ #define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ #define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ #define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ /* For IEEE80211_RADIOTAP_FLAGS */ #define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received * during CFP */ #define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received * with short * preamble */ #define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received * with WEP encryption */ #define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received * with fragmentation */ #define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ #define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between * 802.11 header and payload * (to 32-bit boundary) */ #define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* frame failed FCS check */ /* For IEEE80211_RADIOTAP_RX_FLAGS */ #define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* bad PLCP */ /* For IEEE80211_RADIOTAP_TX_FLAGS */ #define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ /* For IEEE80211_RADIOTAP_AMPDU_STATUS */ #define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001 #define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002 #define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004 #define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008 #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 /* For IEEE80211_RADIOTAP_MCS */ #define IEEE80211_RADIOTAP_MCS_HAVE_BW 0x01 #define IEEE80211_RADIOTAP_MCS_HAVE_MCS 0x02 #define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04 #define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08 #define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10 #define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20 #define IEEE80211_RADIOTAP_MCS_HAVE_NESS 0x40 #define IEEE80211_RADIOTAP_MCS_NESS_BIT1 0x80 #define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03 #define IEEE80211_RADIOTAP_MCS_BW_20 0 #define IEEE80211_RADIOTAP_MCS_BW_40 1 #define IEEE80211_RADIOTAP_MCS_BW_20L 2 #define IEEE80211_RADIOTAP_MCS_BW_20U 3 #define IEEE80211_RADIOTAP_MCS_SGI 0x04 #define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 #define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 #define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60 #define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5 #define IEEE80211_RADIOTAP_MCS_STBC_1 1 #define IEEE80211_RADIOTAP_MCS_STBC_2 2 #define IEEE80211_RADIOTAP_MCS_STBC_3 3 #define IEEE80211_RADIOTAP_MCS_NESS_BIT0 0x80 #endif /* IEEE80211_RADIOTAP_H */ horst-version-5.0/radiotap/radiotap_iter.h000066400000000000000000000055641273543766500210360ustar00rootroot00000000000000#ifndef __RADIOTAP_ITER_H #define __RADIOTAP_ITER_H #include #include "radiotap.h" /* Radiotap header iteration * implemented in radiotap.c */ struct radiotap_override { uint8_t field; uint8_t align:4, size:4; }; struct radiotap_align_size { uint8_t align:4, size:4; }; struct ieee80211_radiotap_namespace { const struct radiotap_align_size *align_size; int n_bits; uint32_t oui; uint8_t subns; }; struct ieee80211_radiotap_vendor_namespaces { const struct ieee80211_radiotap_namespace *ns; int n_ns; }; /** * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args * @this_arg_index: index of current arg, valid after each successful call * to ieee80211_radiotap_iterator_next() * @this_arg: pointer to current radiotap arg; it is valid after each * call to ieee80211_radiotap_iterator_next() but also after * ieee80211_radiotap_iterator_init() where it will point to * the beginning of the actual data portion * @this_arg_size: length of the current arg, for convenience * @current_namespace: pointer to the current namespace definition * (or internally %NULL if the current namespace is unknown) * @is_radiotap_ns: indicates whether the current namespace is the default * radiotap namespace or not * * @overrides: override standard radiotap fields * @n_overrides: number of overrides * * @_rtheader: pointer to the radiotap header we are walking through * @_max_length: length of radiotap header in cpu byte ordering * @_arg_index: next argument index * @_arg: next argument pointer * @_next_bitmap: internal pointer to next present u32 * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present * @_vns: vendor namespace definitions * @_next_ns_data: beginning of the next namespace's data * @_reset_on_ext: internal; reset the arg index to 0 when going to the * next bitmap word * * Describes the radiotap parser state. Fields prefixed with an underscore * must not be used by users of the parser, only by the parser internally. */ struct ieee80211_radiotap_iterator { struct ieee80211_radiotap_header *_rtheader; const struct ieee80211_radiotap_vendor_namespaces *_vns; const struct ieee80211_radiotap_namespace *current_namespace; unsigned char *_arg, *_next_ns_data; uint32_t *_next_bitmap; unsigned char *this_arg; #ifdef RADIOTAP_SUPPORT_OVERRIDES const struct radiotap_override *overrides; int n_overrides; #endif int this_arg_index; int this_arg_size; int is_radiotap_ns; int _max_length; int _arg_index; uint32_t _bitmap_shifter; int _reset_on_ext; }; extern int ieee80211_radiotap_iterator_init( struct ieee80211_radiotap_iterator *iterator, struct ieee80211_radiotap_header *radiotap_header, int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns); extern int ieee80211_radiotap_iterator_next( struct ieee80211_radiotap_iterator *iterator); #endif /* __RADIOTAP_ITER_H */ horst-version-5.0/util.c000066400000000000000000000060261273543766500153470ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "util.h" int normalize(float oval, int max_val, int max) { int val; val= (oval / max_val) * max; if (val > max) /* cap if still bigger */ val = max; if (val == 0 && oval > 0) val = 1; if (val < 0) val = 0; return val; } #if DO_DEBUG void dump_packet(const unsigned char* buf, int len) { int i; for (i = 0; i < len; i++) { if ((i % 2) == 0) { printf(" "); } if ((i % 16) == 0) { printf("\n"); } printf("%02x", buf[i]); } printf("\n"); } #else void dump_packet(__attribute__((unused)) const unsigned char* buf, __attribute__((unused)) int len) { } #endif const char* ether_sprintf(const unsigned char *mac) { static char etherbuf[18]; snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return etherbuf; } const char* ether_sprintf_short(const unsigned char *mac) { static char etherbuf[5]; snprintf(etherbuf, sizeof(etherbuf), "%02x%02x", mac[4], mac[5]); return etherbuf; } const char* ip_sprintf(const unsigned int ip) { static char ipbuf[18]; unsigned char* cip = (unsigned char*)&ip; snprintf(ipbuf, sizeof(ipbuf), "%d.%d.%d.%d", cip[0], cip[1], cip[2], cip[3]); return ipbuf; } const char* ip_sprintf_short(const unsigned int ip) { static char ipbuf[5]; unsigned char* cip = (unsigned char*)&ip; snprintf(ipbuf, sizeof(ipbuf), ".%d", cip[3]); return ipbuf; } void convert_string_to_mac(const char* string, unsigned char* mac) { int c; for(c = 0; c < 6 && string; c++) { int x = 0; if (string) sscanf(string, "%x", &x); mac[c] = x; string = strchr(string, ':'); if (string) string++; } } const char* kilo_mega_ize(unsigned int val) { static char buf[20]; char c = 0; int rest; if (val >= 1024) { /* kilo */ rest = (val & 1023) / 102.4; /* only one digit */ val = val >> 10; c = 'k'; } if (val >= 1024) { /* mega */ rest = (val & 1023) / 102.4; /* only one digit */ val = val >> 10; c = 'M'; } if (c) snprintf(buf, sizeof(buf), "%d.%d%c", val, rest, c); else snprintf(buf, sizeof(buf), "%d", val); return buf; } /* simple ilog2 implementation */ int ilog2(int x) { int n; for (n = 0; !(x & 1); n++) x = x >> 1; return n; } horst-version-5.0/util.h000066400000000000000000000071111273543766500153500ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _UTIL_H_ #define _UTIL_H_ #include #ifdef _ALLBSD_SOURCE #include #elif __linux__ #include #endif #if defined(__APPLE__) #include #define le64toh(x) OSSwapLittleToHostInt64(x) #define le32toh(x) OSSwapLittleToHostInt32(x) #define le16toh(x) OSSwapLittleToHostInt16(x) #define htole64(x) OSSwapHostToLittleInt64(x) #define htole32(x) OSSwapHostToLittleInt32(x) #define htole16(x) OSSwapHostToLittleInt16(x) #else #include #endif #if BYTE_ORDER == LITTLE_ENDIAN #if !defined(le64toh) #define le64toh(x) (x) #endif #if !defined(le32toh) #define le32toh(x) (x) #endif #if !defined(le16toh) #define le16toh(x) (x) #endif #if !defined(htole64) #define htole64(x) (x) #endif #if !defined(htole32) #define htole32(x) (x) #endif #if !defined(htole16) #define htole16(x) (x) #endif #else #if !defined(le64toh) #define le64toh(x) bswap_64(x) #endif #if !defined(le32toh) #define le32toh(x) bswap_32(x) #endif #if !defined(le16toh) #define le16toh(x) bswap_16(x) #endif #if !defined(htole64) #define htole64(x) bswap_64(x) #endif #if !defined(htole32) #define htole32(x) bswap_32(x) #endif #if !defined(htole16) #define htole16(x) bswap_16(x) #endif #endif #define MAC_NOT_EMPTY(_mac) (_mac[0] || _mac[1] || _mac[2] || _mac[3] || _mac[4] || _mac[5]) #define MAC_EMPTY(_mac) (!_mac[0] && !_mac[1] && !_mac[2] && !_mac[3] && !_mac[4] && !_mac[5]) #define BIT(nr) (1 << (nr)) #define TOGGLE_BIT(_x, _m) (_x) ^= (_m) /** * TOGGLE_BITSET() - toggle set of bits as a whole * @_x: an integer variable * @_s: an integer variable interpreted as a bitset * @_t: type (e.g. uint16_t) * If any of the bits are set, all bits will be unset. Otherwise, if * none of the bits are set, all bits will be set. */ #define TOGGLE_BITSET(_x, _s, _type) do { \ if ((_x) & (_s)) \ (_x) &= (_type)~(_s); \ else \ (_x) |= (_s); \ } while(0) #define max(_x, _y) ((_x) > (_y) ? (_x) : (_y)) #define min(_x, _y) ((_x) < (_y) ? (_x) : (_y)) #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) void dump_packet(const unsigned char* buf, int len); const char* ether_sprintf(const unsigned char *mac); const char* ether_sprintf_short(const unsigned char *mac); const char* ip_sprintf(const unsigned int ip); const char* ip_sprintf_short(const unsigned int ip); void convert_string_to_mac(const char* string, unsigned char* mac); int normalize(float val, int max_val, int max); const char* kilo_mega_ize(unsigned int val); int ilog2(int x); static inline int normalize_db(int val, int max) { if (val <= 30) return 0; else if (val >= 100) return max; else return normalize(val - 30, 70, max); } static inline __attribute__((const)) int is_power_of_2(unsigned long n) { return (n != 0 && ((n & (n - 1)) == 0)); } #endif horst-version-5.0/wlan80211.h000066400000000000000000000171361273543766500157400ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _WLAN_HEADER_H_ #define _WLAN_HEADER_H_ #include struct wlan_frame { uint16_t fc; uint16_t duration; uint8_t addr1[6]; uint8_t addr2[6]; uint8_t addr3[6]; uint16_t seq; union { uint16_t qos; uint8_t addr4[6]; struct { uint16_t qos; uint32_t ht; } __attribute__ ((packed)) ht; struct { uint8_t addr4[6]; uint16_t qos; uint32_t ht; } __attribute__ ((packed)) addr4_qos_ht; } u; } __attribute__ ((packed)); #define WLAN_FRAME_FC_VERSION_MASK 0x0003 #define WLAN_FRAME_FC_TYPE_MASK 0x000C #define WLAN_FRAME_FC_STYPE_MASK 0x00F0 #define WLAN_FRAME_FC_STYPE_QOS 0x0080 #define WLAN_FRAME_FC_TO_DS 0x0100 #define WLAN_FRAME_FC_FROM_DS 0x0200 #define WLAN_FRAME_FC_MORE_FRAG 0x0400 #define WLAN_FRAME_FC_RETRY 0x0800 #define WLAN_FRAME_FC_POWER_MGMT 0x1000 #define WLAN_FRAME_FC_MORE_DATA 0x2000 #define WLAN_FRAME_FC_PROTECTED 0x4000 #define WLAN_FRAME_FC_ORDER 0x8000 #define WLAN_FRAME_FC_MASK (WLAN_FRAME_FC_TYPE_MASK | WLAN_FRAME_FC_STYPE_MASK) #define WLAN_FRAME_TYPE(_fc) ((_fc & WLAN_FRAME_FC_TYPE_MASK) >> 2) #define WLAN_FRAME_STYPE(_fc) ((_fc & WLAN_FRAME_FC_STYPE_MASK) >> 4) #define WLAN_FRAME_FC(_type, _stype) ((((_type) << 2) | ((_stype) << 4)) & WLAN_FRAME_FC_MASK) #define WLAN_FRAME_TYPE_MGMT 0x0 #define WLAN_FRAME_TYPE_CTRL 0x1 #define WLAN_FRAME_TYPE_DATA 0x2 #define WLAN_NUM_TYPES 3 #define WLAN_NUM_STYPES 16 #define WLAN_FRAME_IS_MGMT(_fc) (WLAN_FRAME_TYPE(_fc) == WLAN_FRAME_TYPE_MGMT) #define WLAN_FRAME_IS_CTRL(_fc) (WLAN_FRAME_TYPE(_fc) == WLAN_FRAME_TYPE_CTRL) #define WLAN_FRAME_IS_DATA(_fc) (WLAN_FRAME_TYPE(_fc) == WLAN_FRAME_TYPE_DATA) #define WLAN_FRAME_IS_QOS(_fc) (((_fc) & WLAN_FRAME_FC_STYPE_MASK) == WLAN_FRAME_FC_STYPE_QOS) /*** management ***/ #define WLAN_FRAME_ASSOC_REQ WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x0) #define WLAN_FRAME_ASSOC_RESP WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x1) #define WLAN_FRAME_REASSOC_REQ WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x2) #define WLAN_FRAME_REASSOC_RESP WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x3) #define WLAN_FRAME_PROBE_REQ WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x4) #define WLAN_FRAME_PROBE_RESP WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x5) #define WLAN_FRAME_TIMING WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x6) /* (reserved) 0x7 */ #define WLAN_FRAME_BEACON WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x8) #define WLAN_FRAME_ATIM WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0x9) #define WLAN_FRAME_DISASSOC WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0xa) #define WLAN_FRAME_AUTH WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0xb) #define WLAN_FRAME_DEAUTH WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0xc) #define WLAN_FRAME_ACTION WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0xd) #define WLAN_FRAME_ACTION_NOACK WLAN_FRAME_FC(WLAN_FRAME_TYPE_MGMT, 0xe) /* (reserved) 0xf */ /*** control ***/ /* (reserved) 0-3 */ #define WLAN_FRAME_BEAM_REP WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0x4) #define WLAN_FRAME_VHT_NDP WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0x5) /* (reserved) 0x6 */ #define WLAN_FRAME_CTRL_WRAP WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0x7) #define WLAN_FRAME_BLKACK_REQ WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0x8) #define WLAN_FRAME_BLKACK WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0x9) #define WLAN_FRAME_PSPOLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0xa) #define WLAN_FRAME_RTS WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0xb) #define WLAN_FRAME_CTS WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0xc) #define WLAN_FRAME_ACK WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0xd) #define WLAN_FRAME_CF_END WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0xe) #define WLAN_FRAME_CF_END_ACK WLAN_FRAME_FC(WLAN_FRAME_TYPE_CTRL, 0xf) /*** data ***/ #define WLAN_FRAME_DATA WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x0) #define WLAN_FRAME_DATA_CF_ACK WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x1) #define WLAN_FRAME_DATA_CF_POLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x2) #define WLAN_FRAME_DATA_CF_ACKPOLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x3) #define WLAN_FRAME_NULL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x4) #define WLAN_FRAME_CF_ACK WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x5) #define WLAN_FRAME_CF_POLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x6) #define WLAN_FRAME_CF_ACKPOLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x7) #define WLAN_FRAME_QDATA WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x8) #define WLAN_FRAME_QDATA_CF_ACK WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0x9) #define WLAN_FRAME_QDATA_CF_POLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0xa) #define WLAN_FRAME_QDATA_CF_ACKPOLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0xb) #define WLAN_FRAME_QOS_NULL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0xc) /* (reserved) 0xd */ #define WLAN_FRAME_QOS_CF_POLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0xe) #define WLAN_FRAME_QOS_CF_ACKPOLL WLAN_FRAME_FC(WLAN_FRAME_TYPE_DATA, 0xf) #define WLAN_FRAME_QOS_TID_MASK 0x7 #define WLAN_FRAME_QOS_AMSDU_PRESENT 0x80 #define WLAN_FRAME_SEQ_MASK 0xFFF0 #define WLAN_FRAME_SEQ_FRAG_MASK 0x000F #define WLAN_FRAME_HT_VHT 0x1 #define WLAN_FRAME_VHT_BW_MASK 0x30000 #define WLAN_FRAME_VHT_BW_20MHZ 0x0 #define WLAN_FRAME_VHT_BW_40MHZ 0x10000 #define WLAN_FRAME_VHT_BW_80MHZ 0x20000 #define WLAN_FRAME_VHT_BW_160MHZ 0x30000 /*** individual frame formats ***/ /* beacon + probe response */ struct wlan_frame_beacon { uint64_t tsf; uint16_t bintval; uint16_t capab; unsigned char ie[0]; } __attribute__ ((packed)); /*** capabilities ***/ #define WLAN_CAPAB_ESS 0x0001 #define WLAN_CAPAB_IBSS 0x0002 #define WLAN_CAPAB_CF_POLL 0x0004 #define WLAN_CAPAB_CF_POLL_REQ 0x0008 #define WLAN_CAPAB_PRIVACY 0x0010 #define WLAN_CAPAB_SHORT_PRE 0x0020 #define WLAN_CAPAB_PBCC 0x0040 #define WLAN_CAPAB_CHAN_AGILIY 0x0080 #define WLAN_CAPAB_SPECT_MGMT 0x0100 #define WLAN_CAPAB_QOS 0x0200 #define WLAN_CAPAB_SHORT_SLOT 0x0400 #define WLAN_CAPAB_APSD 0x0800 #define WLAN_CAPAB_RADIO_MEAS 0x1000 #define WLAN_CAPAB_OFDM 0x2000 #define WLAN_CAPAB_DEL_BLKACK 0x4000 #define WLAN_CAPAB_IMM_BLKACK 0x8000 /*** information elements ***/ struct information_element { uint8_t id; uint8_t len; unsigned char var[0]; }; /* only the information element IDs we are interested in */ #define WLAN_IE_ID_SSID 0 #define WLAN_IE_ID_DSSS_PARAM 3 #define WLAN_IE_ID_HT_CAPAB 45 #define WLAN_IE_ID_RSN 48 #define WLAN_IE_ID_HT_OPER 61 #define WLAN_IE_ID_VHT_CAPAB 191 #define WLAN_IE_ID_VHT_OPER 192 #define WLAN_IE_ID_VHT_OMN 199 #define WLAN_IE_ID_VENDOR 221 /* HT capability info */ // present in Beacon, Assoc Req/Resp, Reassoc Req/Resp, Probe Req/Resp, Mesh Peering Open/Close #define WLAN_IE_HT_CAPAB_INFO_CHAN_WIDTH_40 0x0002 #define WLAN_IE_HT_OPER_INFO_CHAN_OFFSET 0x0003 /* VHT capability info */ #define WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH 0x0000000c #define WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH_80 0 /* 80MHz only */ #define WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH_160 1 /* 160MHz */ #define WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH_BOTH 2 /* 160MHz and 80+80 MHz */ #define WLAN_MAX_SSID_LEN 34 #endif horst-version-5.0/wlan_parser.c000066400000000000000000000373601273543766500167140ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "prism_header.h" #include "radiotap/radiotap.h" #include "radiotap/radiotap_iter.h" #include "wlan80211.h" #include "wlan_util.h" #include "main.h" #include "util.h" /* return consumed length or -1 on error */ static int parse_prism_header(unsigned char* buf, int len, struct packet_info* p) { wlan_ng_prism2_header* ph; DEBUG("PRISM2 HEADER\n"); if (len > 0 && (size_t)len < sizeof(wlan_ng_prism2_header)) return -1; ph = (wlan_ng_prism2_header*)buf; /* * different drivers report S/N and rssi values differently */ if (((int)ph->noise.data) < 0) { /* new madwifi */ p->phy_signal = ph->signal.data; } else if (((int)ph->rssi.data) < 0) { /* broadcom hack */ p->phy_signal = ph->rssi.data; } else { /* assume hostap */ p->phy_signal = ph->signal.data; } p->phy_rate = ph->rate.data * 10; /* just in case...*/ if (p->phy_rate == 0 || p->phy_rate > 1080) { /* assume min rate, guess mode from channel */ DEBUG("*** fixing wrong rate\n"); if (ph->channel.data > 14) p->phy_rate = 120; /* 6 * 2 */ else p->phy_rate = 20; /* 1 * 2 */ } p->phy_rate_idx = rate_to_index(p->phy_rate); /* guess phy mode */ if (ph->channel.data > 14) p->phy_flags |= PHY_FLAG_A; else p->phy_flags |= PHY_FLAG_G; /* always assume shortpre */ p->phy_flags |= PHY_FLAG_SHORTPRE; DEBUG("devname: %s\n", ph->devname); DEBUG("signal: %d -> %d\n", ph->signal.data, p->phy_signal); DEBUG("rate: %d\n", ph->rate.data); DEBUG("rssi: %d\n", ph->rssi.data); return sizeof(wlan_ng_prism2_header); } static void get_radiotap_info(struct ieee80211_radiotap_iterator *iter, struct packet_info* p) { uint16_t x; signed char c; unsigned char known, flags, ht20, lgi; switch (iter->this_arg_index) { /* ignoring these */ case IEEE80211_RADIOTAP_TSFT: case IEEE80211_RADIOTAP_FHSS: case IEEE80211_RADIOTAP_LOCK_QUALITY: case IEEE80211_RADIOTAP_TX_ATTENUATION: case IEEE80211_RADIOTAP_DB_TX_ATTENUATION: case IEEE80211_RADIOTAP_DBM_TX_POWER: case IEEE80211_RADIOTAP_TX_FLAGS: case IEEE80211_RADIOTAP_RX_FLAGS: case IEEE80211_RADIOTAP_RTS_RETRIES: case IEEE80211_RADIOTAP_DATA_RETRIES: case IEEE80211_RADIOTAP_AMPDU_STATUS: break; case IEEE80211_RADIOTAP_FLAGS: /* short preamble */ DEBUG("[flags %0x", *iter->this_arg); if (*iter->this_arg & IEEE80211_RADIOTAP_F_SHORTPRE) { p->phy_flags |= PHY_FLAG_SHORTPRE; DEBUG(" shortpre"); } if (*iter->this_arg & IEEE80211_RADIOTAP_F_BADFCS) { p->phy_flags |= PHY_FLAG_BADFCS; DEBUG(" badfcs"); } DEBUG("]"); break; case IEEE80211_RADIOTAP_RATE: //TODO check! //printf("\trate: %lf\n", (double)*iter->this_arg/2); DEBUG("[rate %0x]", *iter->this_arg); p->phy_rate = (*iter->this_arg)*5; /* rate is in 500kbps */ p->phy_rate_idx = rate_to_index(p->phy_rate); break; #define IEEE80211_CHAN_A \ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) #define IEEE80211_CHAN_G \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) case IEEE80211_RADIOTAP_CHANNEL: /* channel & channel type */ p->phy_freq = le16toh(*(uint16_t*)iter->this_arg); DEBUG("[freq %d", p->phy_freq); iter->this_arg = iter->this_arg + 2; x = le16toh(*(uint16_t*)iter->this_arg); if ((x & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) { p->phy_flags |= PHY_FLAG_A; DEBUG("A]"); } else if ((x & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) { p->phy_flags |= PHY_FLAG_G; DEBUG("G]"); } else if ((x & IEEE80211_CHAN_2GHZ) == IEEE80211_CHAN_2GHZ) { p->phy_flags |= PHY_FLAG_B; DEBUG("B]"); } break; case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: c = *(signed char*)iter->this_arg; DEBUG("[sig %0d]", c); /* we get the signal per rx chain with newer drivers. * save the highest value, but make sure we don't override * with invalid values */ if (c < 0 && (p->phy_signal == 0 || c > p->phy_signal)) p->phy_signal = c; break; case IEEE80211_RADIOTAP_DBM_ANTNOISE: DEBUG("[noi %0x]", *(signed char*)iter->this_arg); // usually not present //p->phy_noise = *(signed char*)iter->this_arg; break; case IEEE80211_RADIOTAP_ANTENNA: DEBUG("[ant %0x]", *iter->this_arg); break; case IEEE80211_RADIOTAP_DB_ANTSIGNAL: DEBUG("[snr %0x]", *iter->this_arg); // usually not present //p->phy_snr = *iter->this_arg; break; case IEEE80211_RADIOTAP_DB_ANTNOISE: //printf("\tantnoise: %02d\n", *iter->this_arg); break; case IEEE80211_RADIOTAP_MCS: /* Ref http://www.radiotap.org/defined-fields/MCS */ known = *iter->this_arg++; flags = *iter->this_arg++; DEBUG("[MCS known %0x flags %0x index %0x]", known, flags, *iter->this_arg); if (known & IEEE80211_RADIOTAP_MCS_HAVE_BW) ht20 = (flags & IEEE80211_RADIOTAP_MCS_BW_MASK) == IEEE80211_RADIOTAP_MCS_BW_20; else ht20 = 1; /* assume HT20 if not present */ if (known & IEEE80211_RADIOTAP_MCS_HAVE_GI) lgi = !(flags & IEEE80211_RADIOTAP_MCS_SGI); else lgi = 1; /* assume long GI if not present */ DEBUG(" %s %s", ht20 ? "HT20" : "HT40", lgi ? "LGI" : "SGI"); p->phy_rate_idx = 12 + *iter->this_arg; p->phy_rate_flags = flags; p->phy_rate = mcs_index_to_rate(*iter->this_arg, ht20, lgi); DEBUG(" RATE %d ", p->phy_rate); break; default: printlog("UNKNOWN RADIOTAP field %d", iter->this_arg_index); break; } } /* return consumed length, 0 for bad FCS, -1 on error */ static int parse_radiotap_header(unsigned char* buf, size_t len, struct packet_info* p) { struct ieee80211_radiotap_header* rh; struct ieee80211_radiotap_iterator iter; int err, rt_len; if (len < sizeof(struct ieee80211_radiotap_header)) return -1; rh = (struct ieee80211_radiotap_header*)buf; rt_len = le16toh(rh->it_len); err = ieee80211_radiotap_iterator_init(&iter, rh, rt_len, NULL); if (err) { DEBUG("malformed radiotap header (init returns %d)\n", err); return -1; } DEBUG("Radiotap: "); while (!(err = ieee80211_radiotap_iterator_next(&iter))) { if (iter.is_radiotap_ns) { get_radiotap_info(&iter, p); } } DEBUG("\nSIG %d", p->phy_signal); /* sanitize */ if (p->phy_rate == 0 || p->phy_rate > 6000) { /* assume min rate for mode */ DEBUG("*** fixing wrong rate\n"); if (p->phy_flags & PHY_FLAG_A) p->phy_rate = 120; /* 6 * 2 */ else if (p->phy_flags & PHY_FLAG_B) p->phy_rate = 20; /* 1 * 2 */ else if (p->phy_flags & PHY_FLAG_G) p->phy_rate = 120; /* 6 * 2 */ else p->phy_rate = 20; } DEBUG("\nrate: %.2f = idx %d\n", (float)p->phy_rate/10, p->phy_rate_idx); DEBUG("signal: %d\n", p->phy_signal); if (p->phy_flags & PHY_FLAG_BADFCS) { /* we can't trust frames with a bad FCS - stop parsing */ DEBUG("=== bad FCS, stop ===\n"); return 0; } else { return rt_len; } } static void wlan_parse_information_elements(unsigned char* buf, size_t bufLen, struct packet_info *p) { int len = bufLen; while (len > 2) { struct information_element* ie = (struct information_element*)buf; DEBUG("------ IE %d len %d t len %d\n", ie->id, ie->len, len); switch (ie->id) { case WLAN_IE_ID_SSID: if (ie->len < WLAN_MAX_SSID_LEN-1) { memcpy(p->wlan_essid, ie->var, ie->len); p->wlan_essid[ie->len] = '\0'; } else { memcpy(p->wlan_essid, ie->var, WLAN_MAX_SSID_LEN-1); p->wlan_essid[WLAN_MAX_SSID_LEN-1] = '\0'; } break; case WLAN_IE_ID_DSSS_PARAM: p->wlan_channel = *ie->var; break; case WLAN_IE_ID_RSN: p->wlan_rsn = 1; break; case WLAN_IE_ID_HT_CAPAB: DEBUG("HT %d %x\n", ie->len, ie->var[0]); if (ie->var[0] & WLAN_IE_HT_CAPAB_INFO_CHAN_WIDTH_40) p->wlan_chan_width = CHAN_WIDTH_40; else p->wlan_chan_width = CHAN_WIDTH_20; if (ie->len >= 26) { ht_streams_from_mcs_set(&ie->var[3], &p->wlan_rx_streams, &p->wlan_tx_streams); DEBUG("STREAMS %dx%d\n", p->wlan_tx_streams, p->wlan_rx_streams); } break; case WLAN_IE_ID_HT_OPER: DEBUG("HT OPER %d %x\n", ie->len, ie->var[0]); if (ie->len > 1) { switch (ie->var[1] & WLAN_IE_HT_OPER_INFO_CHAN_OFFSET) { case 0: p->wlan_chan_width = CHAN_WIDTH_20; break; case 1: p->wlan_ht40plus = true; break; case 3: p->wlan_ht40plus = false; break; default: DEBUG("HT OPER wrong?"); break; } } break; case WLAN_IE_ID_VHT_OPER: case WLAN_IE_ID_VHT_OMN: DEBUG("VHT OPER %d %x\n", ie->len, ie->var[0]); p->wlan_chan_width = CHAN_WIDTH_80; /* minimum, otherwise not AC */ break; case WLAN_IE_ID_VHT_CAPAB: DEBUG("VHT %d %x\n", ie->len, ie->var[0]); if (ie->len >= 12) { p->wlan_chan_width = chan_width_from_vht_capab(ie->var[0]); vht_streams_from_mcs_set(&ie->var[4], &p->wlan_rx_streams, &p->wlan_tx_streams); DEBUG("VHT STREAMS %dx%d\n", p->wlan_tx_streams, p->wlan_rx_streams); } break; case WLAN_IE_ID_VENDOR: if (ie->len >= 4 && ie->var[0] == 0x00 && ie->var[1] == 0x50 && ie->var[2] == 0xf2 && /* Microsoft OUI (00:50:F2) */ ie->var[3] == 1) { /* OUI Type 1 - WPA IE */ p->wlan_wpa=1; } break; } buf += ie->len + 2; len -= ie->len + 2; } } /* return consumed length, 0 for stop parsing, or -1 on error */ static int parse_80211_header(unsigned char* buf, size_t len, struct packet_info* p) { struct wlan_frame* wh; size_t hdrlen; uint8_t* ra = NULL; uint8_t* ta = NULL; uint8_t* bssid = NULL; uint16_t fc, cap_i; if (len < 10) /* minimum frame size (CTS/ACK) */ return -1; p->wlan_mode = WLAN_MODE_UNKNOWN; wh = (struct wlan_frame*)buf; fc = le16toh(wh->fc); p->wlan_type = (fc & WLAN_FRAME_FC_MASK); DEBUG("wlan_type %x - type %x - stype %x\n", fc, fc & WLAN_FRAME_FC_TYPE_MASK, fc & WLAN_FRAME_FC_STYPE_MASK); DEBUG("%s\n", get_packet_type_name(fc)); if (WLAN_FRAME_IS_DATA(fc)) { hdrlen = 24; if (WLAN_FRAME_IS_QOS(fc)) { hdrlen += 2; if (fc & WLAN_FRAME_FC_ORDER) hdrlen += 4; } /* AP, STA or IBSS */ if ((fc & WLAN_FRAME_FC_FROM_DS) == 0 && (fc & WLAN_FRAME_FC_TO_DS) == 0) { p->wlan_mode = WLAN_MODE_IBSS; bssid = wh->addr3; } else if ((fc & WLAN_FRAME_FC_FROM_DS) && (fc & WLAN_FRAME_FC_TO_DS)) { p->wlan_mode = WLAN_MODE_4ADDR; hdrlen += 6; if (WLAN_FRAME_IS_QOS(fc)) { uint16_t qos = le16toh(wh->u.addr4_qos_ht.qos); DEBUG("4ADDR A-MSDU %x\n", qos & WLAN_FRAME_QOS_AMSDU_PRESENT); if (qos & WLAN_FRAME_QOS_AMSDU_PRESENT) bssid = wh->addr3; // in the MSDU case BSSID is unknown } } else if (fc & WLAN_FRAME_FC_FROM_DS) { p->wlan_mode = WLAN_MODE_AP; bssid = wh->addr2; } else if (fc & WLAN_FRAME_FC_TO_DS) { p->wlan_mode = WLAN_MODE_STA; bssid = wh->addr1; } if (len < hdrlen) return -1; p->wlan_nav = le16toh(wh->duration); DEBUG("DATA NAV %d\n", p->wlan_nav); p->wlan_seqno = (le16toh(wh->seq) & WLAN_FRAME_SEQ_MASK) >> 4; DEBUG("DATA SEQ %d\n", p->wlan_seqno); DEBUG("A1 %s\n", ether_sprintf(wh->addr1)); DEBUG("A2 %s\n", ether_sprintf(wh->addr2)); DEBUG("A3 %s\n", ether_sprintf(wh->addr3)); if (p->wlan_mode == WLAN_MODE_4ADDR) { DEBUG("A4 %s\n", ether_sprintf(wh->u.addr4)); } DEBUG("ToDS %d FromDS %d\n", (fc & WLAN_FRAME_FC_FROM_DS) != 0, (fc & WLAN_FRAME_FC_TO_DS) != 0); ra = wh->addr1; ta = wh->addr2; /* WEP */ if (fc & WLAN_FRAME_FC_PROTECTED) p->wlan_wep = 1; if (fc & WLAN_FRAME_FC_RETRY) p->wlan_retry = 1; } else if (WLAN_FRAME_IS_CTRL(fc)) { if (p->wlan_type == WLAN_FRAME_CTS || p->wlan_type == WLAN_FRAME_ACK) hdrlen = 10; else hdrlen = 16; if (len < hdrlen) return -1; } else if (WLAN_FRAME_IS_MGMT(fc)) { hdrlen = 24; if (fc & WLAN_FRAME_FC_ORDER) hdrlen += 4; if (len < hdrlen) return -1; ra = wh->addr1; ta = wh->addr2; bssid = wh->addr3; p->wlan_seqno = (le16toh(wh->seq) & WLAN_FRAME_SEQ_MASK) >> 4; DEBUG("MGMT SEQ %d\n", p->wlan_seqno); if (fc & WLAN_FRAME_FC_RETRY) p->wlan_retry = 1; } else { DEBUG("!!!UNKNOWN FRAME!!!"); return -1; } p->wlan_len = len; switch (p->wlan_type) { case WLAN_FRAME_NULL: break; case WLAN_FRAME_QDATA: p->wlan_qos_class = le16toh(wh->u.qos) & WLAN_FRAME_QOS_TID_MASK; DEBUG("***QDATA %x\n", p->wlan_qos_class); break; case WLAN_FRAME_RTS: p->wlan_nav = le16toh(wh->duration); DEBUG("RTS NAV %d\n", p->wlan_nav); ra = wh->addr1; ta = wh->addr2; break; case WLAN_FRAME_CTS: p->wlan_nav = le16toh(wh->duration); DEBUG("CTS NAV %d\n", p->wlan_nav); ra = wh->addr1; break; case WLAN_FRAME_ACK: p->wlan_nav = le16toh(wh->duration); DEBUG("ACK NAV %d\n", p->wlan_nav); ra = wh->addr1; break; case WLAN_FRAME_PSPOLL: ra = wh->addr1; bssid = wh->addr1; ta = wh->addr2; break; case WLAN_FRAME_CF_END: case WLAN_FRAME_CF_END_ACK: ra = wh->addr1; ta = wh->addr2; bssid = wh->addr2; break; case WLAN_FRAME_BLKACK: case WLAN_FRAME_BLKACK_REQ: p->wlan_nav = le16toh(wh->duration); ra = wh->addr1; ta = wh->addr2; break; case WLAN_FRAME_BEACON: case WLAN_FRAME_PROBE_RESP: ; struct wlan_frame_beacon* bc = (struct wlan_frame_beacon*)(buf + hdrlen); p->wlan_tsf = le64toh(bc->tsf); p->wlan_bintval = le16toh(bc->bintval); //DEBUG("TSF %u\n BINTVAL %u", p->wlan_tsf, p->wlan_bintval); wlan_parse_information_elements(bc->ie, len - hdrlen - sizeof(struct wlan_frame_beacon) - 4 /* FCS */, p); DEBUG("ESSID %s \n", p->wlan_essid ); DEBUG("CHAN %d \n", p->wlan_channel ); cap_i = le16toh(bc->capab); if (cap_i & WLAN_CAPAB_IBSS) p->wlan_mode = WLAN_MODE_IBSS; else if (cap_i & WLAN_CAPAB_ESS) p->wlan_mode = WLAN_MODE_AP; if (cap_i & WLAN_CAPAB_PRIVACY) p->wlan_wep = 1; break; case WLAN_FRAME_PROBE_REQ: wlan_parse_information_elements(buf + hdrlen, len - hdrlen - 4 /* FCS */, p); p->wlan_mode = WLAN_MODE_PROBE; break; case WLAN_FRAME_ASSOC_REQ: case WLAN_FRAME_ASSOC_RESP: case WLAN_FRAME_REASSOC_REQ: case WLAN_FRAME_REASSOC_RESP: case WLAN_FRAME_DISASSOC: break; case WLAN_FRAME_AUTH: if (fc & WLAN_FRAME_FC_PROTECTED) p->wlan_wep = 1; /* no break */ case WLAN_FRAME_DEAUTH: break; case WLAN_FRAME_ACTION: break; } if (ta != NULL) { memcpy(p->wlan_src, ta, MAC_LEN); DEBUG("TA %s\n", ether_sprintf(ta)); } if (ra != NULL) { memcpy(p->wlan_dst, ra, MAC_LEN); DEBUG("RA %s\n", ether_sprintf(ra)); } if (bssid != NULL) { memcpy(p->wlan_bssid, bssid, MAC_LEN); DEBUG("BSSID %s\n", ether_sprintf(bssid)); } /* only data frames contain more info, otherwise stop parsing */ if (WLAN_FRAME_IS_DATA(p->wlan_type) && p->wlan_wep != 1) { return hdrlen; } return 0; } /* return rest of packet length (may be 0) or negative value on error */ int wlan_parse_packet(unsigned char* buf, size_t len, struct packet_info* p) { int ret; if (conf.arphrd == ARPHRD_IEEE80211_PRISM) { ret = parse_prism_header(buf, len, p); if (ret <= 0) return -1; } else if (conf.arphrd == ARPHRD_IEEE80211_RADIOTAP) { ret = parse_radiotap_header(buf, len, p); if (ret <= 0) /* 0: Bad FCS, allow packet but stop parsing */ return 0; } else { return -1; } if ((size_t)ret >= len) { DEBUG("impossible radiotap len"); return -1; } DEBUG("before parse 80211 len: %zd\n", len - ret); return parse_80211_header(buf + ret, len - ret, p); } horst-version-5.0/wlan_util.c000066400000000000000000000253571273543766500164000ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "main.h" #include "util.h" #include "wlan80211.h" #include "wlan_util.h" /* lists of packet names */ struct pkt_name stype_names[WLAN_NUM_TYPES][WLAN_NUM_STYPES] = { { { 'a', "ASOCRQ", WLAN_FRAME_ASSOC_REQ, "Association request" }, { 'A', "ASOCRP", WLAN_FRAME_ASSOC_RESP, "Association response" }, { 'o', "REASRQ", WLAN_FRAME_REASSOC_REQ, "Reassociation request" }, { 'O', "REASRP", WLAN_FRAME_REASSOC_RESP, "Reassociation response" }, { 'p', "PROBRQ", WLAN_FRAME_PROBE_REQ, "Probe request" }, { 'P', "PROBRP", WLAN_FRAME_PROBE_RESP, "Probe response" }, { 'T', "TIMING", WLAN_FRAME_TIMING, "Timing Advertisement" }, { '-', "-RESV-", 0x0070, "RESERVED" }, { 'b', "BEACON", WLAN_FRAME_BEACON, "Beacon" }, { 't', "ATIM", WLAN_FRAME_ATIM, "ATIM" }, { 'S', "DISASC", WLAN_FRAME_DISASSOC, "Disassociation" }, { 'u', "AUTH", WLAN_FRAME_AUTH, "Authentication" }, { 'U', "DEAUTH", WLAN_FRAME_DEAUTH, "Deauthentication" }, { 'X', "ACTION", WLAN_FRAME_ACTION, "Action" }, { 'x', "ACTNOA", WLAN_FRAME_ACTION_NOACK, "Action No Ack" }, { '-', "-RESV-", 0x0070, "RESERVED" }, }, { { '-', "-RESV-", 0x0004, "RESERVED" }, { '-', "-RESV-", 0x0014, "RESERVED" }, { '-', "-RESV-", 0x0024, "RESERVED" }, { '-', "-RESV-", 0x0034, "RESERVED" }, { 'B', "BEAMRP", WLAN_FRAME_BEAM_REP, "Beamforming Report Poll" }, { 'v', "VHTNDP", WLAN_FRAME_VHT_NDP, "VHT NDP Announcement" }, { '-', "-RESV-", 0x0064, "RESERVED" }, { 'w', "CTWRAP", WLAN_FRAME_CTRL_WRAP, "Control Wrapper" }, { 'l', "BACKRQ", WLAN_FRAME_BLKACK_REQ, "Block Ack Request" }, { 'L', "BACK", WLAN_FRAME_BLKACK, "Block Ack" }, { 's', "PSPOLL", WLAN_FRAME_PSPOLL, "PS-Poll" }, { 'R', "RTS", WLAN_FRAME_RTS, "RTS" }, { 'C', "CTS", WLAN_FRAME_CTS, "CTS" }, { 'k', "ACK", WLAN_FRAME_ACK, "ACK" }, { 'e', "CFEND", WLAN_FRAME_CF_END, "CF-End" }, { 'E', "CFENDK", WLAN_FRAME_CF_END_ACK, "CF-End + CF-Ack" }, }, { { 'D', "DATA", WLAN_FRAME_DATA, "Data" }, { 'F', "DCFACK", WLAN_FRAME_DATA_CF_ACK, "Data + CF-Ack" }, { 'g', "DCFPLL", WLAN_FRAME_DATA_CF_POLL, "Data + CF-Poll" }, { 'G', "DCFKPL", WLAN_FRAME_DATA_CF_ACKPOLL, "Data + CF-Ack + CF-Poll" }, { 'n', "NULL", WLAN_FRAME_NULL, "Null (no data)" }, { 'h', "CFACK", WLAN_FRAME_CF_ACK, "CF-Ack (no data)" }, { 'H', "CFPOLL", WLAN_FRAME_CF_POLL, "CF-Poll (no data)" }, { 'j', "CFCKPL", WLAN_FRAME_CF_ACKPOLL, "CF-Ack + CF-Poll (no data)" }, { 'Q', "QDATA", WLAN_FRAME_QDATA, "QoS Data" }, { 'q', "QDCFCK", WLAN_FRAME_QDATA_CF_ACK, "QoS Data + CF-Ack" }, { 'K', "QDCFPL", WLAN_FRAME_QDATA_CF_POLL, "QoS Data + CF-Poll" }, { 'y', "QDCFKP", WLAN_FRAME_QDATA_CF_ACKPOLL, "QoS Data + CF-Ack + CF-Poll" }, { 'N', "QDNULL", WLAN_FRAME_QOS_NULL, "QoS Null (no data)" }, { '-', "-RESV-", 0x00D0, "RESERVED" }, { 'Y', "QCFPLL", WLAN_FRAME_QOS_CF_POLL, "QoS CF-Poll (no data)" }, { 'z', "QCFKPL", WLAN_FRAME_QOS_CF_ACKPOLL, "QoS CF-Ack + CF-Poll (no data)" }, } }; static struct pkt_name unknow = { '?', "UNKNOW", 0, "Unknown" }; static struct pkt_name badfcs = { '*', "BADFCS", 0, "Bad FCS" }; struct pkt_name get_packet_struct(uint16_t type) { int t = WLAN_FRAME_TYPE(type); if (type == 1) /* special case for bad FCS */ return badfcs; if (t == 3) return unknow; else return stype_names[WLAN_FRAME_TYPE(type)][WLAN_FRAME_STYPE(type)]; return unknow; } char get_packet_type_char(uint16_t type) { return get_packet_struct(type).c; } const char* get_packet_type_name(uint16_t type) { return get_packet_struct(type).name; } /* rate in 100kbps */ int rate_to_index(int rate) { switch (rate) { case 540: return 12; case 480: return 11; case 360: return 10; case 240: return 9; case 180: return 8; case 120: return 7; case 110: return 6; case 90: return 5; case 60: return 4; case 55: return 3; case 20: return 2; case 10: return 1; default: return 0; } } /* return rate in 100kbps */ int rate_index_to_rate(int idx) { switch (idx) { case 12: return 540; case 11: return 480; case 10: return 360; case 9: return 240; case 8: return 180; case 7: return 120; case 6: return 110; case 5: return 90; case 4: return 60; case 3: return 55; case 2: return 20; case 1: return 10; default: return 0; } } /* return rate in 100kbps */ int mcs_index_to_rate(int mcs, bool ht20, bool lgi) { /* MCS Index, http://en.wikipedia.org/wiki/IEEE_802.11n-2009#Data_rates */ switch (mcs) { case 0: return ht20 ? (lgi ? 65 : 72) : (lgi ? 135 : 150); case 1: return ht20 ? (lgi ? 130 : 144) : (lgi ? 270 : 300); case 2: return ht20 ? (lgi ? 195 : 217) : (lgi ? 405 : 450); case 3: return ht20 ? (lgi ? 260 : 289) : (lgi ? 540 : 600); case 4: return ht20 ? (lgi ? 390 : 433) : (lgi ? 810 : 900); case 5: return ht20 ? (lgi ? 520 : 578) : (lgi ? 1080 : 1200); case 6: return ht20 ? (lgi ? 585 : 650) : (lgi ? 1215 : 1350); case 7: return ht20 ? (lgi ? 650 : 722) : (lgi ? 1350 : 1500); case 8: return ht20 ? (lgi ? 130 : 144) : (lgi ? 270 : 300); case 9: return ht20 ? (lgi ? 260 : 289) : (lgi ? 540 : 600); case 10: return ht20 ? (lgi ? 390 : 433) : (lgi ? 810 : 900); case 11: return ht20 ? (lgi ? 520 : 578) : (lgi ? 1080 : 1200); case 12: return ht20 ? (lgi ? 780 : 867) : (lgi ? 1620 : 1800); case 13: return ht20 ? (lgi ? 1040 : 1156) : (lgi ? 2160 : 2400); case 14: return ht20 ? (lgi ? 1170 : 1300) : (lgi ? 2430 : 2700); case 15: return ht20 ? (lgi ? 1300 : 1444) : (lgi ? 2700 : 3000); case 16: return ht20 ? (lgi ? 195 : 217) : (lgi ? 405 : 450); case 17: return ht20 ? (lgi ? 39 : 433) : (lgi ? 810 : 900); case 18: return ht20 ? (lgi ? 585 : 650) : (lgi ? 1215 : 1350); case 19: return ht20 ? (lgi ? 78 : 867) : (lgi ? 1620 : 1800); case 20: return ht20 ? (lgi ? 1170 : 1300) : (lgi ? 2430 : 2700); case 21: return ht20 ? (lgi ? 1560 : 1733) : (lgi ? 3240 : 3600); case 22: return ht20 ? (lgi ? 1755 : 1950) : (lgi ? 3645 : 4050); case 23: return ht20 ? (lgi ? 1950 : 2167) : (lgi ? 4050 : 4500); case 24: return ht20 ? (lgi ? 260 : 288) : (lgi ? 540 : 600); case 25: return ht20 ? (lgi ? 520 : 576) : (lgi ? 1080 : 1200); case 26: return ht20 ? (lgi ? 780 : 868) : (lgi ? 1620 : 1800); case 27: return ht20 ? (lgi ? 1040 : 1156) : (lgi ? 2160 : 2400); case 28: return ht20 ? (lgi ? 1560 : 1732) : (lgi ? 3240 : 3600); case 29: return ht20 ? (lgi ? 2080 : 2312) : (lgi ? 4320 : 4800); case 30: return ht20 ? (lgi ? 2340 : 2600) : (lgi ? 4860 : 5400); case 31: return ht20 ? (lgi ? 2600 : 2888) : (lgi ? 5400 : 6000); } return 0; } /* return rate in 100kbps * * Formula from http://equicom.hu/uploads/file/fluke/pros/how_fast_80211ac_poster.PDF * may not be 100% exact, but good enough */ int vht_mcs_index_to_rate(enum chan_width width, int streams, int mcs, bool sgi) { int wf; float mf; switch (width) { case CHAN_WIDTH_UNSPEC: case CHAN_WIDTH_20_NOHT: return -1; /* not supported */ case CHAN_WIDTH_20: wf = 52; break; case CHAN_WIDTH_40: wf = 108; break; case CHAN_WIDTH_80: wf = 234; break; case CHAN_WIDTH_160: case CHAN_WIDTH_8080: wf = 468; break; default: return -1; /* not supported */ } switch (mcs) { case 0: mf = 0.5; break; case 1: mf = 1.0; break; case 2: mf = 1.5; break; case 3: mf = 2.0; break; case 4: mf = 3.0; break; case 5: mf = 4.0; break; case 6: mf = 4.5; break; case 7: mf = 5.0; break; case 8: mf = 6.0; break; case 9: mf = 6.67; break; default: return -1; /* not supported */ } /* special unsupported cases */ if (width == CHAN_WIDTH_20 && mcs == 9 && streams != 3) return -1; if (width == CHAN_WIDTH_80 && mcs == 6 && streams == 3) return -1; if (width == CHAN_WIDTH_160 && mcs == 9 && streams == 3) return -1; if (width < CHAN_WIDTH_80 && streams > 4) return -1; if (width == CHAN_WIDTH_80 && mcs == 9 && streams == 6) return -1; if (width == CHAN_WIDTH_80 && mcs == 6 && streams == 7) return -1; return 10.0 /* kpbs */ * streams * wf * mf / (sgi ? 3.6 : 4.0); } enum chan_width chan_width_from_vht_capab(uint32_t vht) { switch (((vht & WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH) >> 2)) { case WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH_80: return CHAN_WIDTH_80; case WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH_160: return CHAN_WIDTH_160; case WLAN_IE_VHT_CAPAB_INFO_CHAN_WIDTH_BOTH: return CHAN_WIDTH_8080; default: printf("(reserved)\n"); return CHAN_WIDTH_UNSPEC; } } /* Note: mcs must be at least 13 bytes long! In theory its 16 byte */ void ht_streams_from_mcs_set(unsigned char* mcs, unsigned char* rx, unsigned char* tx) { int i; for (i = 0; i < 4; i++) { if (!mcs[i]) break; } *rx = i; bool tx_mcs_defined = mcs[12] & 0x01; bool tx_rx_mcs_not_equal = !!(mcs[12] & 0x02); char tx_max_streams = ((mcs[12] & 0x0c) >> 2) + 1; if (tx_mcs_defined && !tx_rx_mcs_not_equal) *tx = *rx; else if (tx_mcs_defined && tx_rx_mcs_not_equal) *tx = tx_max_streams; } /* Note: mcs must be at least 6 bytes long! In theory its 8 byte */ void vht_streams_from_mcs_set(unsigned char* mcs, unsigned char* rx, unsigned char* tx) { int i; /* RX */ uint16_t tmp = mcs[0] | (mcs[1] << 8); for (i = 0; i < 8; i++) { if (((tmp >> (i*2)) & 3) == 3) break; } *rx = i; /* TX */ tmp = mcs[4] | (mcs[5] << 8); for (i = 0; i < 8; i++) { if (((tmp >> (i*2)) & 3) == 3) break; } *tx = i; } const char* get_80211std(enum chan_width width, int chan) { switch (width) { case CHAN_WIDTH_UNSPEC: case CHAN_WIDTH_20_NOHT: return chan > 14 ? "a" : "bg"; case CHAN_WIDTH_20: case CHAN_WIDTH_40: return "n"; case CHAN_WIDTH_80: case CHAN_WIDTH_160: case CHAN_WIDTH_8080: return "ac"; default: return "?"; } } /* in 100kbps or -1 when unsupported */ int get_phy_thruput(enum chan_width width, unsigned char streams_rx) { switch (width) { case CHAN_WIDTH_UNSPEC: case CHAN_WIDTH_20_NOHT: return 540; case CHAN_WIDTH_20: return mcs_index_to_rate(streams_rx * 8 - 1, true, false); case CHAN_WIDTH_40: return mcs_index_to_rate(streams_rx * 8 - 1, false, false); case CHAN_WIDTH_80: case CHAN_WIDTH_160: case CHAN_WIDTH_8080: return vht_mcs_index_to_rate(width, streams_rx, 9, true); } return 0; } horst-version-5.0/wlan_util.h000066400000000000000000000036611273543766500163770ustar00rootroot00000000000000/* horst - Highly Optimized Radio Scanning Tool * * Copyright (C) 2005-2016 Bruno Randolf (br1@einfach.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _WLAN_UTIL_H_ #define _WLAN_UTIL_H_ #include #include #include "wlan80211.h" enum chan_width; struct pkt_name { const char c; const char* name; const uint16_t fc; const char* desc; }; /* * Names and abbreviations for all WLAN frame types (2 bit, but only MGMT, CTRL * and DATA defined) and subtypes (4 bit) */ extern struct pkt_name stype_names[WLAN_NUM_TYPES][WLAN_NUM_STYPES]; struct pkt_name get_packet_struct(uint16_t type); char get_packet_type_char(uint16_t type); const char* get_packet_type_name(uint16_t type); int rate_to_index(int rate); int rate_index_to_rate(int idx); int mcs_index_to_rate(int mcs, bool ht20, bool lgi); int vht_mcs_index_to_rate(enum chan_width width, int streams, int mcs, bool sgi); enum chan_width chan_width_from_vht_capab(uint32_t vht); void ht_streams_from_mcs_set(unsigned char* mcs, unsigned char* rx, unsigned char* tx); void vht_streams_from_mcs_set(unsigned char* mcs, unsigned char* rx, unsigned char* tx); const char* get_80211std(enum chan_width width, int chan); int get_phy_thruput(enum chan_width width, unsigned char streams_rx); #endif