pax_global_header00006660000000000000000000000064126355770420014525gustar00rootroot0000000000000052 comment=70f945ac889d9a867f6dfe8f7eaa6f11a6ad7bdd nethogs-0.8.1/000077500000000000000000000000001263557704200132025ustar00rootroot00000000000000nethogs-0.8.1/.cproject000066400000000000000000000760621263557704200150270ustar00rootroot00000000000000 make all true true true make all true true true nethogs-0.8.1/.cvsignore000066400000000000000000000000251263557704200151770ustar00rootroot00000000000000nethogs decpcap_test nethogs-0.8.1/.gitignore000066400000000000000000000000411263557704200151650ustar00rootroot00000000000000nethogs decpcap_test TAGS *.o *~ nethogs-0.8.1/.project000066400000000000000000000046031263557704200146540ustar00rootroot00000000000000 nethogs org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.buildArguments org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.buildLocation ${workspace_loc:/nethogs/Debug} org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget all org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd true org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.core.cnature nethogs-0.8.1/.travis.yml000066400000000000000000000001171263557704200153120ustar00rootroot00000000000000language: cpp addons: apt: packages: - libpcap0.8-dev script: make nethogs-0.8.1/COPYING000066400000000000000000000432541263557704200142450ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, 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 Lesser 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 Street, 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 Lesser General Public License instead of this License. nethogs-0.8.1/Changelog000066400000000000000000000050521263557704200150160ustar00rootroot00000000000000Changelog 12/05/13 (muzso) - added new command line switches: -s Sorts output by the sent column. -c Limits the number of updates (useful for tracemode and scripting the output). -v Sets view mode (0 = KB/s, 1 = total KB, 2 = total B, 3 = total MB) - changed needrefresh default value from true to false (upon startup there's no useful info on network usage, so displaying any results has no use for the user) 31/08/10 (Arnout) - support for screens wider than 80 characters, thanks to Shock at https://bugs.launchpad.net/ubuntu/+source/nethogs/+bug/627626 10/08/08 (Arnout) - compile with g++ 4.3, thanks to Leslie P. Polzer - skypher 08/06/08 (Arnout, thanks to MeneerJansen for sparking some activity again :) ) - add 'if (DEBUG)' to debug message - fix compiler warning by marking some constant strings 'const' - set view mode to 'kbps' by default, instead of the 'grand total' number of bytes - some fixes thanks to Bart Martens 27/08/05 (Arnout) - giving all unknown connections their own `unknown' process - Initial work on UDP support - investigated memleak, turns out to be a problem in libc: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=273051 https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=103142 18/09/04 (Arnout) - also compile with gcc-3.4 ------------------------------------------------- 17/09/04 (Arnout) -Wrapped pcap stuff in nicer 'decpcap' module -Support for IPv6 and PPP -using standard C++ maps instead of custom structures -many more code cleanups -documentation -split up ui code in seperate module ------------------------------------------------- 15/08/04 (Fabian) -Totals (in curses mode) ------------------------------------------------- 29/07/04 (Fabian) -tracemode (-t) ------------------------------------------------- 06/07/04 (Arnout) -added support for monitoring multiple interfaces at once ------------------------------------------------- 02/07/04 (Arnout) -enabled 'q' for quitting ------------------------------------------------- 29/06/04 (Fabian) <-> 0.5.1 -Adding forceExit when device is ifdown (handle was null => segfault) -Changelog activated ------------------------------------------------- 10/05/04 (Arnout) -cleanups -splitting out incoming and outgoing traffic (based on 'I/O by balance' by Fabian) ------------------------------------------------- 06/04/04 (Fabian) -getLocal -I/O by balance -forceExit ------------------------------------------------- 14/02/04 (Fabian) -Refresh delay -Help -Handling command-line options ------------------------------------------------- nethogs-0.8.1/DESIGN000066400000000000000000000022671263557704200141050ustar00rootroot00000000000000Rough design notitions: decpcap handles pcap. nethogs.cpp asks to be notified of all IPv4 or IPv6 TCP packets. the IP callbacks store the source and destination addresses into the user data. The TCP callback makes a Packet out of it, finds the Connection corresponding to that packet, and adds the packet to the connection. If no connection is found, a new one is constructed, and the process related to that connection is found though getProcess. If needed, the screen is refreshed. If, in getProcess, no corresponding process is found, the connection is added to the 'unknown' process. To prevent connections from accidentally ending up in the 'unknown' process, and then staying there indefinitely, we should maybe walk though the unknownproc's connections whenever the connection-to-inode table is refreshed. And maybe, while there are still unknown connections, the connection-to-inode table should be updated regularly. There are some global data structures: connection.cpp: connections. 'ConnList' list containting all currently known connections. A connection removes itself from the global 'connections' list in its destructor. processes. 'ProcList *' containing all processes. nethogs-0.8.1/INSTALL000066400000000000000000000002121263557704200142260ustar00rootroot00000000000000make ; make install you need the 'libpcap-dev' and 'libpcap' packages. let me know if you have any other problems on nethogs@bzzt.net nethogs-0.8.1/Makefile000066400000000000000000000045321263557704200146460ustar00rootroot00000000000000VERSION := 0 SUBVERSION := 8 MINORVERSION := 1 #prefix := /usr prefix := /usr/local sbin := $(prefix)/sbin man8 := $(prefix)/share/man/man8/ all: nethogs decpcap_test runtests: test ./test # nethogs_testsum CFLAGS?=-Wall -Wextra CXXFLAGS?=-Wall -Wextra OBJS=packet.o connection.o process.o refresh.o decpcap.o cui.o inode2prog.o conninode.o devices.o NCURSES_LIBS?=-lncurses .PHONY: tgz tgz: clean cd .. ; tar czvf nethogs-$(VERSION).$(SUBVERSION).$(MINORVERSION).tar.gz --exclude-vcs nethogs/* .PHONY: check check: echo "Not implemented" install: nethogs nethogs.8 install -d -m 755 $(DESTDIR)$(sbin) install -m 755 nethogs $(DESTDIR)$(sbin) install -d -m 755 $(DESTDIR)$(man8) install -m 644 nethogs.8 $(DESTDIR)$(man8) test: test.cpp $(CXX) $(CXXFLAGS) $(LDFLAGS) test.cpp -o test -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" nethogs: main.cpp nethogs.cpp $(OBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) main.cpp $(OBJS) -o nethogs -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" nethogs_testsum: nethogs_testsum.cpp $(OBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) nethogs_testsum.cpp $(OBJS) -o nethogs_testsum -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" decpcap_test: decpcap_test.cpp decpcap.o $(CXX) $(CXXFLAGS) $(LDFLAGS) decpcap_test.cpp decpcap.o -o decpcap_test -lpcap -lm #-lefence refresh.o: refresh.cpp refresh.h nethogs.h $(CXX) $(CXXFLAGS) -c refresh.cpp process.o: process.cpp process.h nethogs.h $(CXX) $(CXXFLAGS) -c process.cpp packet.o: packet.cpp packet.h nethogs.h $(CXX) $(CXXFLAGS) -c packet.cpp connection.o: connection.cpp connection.h nethogs.h $(CXX) $(CXXFLAGS) -c connection.cpp decpcap.o: decpcap.c decpcap.h $(CC) $(CFLAGS) -c decpcap.c inode2prog.o: inode2prog.cpp inode2prog.h nethogs.h $(CXX) $(CXXFLAGS) -c inode2prog.cpp conninode.o: conninode.cpp nethogs.h conninode.h $(CXX) $(CXXFLAGS) -c conninode.cpp #devices.o: devices.cpp devices.h # $(CXX) $(CXXFLAGS) -c devices.cpp cui.o: cui.cpp cui.h nethogs.h $(CXX) $(CXXFLAGS) -c cui.cpp -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" .PHONY: clean clean: rm -f $(OBJS) rm -f nethogs rm -f test rm -f decpcap_test nethogs-0.8.1/README.decpcap.txt000066400000000000000000000051131263557704200162760ustar00rootroot00000000000000This is a brainstorm about a libpcap-wrapper. It should make it possible to add callbacks requesting specific packets, for example asking for all TCP packets, whether they are sent over IPv4 or IPv6. Return value of the callback specifies of the packet should 'fall through', i.e., if it should be sent to other callbacks, too. give the programmer the opportunity to let packages re-enter the 'stream'. Callbacks should be called from high to low level. When a callback returns 'true', no lower callbacks should be called. The payload is available in a nice struct (union?), too. = Examples - how it'd work = == For the developers of the lib == When the sniffer is started, we learn what kind of packets are on the wire (ethernet, ppp, etc) and start pcap. Whenever a packet arrives, it is parsed. After parsing, if a callback is defined for this type of packet, the callback is pushed onto a stack. After that the payload is parsed. This goes on until the payload is, as far as we're concerned, raw data. Then the callbacks on the stack are called, until one of them returns 'true' ('done parsing this packet') Undefined callbacks move the parser to the next level. -- alternatively -- When the sniffer is started, we learn what kind of packets are on the wire (ethernet, ppp, etc) and start pcap. Whenever a packet arrives, it is parsed. After parsing, if a callback is defined for this type of packet, that callback is called. If it returns 'true', the packet is 'done', and discarded. If it returns 'false', it's passed on to the next level, leaving any changes to the user data intact. == For the users of the lib == If you want to sniff only tcp packets, add a callback for the 'packet_tcp' packet type. If you also want to count the total amount of IP traffic, make sure the 'packet_tcp' handler returns 'false' - that means after the tcp callback the packet will go on and be presented to the IP callback also. If you want to sniff specifically IPv4 TCP packets, you add a callback for IPv4 that calls the function to parse the payload directly, and then returns 'false'. If you modify the 'user' data in top-level callbacks which return 'false', -- alternatively -- If you want to sniff only tcp packets, simply only add a callback for 'dp_packet_tcp'. If, on top of that, you also want to count the total amount of IP traffic, make sure it returns 'false' and return. If you want to sniff specifically IPv4 TCP packets, you can do 2 things: add a 'true'-returning callback to everything else apart from IPv4 (which is ugly), or only add a callback for IPv4 and call the TCP-parsing code by hand. nethogs-0.8.1/README.md000066400000000000000000000031461263557704200144650ustar00rootroot00000000000000Nethogs ======= [![Build Status](https://travis-ci.org/raboof/nethogs.svg?branch=master)](https://travis-ci.org/raboof/nethogs) http://nethogs.sf.net Introduction ------------ NetHogs is a small 'net top' tool. Instead of breaking the traffic down per protocol or per subnet, like most tools do, it groups bandwidth by process. NetHogs does not rely on a special kernel module to be loaded. If there's suddenly a lot of network traffic, you can fire up NetHogs and immediately see which PID is causing this. This makes it easy to indentify programs that have gone wild and are suddenly taking up your bandwidth. Since NetHogs heavily relies on /proc, it currently runs on Linux only. Status ------ Nethogs is a mature piece of software included in most Linux distributions. Ideas for features, as well as open bugs, can be found at https://github.com/raboof/nethogs/issues Coding standards ---------------- Can anyone recommend a sensible set? :) For now: * '{' * on a new line for function definitions * on a new line for enums * on the same line for conditionals/loops * omitted when possible * use tab for indentation * use doxygen/javadoc-style comments. * for multiline doxygen docs, add a newline after '/**' * case * classes: camelcased, start uppercase * enums: camelcased, start uppercase * functions: camelcased, start lowercase * local variables: camelcased, start lowercase License ------- Copyright 2004-2005, 2008, 2010-2012, 2015 Arnout Engelen License: nethogs may be redistributed under the terms of the GPLv2 or any later version. See the COPYING file for the license text. nethogs-0.8.1/connection.cpp000066400000000000000000000125041263557704200160470ustar00rootroot00000000000000/* * connection.cpp * * Copyright (c) 2004-2006,2008 Arnout Engelen * * 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 "nethogs.h" #include "connection.h" #include "process.h" ConnList * connections = NULL; void PackList::add (Packet * p) { if (content == NULL) { content = new PackListNode (new Packet (*p)); return; } if (content->val->time.tv_sec == p->time.tv_sec) { content->val->len += p->len; return; } /* store copy of packet, so that original may be freed */ content = new PackListNode(new Packet (*p), content); } /* sums up the total bytes used and removes 'old' packets */ u_int32_t PackList::sumanddel (timeval t) { u_int32_t retval = 0; PackListNode * current = content; PackListNode * previous = NULL; while (current != NULL) { //std::cout << "Comparing " << current->val->time.tv_sec << " <= " << t.tv_sec - PERIOD << endl; if (current->val->time.tv_sec <= t.tv_sec - PERIOD) { if (current == content) content = NULL; else if (previous != NULL) previous->next = NULL; delete current; return retval; } retval += current->val->len; previous = current; current = current->next; } return retval; } /* packet may be deleted by caller */ Connection::Connection (Packet * packet) { assert (packet != NULL); connections = new ConnList (this, connections); sent_packets = new PackList (); recv_packets = new PackList (); sumSent = 0; sumRecv = 0; if (DEBUG) { std::cout << "New connection, with package len " << packet->len << std::endl; } if (packet->Outgoing()) { sumSent += packet->len; sent_packets->add(packet); refpacket = new Packet (*packet); } else { sumRecv += packet->len; recv_packets->add(packet); refpacket = packet->newInverted(); } lastpacket = packet->time.tv_sec; if (DEBUG) std::cout << "New reference packet created at " << refpacket << std::endl; } Connection::~Connection () { if (DEBUG) std::cout << "Deleting connection" << std::endl; /* refpacket is not a pointer to one of the packets in the lists * so deleted */ delete (refpacket); if (sent_packets != NULL) delete sent_packets; if (recv_packets != NULL) delete recv_packets; ConnList * curr_conn = connections; ConnList * prev_conn = NULL; while (curr_conn != NULL) { if (curr_conn->getVal() == this) { ConnList * todelete = curr_conn; curr_conn = curr_conn->getNext(); if (prev_conn == NULL) { connections = curr_conn; } else { prev_conn->setNext(curr_conn); } delete (todelete); } else { prev_conn = curr_conn; curr_conn = curr_conn->getNext(); } } } /* the packet will be freed by the calling code */ void Connection::add (Packet * packet) { lastpacket = packet->time.tv_sec; if (packet->Outgoing()) { if (DEBUG) { std::cout << "Outgoing: " << packet->len << std::endl; } sumSent += packet->len; sent_packets->add (packet); } else { if (DEBUG) { std::cout << "Incoming: " << packet->len << std::endl; } sumRecv += packet->len; if (DEBUG) { std::cout << "sumRecv now: " << sumRecv << std::endl; } recv_packets->add (packet); } } Connection * findConnectionWithMatchingSource(Packet * packet) { assert(packet->Outgoing()); ConnList * current = connections; while (current != NULL) { /* the reference packet is always outgoing */ if (packet->matchSource(current->getVal()->refpacket)) { return current->getVal(); } current = current->getNext(); } return NULL; } Connection * findConnectionWithMatchingRefpacketOrSource(Packet * packet) { ConnList * current = connections; while (current != NULL) { /* the reference packet is always *outgoing* */ if (packet->match(current->getVal()->refpacket)) { return current->getVal(); } current = current->getNext(); } return findConnectionWithMatchingSource(packet); } /* * finds connection to which this packet belongs. * a packet belongs to a connection if it matches * to its reference packet */ Connection * findConnection (Packet * packet) { if (packet->Outgoing()) return findConnectionWithMatchingRefpacketOrSource(packet); else { Packet * invertedPacket = packet->newInverted(); Connection * result = findConnectionWithMatchingRefpacketOrSource(invertedPacket); delete invertedPacket; return result; } } /* * Connection::sumanddel * * sums up the total bytes used * and removes 'old' packets. * * Returns sum of sent packages (by address) * sum of recieved packages (by address) */ void Connection::sumanddel (timeval t, u_int32_t * recv, u_int32_t * sent) { (*sent)=(*recv)=0; *sent = sent_packets->sumanddel(t); *recv = recv_packets->sumanddel(t); } nethogs-0.8.1/connection.h000066400000000000000000000050451263557704200155160ustar00rootroot00000000000000/* * connection.h * * Copyright (c) 2004-2006,2008 Arnout Engelen * * 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 __CONNECTION_H #define __CONNECTION_H #include #include "packet.h" class PackListNode { public: PackListNode (Packet * m_val, PackListNode * m_next = NULL) { val = m_val; next = m_next; } ~PackListNode () { delete val; if (next != NULL) delete next; } PackListNode * next; Packet * val; }; class PackList { public: PackList () { content = NULL; } PackList (Packet * m_val) { assert (m_val != NULL); content = new PackListNode(m_val); } ~PackList () { if (content != NULL) delete content; } /* sums up the total bytes used and removes 'old' packets */ u_int32_t sumanddel (timeval t); /* calling code may delete packet */ void add (Packet * p); private: PackListNode * content; }; class Connection { public: /* constructs a connection, makes a copy of * the packet as 'refpacket', and adds the * packet to the packlist */ /* packet may be deleted by caller */ Connection (Packet * packet); ~Connection(); /* add a packet to the packlist * will delete the packet structure * when it is 'merged with' (added to) another * packet */ void add (Packet * packet); int getLastPacket () { return lastpacket; } /* sums up the total bytes used * and removes 'old' packets. */ void sumanddel(timeval curtime, u_int32_t * recv, u_int32_t * sent); /* for checking if a packet is part of this connection */ /* the reference packet is always *outgoing*. */ Packet * refpacket; /* total sum or sent/received bytes */ u_int32_t sumSent; u_int32_t sumRecv; private: PackList * sent_packets; PackList * recv_packets; int lastpacket; }; /* Find the connection this packet belongs to */ /* (the calling code may free the packet afterwards) */ Connection * findConnection (Packet * packet); #endif nethogs-0.8.1/conninode.cpp000066400000000000000000000135231263557704200156660ustar00rootroot00000000000000/* * conninode.cpp * * Copyright (c) 2008,2009 Arnout Engelen * * 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 "nethogs.h" #include "conninode.h" extern local_addr * local_addrs; /* * connection-inode table. takes information from /proc/net/tcp. * key contains source ip, source port, destination ip, destination * port in format: '1.2.3.4:5-1.2.3.4:5' */ std::map conninode; /* * parses a /proc/net/tcp-line of the form: * sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode * 10: 020310AC:1770 9DD8A9C3:A525 01 00000000:00000000 00:00000000 00000000 0 0 2119 1 c0f4f0c0 206 40 10 3 -1 * 11: 020310AC:0404 936B2ECF:0747 01 00000000:00000000 00:00000000 00000000 1000 0 2109 1 c0f4fc00 368 40 20 2 -1 * * and of the form: * 2: 0000000000000000FFFF0000020310AC:0016 0000000000000000FFFF00009DD8A9C3:A526 01 00000000:00000000 02:000A7214 00000000 0 0 2525 2 c732eca0 201 40 1 2 -1 * */ void addtoconninode (char * buffer) { short int sa_family; struct in6_addr result_addr_local; struct in6_addr result_addr_remote; char rem_addr[128], local_addr[128]; int local_port, rem_port; struct in6_addr in6_local; struct in6_addr in6_remote; // this leaked memory //unsigned long * inode = (unsigned long *) malloc (sizeof(unsigned long)); unsigned long inode; int matches = sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X %*X:%*X %*X:%*X %*X %*d %*d %ld %*512s\n", local_addr, &local_port, rem_addr, &rem_port, &inode); if (matches != 5) { fprintf(stderr,"Unexpected buffer: '%s'\n",buffer); exit(0); } if (inode == 0) { /* connection is in TIME_WAIT state. We rely on * the old data still in the table. */ return; } if (strlen(local_addr) > 8) { /* this is an IPv6-style row */ /* Demangle what the kernel gives us */ sscanf(local_addr, "%08X%08X%08X%08X", &in6_local.s6_addr32[0], &in6_local.s6_addr32[1], &in6_local.s6_addr32[2], &in6_local.s6_addr32[3]); sscanf(rem_addr, "%08X%08X%08X%08X", &in6_remote.s6_addr32[0], &in6_remote.s6_addr32[1], &in6_remote.s6_addr32[2], &in6_remote.s6_addr32[3]); if ((in6_local.s6_addr32[0] == 0x0) && (in6_local.s6_addr32[1] == 0x0) && (in6_local.s6_addr32[2] == 0xFFFF0000)) { /* IPv4-compatible address */ result_addr_local = *((struct in6_addr*) &(in6_local.s6_addr32[3])); result_addr_remote = *((struct in6_addr*) &(in6_remote.s6_addr32[3])); sa_family = AF_INET; } else { /* real IPv6 address */ //inet_ntop(AF_INET6, &in6_local, addr6, sizeof(addr6)); //INET6_getsock(addr6, (struct sockaddr *) &localaddr); //inet_ntop(AF_INET6, &in6_remote, addr6, sizeof(addr6)); //INET6_getsock(addr6, (struct sockaddr *) &remaddr); //localaddr.sin6_family = AF_INET6; //remaddr.sin6_family = AF_INET6; result_addr_local = in6_local; result_addr_remote = in6_remote; sa_family = AF_INET6; } } else { /* this is an IPv4-style row */ sscanf(local_addr, "%X", (unsigned int *) &result_addr_local); sscanf(rem_addr, "%X", (unsigned int *) &result_addr_remote); sa_family = AF_INET; } char * hashkey = (char *) malloc (HASHKEYSIZE * sizeof(char)); char * local_string = (char*) malloc (50); char * remote_string = (char*) malloc (50); inet_ntop(sa_family, &result_addr_local, local_string, 49); inet_ntop(sa_family, &result_addr_remote, remote_string, 49); snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string, local_port, remote_string, rem_port); free (local_string); //if (DEBUG) // fprintf (stderr, "Hashkey: %s\n", hashkey); //std::cout << "Adding to conninode\n" << std::endl; conninode[hashkey] = inode; /* workaround: sometimes, when a connection is actually from 172.16.3.1 to * 172.16.3.3, packages arrive from 195.169.216.157 to 172.16.3.3, where * 172.16.3.1 and 195.169.216.157 are the local addresses of different * interfaces */ struct local_addr * current_local_addr = local_addrs; while (current_local_addr != NULL) { /* TODO maybe only add the ones with the same sa_family */ snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", current_local_addr->string, local_port, remote_string, rem_port); conninode[hashkey] = inode; current_local_addr = current_local_addr->next; } free (hashkey); free (remote_string); } /* opens /proc/net/tcp[6] and adds its contents line by line */ int addprocinfo (const char * filename) { FILE * procinfo = fopen (filename, "r"); char buffer[8192]; if (procinfo == NULL) return 0; fgets(buffer, sizeof(buffer), procinfo); do { if (fgets(buffer, sizeof(buffer), procinfo)) addtoconninode(buffer); } while (!feof(procinfo)); fclose(procinfo); return 1; } void refreshconninode () { /* we don't forget old mappings, just overwrite */ //delete conninode; //conninode = new HashTable (256); if (! addprocinfo ("/proc/net/tcp")) { std::cout << "Error: couldn't open /proc/net/tcp\n"; exit(0); } addprocinfo ("/proc/net/tcp6"); //if (DEBUG) // reviewUnknown(); } nethogs-0.8.1/conninode.h000066400000000000000000000015271263557704200153340ustar00rootroot00000000000000/* * conninode.h * * Copyright (c) 2008 Arnout Engelen * * 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. * */ // handling the connection->inode mapping void refreshconninode (); nethogs-0.8.1/cui.cpp000066400000000000000000000301151263557704200144660ustar00rootroot00000000000000/* * cui.cpp * * Copyright (c) 2004-2006,2008,2010,2011 Arnout Engelen * * 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. * */ /* NetHogs console UI */ #include #include #include #include #include #include #include #include #include "nethogs.h" #include "process.h" std::string * caption; extern const char version[]; extern ProcList * processes; extern timeval curtime; extern Process * unknowntcp; extern Process * unknownudp; extern Process * unknownip; extern bool sortRecv; extern int viewMode; extern unsigned refreshlimit; extern unsigned refreshcount; #define PID_MAX 4194303 class Line { public: Line (const char * name, double n_recv_value, double n_sent_value, pid_t pid, uid_t uid, const char * n_devicename) { assert (pid >= 0); assert (pid <= PID_MAX); m_name = name; sent_value = n_sent_value; recv_value = n_recv_value; devicename = n_devicename; m_pid = pid; m_uid = uid; assert (m_pid >= 0); } void show (int row, unsigned int proglen); void log (); double sent_value; double recv_value; private: const char * m_name; const char * devicename; pid_t m_pid; uid_t m_uid; }; #include std::string itoa(int i) { std::stringstream out; out << i; return out.str(); } /** * @returns the username that corresponds to this uid */ std::string uid2username (uid_t uid) { struct passwd * pwd = NULL; errno = 0; /* points to a static memory area, should not be freed */ pwd = getpwuid(uid); if (pwd == NULL) if (errno == 0) return itoa(uid); else forceExit(false, "Error calling getpwuid(3) for uid %d: %d %s", uid, errno, strerror(errno)); else return std::string(pwd->pw_name); } void Line::show (int row, unsigned int proglen) { assert (m_pid >= 0); assert (m_pid <= PID_MAX); if (m_pid == 0) mvprintw (row, 6, "?"); else mvprintw (row, 0, "%7d", m_pid); std::string username = uid2username(m_uid); mvprintw (row, 8, "%s", username.c_str()); if (strlen (m_name) > proglen) { // truncate oversized names char * tmp = strdup(m_name); char * start = tmp + strlen (m_name) - proglen; start[0] = '.'; start[1] = '.'; mvprintw (row, 8 + 9, "%s", start); free (tmp); } else { mvprintw (row, 8 + 9, "%s", m_name); } mvprintw (row, 8 + 9 + proglen + 2, "%s", devicename); mvprintw (row, 8 + 9 + proglen + 2 + 6, "%10.3f", sent_value); mvprintw (row, 8 + 9 + proglen + 2 + 6 + 9 + 3, "%10.3f", recv_value); if (viewMode == VIEWMODE_KBPS) { mvprintw (row, 8 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "KB/sec"); } else if (viewMode == VIEWMODE_TOTAL_MB) { mvprintw (row, 8 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "MB "); } else if (viewMode == VIEWMODE_TOTAL_KB) { mvprintw (row, 8 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "KB "); } else if (viewMode == VIEWMODE_TOTAL_B) { mvprintw (row, 8 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "B "); } } void Line::log() { std::cout << m_name << '/' << m_pid << '/' << m_uid << "\t" << sent_value << "\t" << recv_value << std::endl; } int GreatestFirst (const void * ma, const void * mb) { Line ** pa = (Line **)ma; Line ** pb = (Line **)mb; Line * a = *pa; Line * b = *pb; double aValue; if (sortRecv) { aValue = a->recv_value; } else { aValue = a->sent_value; } double bValue; if (sortRecv) { bValue = b->recv_value; } else { bValue = b->sent_value; } if (aValue > bValue) { return -1; } if (aValue == bValue) { return 0; } return 1; } void init_ui () { WINDOW * screen = initscr(); raw(); noecho(); cbreak(); nodelay(screen, TRUE); caption = new std::string ("NetHogs"); caption->append(getVersion()); //caption->append(", running at "); } void exit_ui () { clear(); endwin(); delete caption; } void ui_tick () { switch (getch()) { case 'q': /* quit */ quit_cb(0); break; case 's': /* sort on 'sent' */ sortRecv = false; break; case 'r': /* sort on 'received' */ sortRecv = true; break; case 'm': /* switch mode: total vs kb/s */ viewMode = (viewMode + 1) % VIEWMODE_COUNT; break; } } float tomb (u_int32_t bytes) { return ((double)bytes) / 1024 / 1024; } float tokb (u_int32_t bytes) { return ((double)bytes) / 1024; } float tokbps (u_int32_t bytes) { return (((double)bytes) / PERIOD) / 1024; } /** Get the kb/s values for this process */ void getkbps (Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; /* walk though all this process's connections, and sum * them up */ ConnList * curconn = curproc->connections; ConnList * previous = NULL; while (curconn != NULL) { if (curconn->getVal()->getLastPacket() <= curtime.tv_sec - CONNTIMEOUT) { /* stalled connection, remove. */ ConnList * todelete = curconn; Connection * conn_todelete = curconn->getVal(); curconn = curconn->getNext(); if (todelete == curproc->connections) curproc->connections = curconn; if (previous != NULL) previous->setNext(curconn); delete (todelete); delete (conn_todelete); } else { u_int32_t sent = 0, recv = 0; curconn->getVal()->sumanddel(curtime, &recv, &sent); sum_sent += sent; sum_recv += recv; previous = curconn; curconn = curconn->getNext(); } } *recvd = tokbps(sum_recv); *sent = tokbps(sum_sent); } /** get total values for this process */ void gettotal(Process * curproc, u_int32_t * recvd, u_int32_t * sent) { u_int32_t sum_sent = 0, sum_recv = 0; ConnList * curconn = curproc->connections; while (curconn != NULL) { Connection * conn = curconn->getVal(); sum_sent += conn->sumSent; sum_recv += conn->sumRecv; curconn = curconn->getNext(); } //std::cout << "Sum sent: " << sum_sent << std::endl; //std::cout << "Sum recv: " << sum_recv << std::endl; *recvd = sum_recv; *sent = sum_sent; } void gettotalmb(Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; gettotal(curproc, &sum_recv, &sum_sent); *recvd = tomb(sum_recv); *sent = tomb(sum_sent); } /** get total values for this process */ void gettotalkb(Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; gettotal(curproc, &sum_recv, &sum_sent); *recvd = tokb(sum_recv); *sent = tokb(sum_sent); } void gettotalb(Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; gettotal(curproc, &sum_recv, &sum_sent); //std::cout << "Total sent: " << sum_sent << std::endl; *sent = sum_sent; *recvd = sum_recv; } void show_trace(Line * lines[], int nproc) { std::cout << "\nRefreshing:\n"; /* print them */ for (int i=0; ilog(); delete lines[i]; } /* print the 'unknown' connections, for debugging */ ConnList * curr_unknownconn = unknowntcp->connections; while (curr_unknownconn != NULL) { std::cout << "Unknown connection: " << curr_unknownconn->getVal()->refpacket->gethashstring() << std::endl; curr_unknownconn = curr_unknownconn->getNext(); } } void show_ncurses(Line * lines[], int nproc) { int rows; // number of terminal rows int cols; // number of terminal columns unsigned int proglen; // max length of the "PROGRAM" column double sent_global = 0; double recv_global = 0; getmaxyx(stdscr, rows, cols); /* find the boundaries of the screeen */ if (cols < 62) { clear(); mvprintw(0,0, "The terminal is too narrow! Please make it wider.\nI'll wait..."); return; } if (cols > PROGNAME_WIDTH) cols = PROGNAME_WIDTH; proglen = cols - 55; clear(); mvprintw (0, 0, "%s", caption->c_str()); attron(A_REVERSE); mvprintw (2, 0, " PID USER %-*.*s DEV SENT RECEIVED ", proglen, proglen, "PROGRAM"); attroff(A_REVERSE); /* print them */ int i; for (i=0; ishow(i+3, proglen); recv_global += lines[i]->recv_value; sent_global += lines[i]->sent_value; delete lines[i]; } attron(A_REVERSE); int totalrow = std::min(rows-1, 3+1+i); mvprintw (totalrow, 0, " TOTAL %-*.*s %10.3f %10.3f ", proglen, proglen, " ", sent_global, recv_global); if (viewMode == VIEWMODE_KBPS) { mvprintw (3+1+i, cols - 7, "KB/sec "); } else if (viewMode == VIEWMODE_TOTAL_B) { mvprintw (3+1+i, cols - 7, "B "); } else if (viewMode == VIEWMODE_TOTAL_KB) { mvprintw (3+1+i, cols - 7, "KB "); } else if (viewMode == VIEWMODE_TOTAL_MB) { mvprintw (3+1+i, cols - 7, "MB "); } attroff(A_REVERSE); mvprintw (totalrow+1, 0, ""); refresh(); } // Display all processes and relevant network traffic using show function void do_refresh() { refreshconninode(); refreshcount++; ProcList * curproc = processes; ProcList * previousproc = NULL; int nproc = processes->size(); /* initialise to null pointers */ Line * lines [nproc]; int n = 0; #ifndef NDEBUG // initialise to null pointers for (int i = 0; i < nproc; i++) lines[i] = NULL; #endif while (curproc != NULL) { // walk though its connections, summing up their data, and // throwing away connections that haven't received a package // in the last PROCESSTIMEOUT seconds. assert (curproc != NULL); assert (curproc->getVal() != NULL); assert (nproc == processes->size()); /* remove timed-out processes (unless it's one of the the unknown process) */ if ((curproc->getVal()->getLastPacket() + PROCESSTIMEOUT <= curtime.tv_sec) && (curproc->getVal() != unknowntcp) && (curproc->getVal() != unknownudp) && (curproc->getVal() != unknownip)) { if (DEBUG) std::cout << "PROC: Deleting process\n"; ProcList * todelete = curproc; Process * p_todelete = curproc->getVal(); if (previousproc) { previousproc->next = curproc->next; curproc = curproc->next; } else { processes = curproc->getNext(); curproc = processes; } delete todelete; delete p_todelete; nproc--; //continue; } else { // add a non-timed-out process to the list of stuff to show float value_sent = 0, value_recv = 0; if (viewMode == VIEWMODE_KBPS) { //std::cout << "kbps viemode" << std::endl; getkbps (curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_KB) { //std::cout << "total viemode" << std::endl; gettotalkb(curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_MB) { //std::cout << "total viemode" << std::endl; gettotalmb(curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_B) { //std::cout << "total viemode" << std::endl; gettotalb(curproc->getVal(), &value_recv, &value_sent); } else { forceExit(false, "Invalid viewMode: %d", viewMode); } uid_t uid = curproc->getVal()->getUid(); #ifndef NDEBUG struct passwd * pwuid = getpwuid(uid); assert (pwuid != NULL); // value returned by pwuid should not be freed, according to // Petr Uzel. //free (pwuid); #endif assert (curproc->getVal()->pid >= 0); assert (n < nproc); lines[n] = new Line (curproc->getVal()->name, value_recv, value_sent, curproc->getVal()->pid, uid, curproc->getVal()->devicename); previousproc = curproc; curproc = curproc->next; n++; #ifndef NDEBUG assert (nproc == processes->size()); if (curproc == NULL) assert (n-1 < nproc); else assert (n < nproc); #endif } } /* sort the accumulated lines */ qsort (lines, nproc, sizeof(Line *), GreatestFirst); if (tracemode || DEBUG) show_trace(lines, nproc); else show_ncurses(lines, nproc); if (refreshlimit != 0 && refreshcount >= refreshlimit) quit_cb(0); } nethogs-0.8.1/cui.h000066400000000000000000000016411263557704200141350ustar00rootroot00000000000000/* * cui.h * * Copyright (c) 2004 Arnout Engelen * * 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. * */ /* NetHogs console UI */ void do_refresh (); void init_ui (); void exit_ui (); /* periodically gives some CPU-time to the UI */ void ui_tick (); nethogs-0.8.1/decpcap.c000066400000000000000000000207711263557704200147540ustar00rootroot00000000000000/* * decpcap.c * * Copyright (c) 2004-2006,2011 Arnout Engelen * * 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 // for memcpy #include #include "decpcap.h" #define DP_DEBUG 0 /* functions to set up a handle (which is basically just a pcap handle) */ struct dp_handle * dp_fillhandle(pcap_t * phandle) { struct dp_handle * retval = (struct dp_handle *) malloc (sizeof (struct dp_handle)); int i; retval->pcap_handle = phandle; for (i = 0; i < dp_n_packet_types; i++) { retval->callback[i] = NULL; } retval->linktype = pcap_datalink(retval->pcap_handle); switch (retval->linktype) { case (DLT_EN10MB): fprintf(stdout, "Ethernet link detected\n"); break; case (DLT_PPP): fprintf(stdout, "PPP link detected\n"); break; case (DLT_LINUX_SLL): fprintf(stdout, "Linux Cooked Socket link detected\n"); break; default: fprintf(stdout, "No PPP or Ethernet link: %d\n", retval->linktype); // TODO maybe error? or 'other' callback? break; } return retval; } struct dp_handle * dp_open_offline(char * fname, char * ebuf) { pcap_t * temp = pcap_open_offline(fname, ebuf); if (temp == NULL) { return NULL; } return dp_fillhandle(temp); } struct dp_handle * dp_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf) { pcap_t * temp = pcap_open_live(device, snaplen, promisc, to_ms, errbuf); if (temp == NULL) { return NULL; } return dp_fillhandle(temp); } /* functions to add callbacks */ void dp_addcb (struct dp_handle * handle, enum dp_packet_type type, dp_callback callback) { handle->callback[type] = callback; } /* functions for parsing the payloads */ void dp_parse_tcp (struct dp_handle * handle, const dp_header * header, const u_char * packet) { //const struct tcphdr * tcp = (struct tcphdr *) packet; //u_char * payload = (u_char *) packet + sizeof (struct tcphdr); if (handle->callback[dp_packet_tcp] != NULL) { int done = (handle->callback[dp_packet_tcp]) (handle->userdata, header, packet); if (done) return; } // TODO: maybe `pass on' payload to lower-level protocol parsing } void dp_parse_ip (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ip * ip = (struct ip *) packet; if (DP_DEBUG) { fprintf(stdout, "Looking at packet with length %ud\n", header->len); } u_char * payload = (u_char *) packet + sizeof (struct ip); if (handle->callback[dp_packet_ip] != NULL) { int done = (handle->callback[dp_packet_ip]) (handle->userdata, header, packet); if (done) return; } switch (ip->ip_p) { case IPPROTO_TCP: dp_parse_tcp (handle, header, payload); break; default: // TODO: maybe support for non-tcp IP packets break; } } void dp_parse_ip6 (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ip6_hdr * ip6 = (struct ip6_hdr *) packet; u_char * payload = (u_char *) packet + sizeof (struct ip6_hdr); if (handle->callback[dp_packet_ip6] != NULL) { int done = (handle->callback[dp_packet_ip6]) (handle->userdata, header, packet); if (done) return; } switch ((ip6->ip6_ctlun).ip6_un1.ip6_un1_nxt) { case IPPROTO_TCP: dp_parse_tcp (handle, header, payload); break; default: // TODO: maybe support for non-tcp ipv6 packets break; } } void dp_parse_ethernet (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ether_header * ethernet = (struct ether_header *)packet; u_char * payload = (u_char *) packet + sizeof (struct ether_header); u_int16_t protocol = 0; /* call handle if it exists */ if (handle->callback[dp_packet_ethernet] != NULL) { int done = (handle->callback[dp_packet_ethernet]) (handle->userdata, header, packet); /* return if handle decides we're done */ if (done) return; } /* parse payload */ protocol = ntohs(ethernet->ether_type); switch (protocol) { case ETHERTYPE_IP: dp_parse_ip (handle, header, payload); break; case ETHERTYPE_IPV6: dp_parse_ip6 (handle, header, payload); break; default: // TODO: maybe support for other protocols apart from IPv4 and IPv6 break; } } /* ppp header, i hope ;) */ /* glanced from ethereal, it's 16 bytes, and the payload packet type is * in the last 2 bytes... */ struct ppp_header { u_int16_t dummy1; u_int16_t dummy2; u_int16_t dummy3; u_int16_t dummy4; u_int16_t dummy5; u_int16_t dummy6; u_int16_t dummy7; u_int16_t packettype; }; void dp_parse_ppp (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ppp_header * ppp = (struct ppp_header *) packet; u_char * payload = (u_char *) packet + sizeof (struct ppp_header); u_int16_t protocol = 0; /* call handle if it exists */ if (handle->callback[dp_packet_ppp] != NULL) { int done = (handle->callback[dp_packet_ppp]) (handle->userdata, header, packet); /* return if handle decides we're done */ if (done) return; } /* parse payload */ protocol = ntohs(ppp->packettype); switch (protocol) { case ETHERTYPE_IP: dp_parse_ip (handle, header, payload); break; case ETHERTYPE_IPV6: dp_parse_ip6 (handle, header, payload); break; default: // TODO: support for other than IPv4 and IPv6 break; } } /* linux cooked header, i hope ;) */ /* glanced from libpcap/ssl.h */ #define SLL_ADDRLEN 8 /* length of address field */ struct sll_header { u_int16_t sll_pkttype; /* packet type */ u_int16_t sll_hatype; /* link-layer address type */ u_int16_t sll_halen; /* link-layer address length */ u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */ u_int16_t sll_protocol; /* protocol */ }; void dp_parse_linux_cooked (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct sll_header * sll = (struct sll_header *) packet; u_char * payload = (u_char *) packet + sizeof (struct sll_header); u_int16_t protocol = 0; /* call handle if it exists */ if (handle->callback[dp_packet_sll] != NULL) { int done = (handle->callback[dp_packet_sll]) (handle->userdata, header, packet); /* return if handle decides we're done */ if (done) return; } /* parse payload */ protocol = ntohs(sll->sll_protocol); switch (protocol) { case ETHERTYPE_IP: dp_parse_ip (handle, header, payload); break; case ETHERTYPE_IPV6: dp_parse_ip6 (handle, header, payload); break; default: // TODO: support for other than IPv4 / IPv6 break; } } /* functions to do the monitoring */ void dp_pcap_callback (u_char * u_handle, const struct pcap_pkthdr * header, const u_char * packet) { struct dp_handle * handle = (struct dp_handle *) u_handle; struct dp_header; /* make a copy of the userdata for every packet */ u_char * userdata_copy = (u_char *) malloc (handle->userdata_size); memcpy (userdata_copy, handle->userdata, handle->userdata_size); switch (handle->linktype) { case (DLT_EN10MB): dp_parse_ethernet (handle, header, packet); break; case (DLT_PPP): dp_parse_ppp (handle, header, packet); break; case (DLT_LINUX_SLL): dp_parse_linux_cooked (handle, header, packet); break; case (DLT_RAW): case (DLT_NULL): // hope for the best dp_parse_ip (handle, header, packet); break; default: fprintf(stdout, "Unknown linktype %d", handle->linktype); break; } free (userdata_copy); } int dp_dispatch (struct dp_handle * handle, int count, u_char *user, int size) { handle->userdata = user; handle->userdata_size = size; return pcap_dispatch (handle->pcap_handle, count, dp_pcap_callback, (u_char *)handle); } int dp_setnonblock (struct dp_handle * handle, int i, char * errbuf) { return pcap_setnonblock (handle->pcap_handle, i, errbuf); } char * dp_geterr (struct dp_handle * handle) { return pcap_geterr (handle->pcap_handle); } nethogs-0.8.1/decpcap.h000066400000000000000000000043511263557704200147550ustar00rootroot00000000000000/* * decpcap.h * * Copyright (c) 2004-2006,2011 Arnout Engelen * * 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 __DECPCAP_H #define __DECPCAP_H #include #include #include #define DP_ERRBUF_SIZE PCAP_ERRBUF_SIZE /* definitions */ enum dp_packet_type { dp_packet_ethernet, dp_packet_ppp, dp_packet_sll, dp_packet_ip, dp_packet_ip6, dp_packet_tcp, dp_packet_udp, dp_n_packet_types }; /*enum dp_link_type { dp_link_ethernet, dp_link_ppp, dp_n_link_types };*/ /*struct dp_header { * pcap };*/ typedef struct pcap_pkthdr dp_header; typedef int (*dp_callback)(u_char *, const dp_header *, const u_char *); struct dp_handle { pcap_t * pcap_handle; dp_callback callback [dp_n_packet_types]; int linktype; u_char * userdata; int userdata_size; }; /* functions to set up a handle (which is basically just a pcap handle) */ struct dp_handle * dp_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf); struct dp_handle * dp_open_offline(char * fname, char * ebuf); /* functions to add callbacks */ void dp_addcb (struct dp_handle * handle, enum dp_packet_type type, dp_callback callback); /* functions to parse payloads */ void dp_parse (enum dp_packet_type type, void * packet); /* functions to start monitoring */ int dp_dispatch (struct dp_handle * handler, int count, u_char *user, int size); /* functions that simply call libpcap */ int dp_datalink(struct dp_handle * handle); int dp_setnonblock (struct dp_handle * handle, int i, char * errbuf); char * dp_geterr (struct dp_handle * handle); #endif nethogs-0.8.1/decpcap_test.cpp000066400000000000000000000026571263557704200163560ustar00rootroot00000000000000/* * decpcap_test.cpp * * Copyright (c) 2006,2011 Arnout Engelen * * 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 extern "C" { #include "decpcap.h" } int process_tcp (u_char * /* userdata */, const dp_header * /* header */, const u_char * /* m_packet */) { std::cout << "Callback for processing TCP packet called" << std::endl; return 0; } int main (int argc, char ** argv) { if (argc < 2) { std::cout << "Please, enter a filename" << std::endl; } char* errbuf = new char[DP_ERRBUF_SIZE]; dp_handle * newhandle = dp_open_offline(argv[1], errbuf); dp_addcb (newhandle, dp_packet_tcp, process_tcp); int ret = dp_dispatch (newhandle, -1, NULL, 0); if (ret == -1) { std::cout << "Error dispatching: " << dp_geterr(newhandle); } } nethogs-0.8.1/devices.cpp000066400000000000000000000035401263557704200153320ustar00rootroot00000000000000/* * devices.cpp * * Copyright (c) 2011 Arnout Engelen * * 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 "devices.h" #include #include #include #include #include device * get_default_devices() { struct ifaddrs *ifaddr, *ifa; if (getifaddrs(&ifaddr) == -1) { std::cerr << "Fail to get interface addresses" << std::endl; // perror("getifaddrs"); return NULL; } device* devices = NULL; for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) continue; // The interface is up, not a loopback and running ? if ( !(ifa->ifa_flags & IFF_LOOPBACK) && (ifa->ifa_flags & IFF_UP) && (ifa->ifa_flags & IFF_RUNNING) ) { // Check if the interface is already known by going through all the devices bool found = false; device* pIter = devices; while(pIter != NULL) { if ( strcmp(ifa->ifa_name,pIter->name) == 0 ) { found = true; } pIter = pIter->next; } // We found a new interface, let's add it if ( found == false ) { devices = new device(strdup(ifa->ifa_name),devices); } } } freeifaddrs(ifaddr); return devices; } nethogs-0.8.1/devices.h000066400000000000000000000022561263557704200150020ustar00rootroot00000000000000/* * devices.h * * Copyright (c) 2011 Arnout Engelen * * 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 __DEVICES_H #define __DEVICES_H #include // NULL class device { public: device (const char * m_name, device * m_next = NULL) { name = m_name; next = m_next; } const char * name; device * next; }; /** * This function can return null, if no good interface is found * The function avoids loopback interface and down/not running interfaces */ device * get_default_devices(); #endif nethogs-0.8.1/inode2prog.cpp000066400000000000000000000130071263557704200157570ustar00rootroot00000000000000/* * inode2prog.cpp * * Copyright (c) 2005,2006,2008,2009 Arnout Engelen * * 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 #include #include #include "inode2prog.h" extern bool bughuntmode; // Not sure, but assuming there's no more PID's than go into 64 unsigned bits.. const int MAX_PID_LENGTH = 20; // Max length of filenames in /proc//fd/*. These are numeric, so 10 digits seems like a safe assumption. const int MAX_FDLINK = 10; /* maps from inode to program-struct */ std::map inodeproc; bool is_number (const char * string) { while (*string) { if (!isdigit (*string)) return false; string++; } return true; } unsigned long str2ulong (const char * ptr) { unsigned long retval = 0; while ((*ptr >= '0') && (*ptr <= '9')) { retval *= 10; retval += *ptr - '0'; ptr++; } return retval; } int str2int (const char * ptr) { int retval = 0; while ((*ptr >= '0') && (*ptr <= '9')) { retval *= 10; retval += *ptr - '0'; ptr++; } return retval; } static std::string read_file (int fd) { char buf[255]; std::string content; for (int length; (length = read(fd, buf, sizeof(buf))) > 0;) { if (length < 0) { std::fprintf(stderr, "Error reading file: %s\n", std::strerror(errno)); std::exit(34); } content.append(buf, length); } return content; } static std::string read_file (const char* filepath) { int fd = open(filepath, O_RDONLY); if (fd < 0) { std::fprintf(stderr, "Error opening %s: %s\n", filepath, std::strerror(errno)); std::exit(3); return NULL; } std::string contents = read_file(fd); if (close(fd)) { std::fprintf(stderr, "Error opening %s: %s\n", filepath, std::strerror(errno)); std::exit(34); } return contents; } std::string getprogname (pid_t pid) { const int maxfilenamelen = 14 + MAX_PID_LENGTH + 1; char filename[maxfilenamelen]; std::snprintf(filename, maxfilenamelen, "/proc/%d/cmdline", pid); return read_file(filename); } void setnode (unsigned long inode, pid_t pid) { prg_node * current_value = inodeproc[inode]; if (current_value == NULL || current_value->pid != pid) { prg_node * newnode = new prg_node; newnode->inode = inode; newnode->pid = pid; newnode->name = getprogname(pid); inodeproc[inode] = newnode; delete current_value; } } void get_info_by_linkname (const char * pid, const char * linkname) { if (strncmp(linkname, "socket:[", 8) == 0) { setnode(str2ulong(linkname + 8), str2int(pid)); } } /* updates the `inodeproc' inode-to-prg_node * for all inodes belonging to this PID * (/proc/pid/fd/42) * */ void get_info_for_pid(const char * pid) { char dirname[10 + MAX_PID_LENGTH]; size_t dirlen = 10 + strlen(pid); snprintf(dirname, dirlen, "/proc/%s/fd", pid); DIR * dir = opendir(dirname); if (!dir) { std::cout << "Couldn't open dir " << dirname << ": " << strerror(errno) << "\n"; return; } /* walk through /proc/%s/fd/... */ dirent * entry; while ((entry = readdir(dir))) { if (entry->d_type != DT_LNK) continue; //std::cout << "Looking at: " << entry->d_name << std::endl; size_t fromlen = dirlen + strlen(entry->d_name) + 1; char fromname[10 + MAX_PID_LENGTH + 1 + MAX_FDLINK]; snprintf (fromname, fromlen, "%s/%s", dirname, entry->d_name); //std::cout << "Linking from: " << fromname << std::endl; int linklen = 80; char linkname [linklen]; int usedlen = readlink(fromname, linkname, linklen-1); if (usedlen == -1) { continue; } assert (usedlen < linklen); linkname[usedlen] = '\0'; get_info_by_linkname (pid, linkname); } closedir(dir); } /* updates the `inodeproc' inode-to-prg_node mapping * for all processes in /proc */ void reread_mapping () { DIR * proc = opendir ("/proc"); if (proc == 0) { std::cerr << "Error reading /proc, neede to get inode-to-pid-maping\n"; exit(1); } dirent * entry; while ((entry = readdir(proc))) { if (entry->d_type != DT_DIR) continue; if (! is_number (entry->d_name)) continue; get_info_for_pid(entry->d_name); } closedir(proc); } struct prg_node * findPID (unsigned long inode) { /* we first look in inodeproc */ struct prg_node * node = inodeproc[inode]; if (node != NULL) { if (bughuntmode) { std::cout << ":) Found pid in inodeproc table" << std::endl; } return node; } reread_mapping(); struct prg_node * retval = inodeproc[inode]; if (bughuntmode) { if (retval == NULL) { std::cout << ":( No pid after inodeproc refresh" << std::endl; } else { std::cout << ":) Found pid after inodeproc refresh" << std::endl; } } return retval; } void prg_cache_clear() {}; /*void main () { std::cout << "Fooo\n"; reread_mapping(); std::cout << "Haihai\n"; }*/ nethogs-0.8.1/inode2prog.h000066400000000000000000000022761263557704200154320ustar00rootroot00000000000000/* * inode2prog.h * * Copyright (c) 2005,2008 Arnout Engelen * * 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 __INODE2PROG_h #define __INODE2PROG_h /* this should be called quickly after the packet * arrived, since the inode may disappear from the table * quickly, too :) */ #include "nethogs.h" struct prg_node { long inode; pid_t pid; std::string name; }; struct prg_node * findPID (unsigned long inode); void prg_cache_clear(); // reread the inode-to-prg_node-mapping void reread_mapping (); #endif nethogs-0.8.1/main.cpp000066400000000000000000000116171263557704200146400ustar00rootroot00000000000000#include "nethogs.cpp" static void versiondisplay(void) { std::cerr << version << "\n"; } static void help(void) { //std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))] [device [device [device ...]]]\n"; std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-v mode] [-c count] [-t] [-p] [-s] [device [device [device ...]]]\n"; std::cerr << " -V : prints version.\n"; std::cerr << " -b : bughunt mode - implies tracemode.\n"; std::cerr << " -d : delay for update refresh rate in seconds. default is 1.\n"; std::cerr << " -v : view mode (0 = KB/s, 1 = total KB, 2 = total B, 3 = total MB). default is 0.\n"; std::cerr << " -c : number of updates. default is 0 (unlimited).\n"; std::cerr << " -t : tracemode.\n"; //std::cerr << " -f : format of packets on interface, default is eth.\n"; std::cerr << " -p : sniff in promiscious mode (not recommended).\n"; std::cerr << " -s : sort output by sent column.\n"; std::cerr << " device : device(s) to monitor. default is all interfaces up and running excluding loopback\n"; std::cerr << std::endl; std::cerr << "When nethogs is running, press:\n"; std::cerr << " q: quit\n"; std::cerr << " s: sort by SENT traffic\n"; std::cerr << " r: sort by RECEIVE traffic\n"; std::cerr << " m: switch between total (KB, B, MB) and KB/s mode\n"; } int main (int argc, char** argv) { process_init(); device * devices = NULL; //dp_link_type linktype = dp_link_ethernet; int promisc = 0; int opt; while ((opt = getopt(argc, argv, "Vhbtpd:v:c:s")) != -1) { switch(opt) { case 'V': versiondisplay(); exit(0); case 'h': help(); exit(0); case 'b': bughuntmode = true; tracemode = true; break; case 't': tracemode = true; break; case 'p': promisc = 1; break; case 's': sortRecv = false; break; case 'd': refreshdelay = atoi(optarg); break; case 'v': viewMode = atoi(optarg) % VIEWMODE_COUNT; break; case 'c': refreshlimit = atoi(optarg); break; /* case 'f': argv++; if (strcmp (optarg, "ppp") == 0) linktype = dp_link_ppp; else if (strcmp (optarg, "eth") == 0) linktype = dp_link_ethernet; } break; */ default: help(); exit(EXIT_FAILURE); } } while (optind < argc) { devices = new device (strdup(argv[optind++]), devices); } if (devices == NULL) { devices = get_default_devices(); if ( devices == NULL ) { std::cerr << "Not devices to monitor" << std::endl; return 0; } } if ((!tracemode) && (!DEBUG)){ init_ui(); } if (NEEDROOT && (geteuid() != 0)) forceExit(false, "You need to be root to run NetHogs!"); char errbuf[PCAP_ERRBUF_SIZE]; handle * handles = NULL; device * current_dev = devices; while (current_dev != NULL) { getLocal(current_dev->name, tracemode); dp_handle * newhandle = dp_open_live(current_dev->name, BUFSIZ, promisc, 100, errbuf); if (newhandle != NULL) { dp_addcb (newhandle, dp_packet_ip, process_ip); dp_addcb (newhandle, dp_packet_ip6, process_ip6); dp_addcb (newhandle, dp_packet_tcp, process_tcp); dp_addcb (newhandle, dp_packet_udp, process_udp); /* The following code solves sf.net bug 1019381, but is only available * in newer versions (from 0.8 it seems) of libpcap * * update: version 0.7.2, which is in debian stable now, should be ok * also. */ if (dp_setnonblock (newhandle, 1, errbuf) == -1) { fprintf(stderr, "Error putting libpcap in nonblocking mode\n"); } handles = new handle (newhandle, current_dev->name, handles); } else { fprintf(stderr, "Error opening handler for device %s\n", current_dev->name); } current_dev = current_dev->next; } signal (SIGALRM, &alarm_cb); signal (SIGINT, &quit_cb); alarm (refreshdelay); fprintf(stderr, "Waiting for first packet to arrive (see sourceforge.net bug 1019381)\n"); struct dpargs * userdata = (dpargs *) malloc (sizeof (struct dpargs)); // Main loop: // // Walks though the 'handles' list, which contains handles opened in non-blocking mode. // This causes the CPU utilisation to go up to 100%. This is tricky: while (1) { bool packets_read = false; handle * current_handle = handles; while (current_handle != NULL) { userdata->device = current_handle->devicename; userdata->sa_family = AF_UNSPEC; int retval = dp_dispatch (current_handle->content, -1, (u_char *)userdata, sizeof (struct dpargs)); if (retval < 0) { std::cerr << "Error dispatching: " << retval << std::endl; } else if (retval != 0) { packets_read = true; } current_handle = current_handle->next; } if (needrefresh) { if ((!DEBUG)&&(!tracemode)) { // handle user input ui_tick(); } do_refresh(); needrefresh = false; } // If no packets were read at all this iteration, pause to prevent 100% // CPU utilisation; if (!packets_read) { usleep(100); } } } nethogs-0.8.1/nethogs.8000066400000000000000000000026151263557704200147460ustar00rootroot00000000000000.\" This page Copyright (C) 2004 Fabian Frederick .\" Content based on Nethogs homepage by Arnout Engelen .TH NETHOGS 8 "14 February 2004" .SH NAME nethogs \- Net top tool grouping bandwidth per process .SH SYNOPSIS .ft B .B nethogs .RB [ "\-h" ] .RB [ "\-V" ] .RB [ "\-d" ] .RB [ "\-v" ] .RB [ "\-t" ] .RB [ "\-c" ] .RB [ "\-p" ] .RB [ "\-s" ] .RI [device(s)] .SH DESCRIPTION NetHogs is a small 'net top' tool. Instead of breaking the traffic down per protocol or per subnet, like most such tools do, it groups bandwidth by process - and does not rely on a special kernel module to be loaded. So if there's suddenly a lot of network traffic, you can fire up NetHogs and immediately see which PID is causing this, and if it's some kind of spinning process, kill it. .SS Options .TP \fB-h\fP display available commands usage. .TP \fB-V\fP prints Version info. .TP \fB-d\fP delay for refresh rate. .TP \fB-v\fP select view mode .TP \fB-p\fP sniff in promiscious mode (not recommended). .TP \fB-t\fP tracemode. .TP \fB-c\fP limit number of refreshes .TP \fB-s\fP sort by traffic sent .PP .I device(s) to monitor. By default eth0 is being used. .SH "INTERACTIVE CONTROL" .TP m cycle between display modes (kb/s, kb, b, mb) .TP r sort by 'received' .TP s sort by 'sent' .TP q quit .RE .SH "SEE ALSO" .I netstat(8) tcpdump(1) pcap(3) .SH AUTHOR .nf Written by Arnout Engelen . nethogs-0.8.1/nethogs.cpp000066400000000000000000000135621263557704200153640ustar00rootroot00000000000000/* * nethogs.cpp * * Copyright (c) 2004-2006,2008,2011 Arnout Engelen * * 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 "nethogs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cui.h" extern "C" { #include "decpcap.h" } #include "packet.h" #include "connection.h" #include "process.h" #include "refresh.h" #include "devices.h" extern Process * unknownudp; unsigned refreshdelay = 1; unsigned refreshlimit = 0; unsigned refreshcount = 0; unsigned processlimit = 0; bool tracemode = false; bool bughuntmode = false; bool needrefresh = false; // sort on sent or received? bool sortRecv = true; // viewMode: kb/s or total int viewMode = VIEWMODE_KBPS; //packet_type packettype = packet_ethernet; //dp_link_type linktype = dp_link_ethernet; const char version[] = " version " VERSION "." SUBVERSION "." MINORVERSION; timeval curtime; bool local_addr::contains (const in_addr_t & n_addr) { if ((sa_family == AF_INET) && (n_addr == addr)) return true; if (next == NULL) return false; return next->contains(n_addr); } bool local_addr::contains(const struct in6_addr & n_addr) { if (sa_family == AF_INET6) { /* if (DEBUG) { char addy [50]; std::cerr << "Comparing: "; inet_ntop (AF_INET6, &n_addr, addy, 49); std::cerr << addy << " and "; inet_ntop (AF_INET6, &addr6, addy, 49); std::cerr << addy << std::endl; } */ //if (addr6.s6_addr == n_addr.s6_addr) if (memcmp (&addr6, &n_addr, sizeof(struct in6_addr)) == 0) { if (DEBUG) std::cerr << "Match!" << std::endl; return true; } } if (next == NULL) return false; return next->contains(n_addr); } struct dpargs { const char * device; int sa_family; in_addr ip_src; in_addr ip_dst; in6_addr ip6_src; in6_addr ip6_dst; }; const char* getVersion() { return version; } int process_tcp (u_char * userdata, const dp_header * header, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; struct tcphdr * tcp = (struct tcphdr *) m_packet; curtime = header->ts; /* get info from userdata, then call getPacket */ Packet * packet; switch (args->sa_family) { case (AF_INET): packet = new Packet (args->ip_src, ntohs(tcp->source), args->ip_dst, ntohs(tcp->dest), header->len, header->ts); break; case (AF_INET6): packet = new Packet (args->ip6_src, ntohs(tcp->source), args->ip6_dst, ntohs(tcp->dest), header->len, header->ts); break; } Connection * connection = findConnection(packet); if (connection != NULL) { /* add packet to the connection */ connection->add(packet); } else { /* else: unknown connection, create new */ connection = new Connection (packet); getProcess(connection, args->device); } delete packet; /* we're done now. */ return true; } int process_udp (u_char * userdata, const dp_header * header, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; struct udphdr * udp = (struct udphdr *) m_packet; curtime = header->ts; Packet * packet; switch (args->sa_family) { case (AF_INET): packet = new Packet (args->ip_src, ntohs(udp->source), args->ip_dst, ntohs(udp->dest), header->len, header->ts); break; case (AF_INET6): packet = new Packet (args->ip6_src, ntohs(udp->source), args->ip6_dst, ntohs(udp->dest), header->len, header->ts); break; } //if (DEBUG) // std::cout << "Got packet from " << packet->gethashstring() << std::endl; Connection * connection = findConnection(packet); if (connection != NULL) { /* add packet to the connection */ connection->add(packet); } else { /* else: unknown connection, create new */ connection = new Connection (packet); getProcess(connection, args->device); } delete packet; /* we're done now. */ return true; } int process_ip (u_char * userdata, const dp_header * /* header */, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; struct ip * ip = (struct ip *) m_packet; args->sa_family = AF_INET; args->ip_src = ip->ip_src; args->ip_dst = ip->ip_dst; /* we're not done yet - also parse tcp :) */ return false; } int process_ip6 (u_char * userdata, const dp_header * /* header */, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; const struct ip6_hdr * ip6 = (struct ip6_hdr *) m_packet; args->sa_family = AF_INET6; args->ip6_src = ip6->ip6_src; args->ip6_dst = ip6->ip6_dst; /* we're not done yet - also parse tcp :) */ return false; } void quit_cb (int /* i */) { procclean(); if ((!tracemode) && (!DEBUG)) exit_ui(); exit(0); } void forceExit(bool success, const char *msg, ...) { if ((!tracemode)&&(!DEBUG)){ exit_ui(); } va_list argp; va_start(argp, msg); vfprintf(stderr, msg, argp); va_end(argp); std::cerr << std::endl; if (success) exit(EXIT_SUCCESS); else exit(EXIT_FAILURE); } class handle { public: handle (dp_handle * m_handle, const char * m_devicename = NULL, handle * m_next = NULL) { content = m_handle; next = m_next; devicename = m_devicename; } dp_handle * content; const char * devicename; handle * next; }; nethogs-0.8.1/nethogs.h000066400000000000000000000100161263557704200150200ustar00rootroot00000000000000/* * nethogs.h * * Copyright (c) 2004-2006,2008,2010 Arnout Engelen * * 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 __NETHOGS_H #define __NETHOGS_H #include #include #include #include #include #include #include #include #define _BSD_SOURCE 1 /* take the average speed over the last 5 seconds */ #define PERIOD 5 /* the amount of time after the last packet was recieved * after which a process is removed */ #define PROCESSTIMEOUT 150 /* the amount of time after the last packet was recieved * after which a connection is removed */ #define CONNTIMEOUT 50 /* Set to '0' when compiling for a system that uses Linux Capabilities, * like www.adamantix.org: in that case nethogs shouldn't check if it's * running as root. Take care to give it sufficient privileges though. */ #ifndef NEEDROOT #define NEEDROOT 1 #endif #define DEBUG 0 #define REVERSEHACK 0 // 2 times: 32 characters, 7 ':''s, a ':12345'. // 1 '-' // -> 2*45+1=91. we make it 92, for the null. #define HASHKEYSIZE 92 #define PROGNAME_WIDTH 512 // viewMode: how to represent numbers #define VIEWMODE_KBPS 0 #define VIEWMODE_TOTAL_KB 1 #define VIEWMODE_TOTAL_B 2 #define VIEWMODE_TOTAL_MB 3 #define VIEWMODE_COUNT 4 #define NORETURN __attribute__ ((__noreturn__)) void forceExit(bool success, const char *msg, ...) NORETURN; class local_addr { public: /* ipv4 constructor takes an in_addr_t */ local_addr (in_addr_t m_addr, local_addr * m_next = NULL) { addr = m_addr; next = m_next; sa_family = AF_INET; string = (char*) malloc (16); inet_ntop (AF_INET, &m_addr, string, 15); } /* this constructor takes an char address[33] */ local_addr (char m_address [33], local_addr * m_next = NULL) { next = m_next; char address [40]; address[0] = m_address[0]; address[1] = m_address[1]; address[2] = m_address[2]; address[3] = m_address[3]; address[4] = ':'; address[5] = m_address[4]; address[6] = m_address[5]; address[7] = m_address[6]; address[8] = m_address[7]; address[9] = ':'; address[10] = m_address[8]; address[11] = m_address[9]; address[12] = m_address[10]; address[13] = m_address[11]; address[14] = ':'; address[15] = m_address[12]; address[16] = m_address[13]; address[17] = m_address[14]; address[18] = m_address[15]; address[19] = ':'; address[20] = m_address[16]; address[21] = m_address[17]; address[22] = m_address[18]; address[23] = m_address[19]; address[24] = ':'; address[25] = m_address[20]; address[26] = m_address[21]; address[27] = m_address[22]; address[28] = m_address[23]; address[29] = ':'; address[30] = m_address[24]; address[31] = m_address[25]; address[32] = m_address[26]; address[33] = m_address[27]; address[34] = ':'; address[35] = m_address[28]; address[36] = m_address[29]; address[37] = m_address[30]; address[38] = m_address[31]; address[39] = 0; string = strdup(address); //if (DEBUG) // std::cout << "Converting address " << address << std::endl; int result = inet_pton (AF_INET6, address, &addr6); assert (result > 0); sa_family = AF_INET6; } bool contains (const in_addr_t & n_addr); bool contains (const struct in6_addr & n_addr); char * string; local_addr * next; private: in_addr_t addr; struct in6_addr addr6; short int sa_family; }; void quit_cb (int i); const char* getVersion(); #endif nethogs-0.8.1/packet.cpp000066400000000000000000000201751263557704200151620ustar00rootroot00000000000000/* * packet.cpp * * Copyright (c) 2004-2006,2008 Arnout Engelen * * 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 "nethogs.h" #include #include "packet.h" #include #include #include #include #include #include #include #include #include #include // #include "inet6.c" local_addr * local_addrs = NULL; /* moves the pointer right until a non-space is seen */ char * stripspaces (char * input) { char * retval = input; while (*retval == ' ') retval++; return retval; } /* * getLocal * device: This should be device explicit (e.g. eth0:1) * * uses ioctl to get address of this device, and adds it to the * local_addrs-list. */ void getLocal (const char *device, bool tracemode) { /* get local IPv4 addresses */ int sock; struct ifreq iFreq; struct sockaddr_in *saddr; if((sock=socket(AF_INET, SOCK_DGRAM, 0))<0) forceExit(false, "creating socket failed while establishing local IP - are you root?"); strcpy(iFreq.ifr_name, device); if(ioctl(sock, SIOCGIFADDR, &iFreq)<0) forceExit(false, "ioctl failed while establishing local IP for selected device %s. You may specify the device on the command line.", device); saddr=(struct sockaddr_in*)&iFreq.ifr_addr; local_addrs = new local_addr (saddr->sin_addr.s_addr, local_addrs); if (tracemode || DEBUG) { printf ("Adding local address: %s\n", inet_ntoa(saddr->sin_addr)); } /* also get local IPv6 addresses */ FILE * ifinfo = fopen ("/proc/net/if_inet6", "r"); char buffer [500]; if (ifinfo) { do { if (fgets(buffer, sizeof(buffer), ifinfo)) { char address [33]; char ifname [9]; int n_results = sscanf (buffer, "%32[0-9a-f] %*d %*d %*d %*d %8[0-9a-zA-Z]", address, ifname); assert (n_results = 2); if (strcmp (stripspaces(ifname), device) == 0) { local_addrs = new local_addr (address, local_addrs); if (tracemode || DEBUG) { printf ("Adding local address: %s\n", address); } } #if DEBUG else { std::cerr << "Address skipped for interface " << ifname << std::endl; } #endif } } while (!feof(ifinfo)); fclose(ifinfo); } } typedef u_int32_t tcp_seq; /* ppp header, i hope ;) */ /* glanced from ethereal, it's 16 bytes, and the payload packet type is * in the last 2 bytes... */ struct ppp_header { u_int16_t dummy1; u_int16_t dummy2; u_int16_t dummy3; u_int16_t dummy4; u_int16_t dummy5; u_int16_t dummy6; u_int16_t dummy7; u_int16_t packettype; }; /* TCP header */ // TODO take from elsewhere. struct tcp_hdr { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN u_int th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN u_int th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; Packet::Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction m_dir) { sip = m_sip; sport = m_sport; dip = m_dip; dport = m_dport; len = m_len; time = m_time; dir = m_dir; sa_family = AF_INET; hashstring = NULL; } Packet::Packet (in6_addr m_sip, unsigned short m_sport, in6_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction m_dir) { sip6 = m_sip; sport = m_sport; dip6 = m_dip; dport = m_dport; len = m_len; time = m_time; dir = m_dir; sa_family = AF_INET6; hashstring = NULL; } direction invert(direction dir) { if (dir == dir_incoming) return dir_outgoing; else if (dir == dir_outgoing) return dir_incoming; else return dir_unknown; } Packet * Packet::newInverted () { direction new_direction = invert(dir); if (sa_family == AF_INET) return new Packet (dip, dport, sip, sport, len, time, new_direction); else return new Packet (dip6, dport, sip6, sport, len, time, new_direction); } /* constructs returns a new Packet() structure with the same contents as this one */ Packet::Packet (const Packet &old_packet) { sip = old_packet.sip; sport = old_packet.sport; sip6 = old_packet.sip6; dip6 = old_packet.dip6; dip = old_packet.dip; dport = old_packet.dport; len = old_packet.len; time = old_packet.time; sa_family = old_packet.sa_family; if (old_packet.hashstring == NULL) hashstring = NULL; else hashstring = strdup(old_packet.hashstring); dir = old_packet.dir; } bool sameinaddr(in_addr one, in_addr other) { return one.s_addr == other.s_addr; } bool Packet::isOlderThan (timeval t) { std::cout << "Comparing " << time.tv_sec << " <= " << t.tv_sec << std::endl; return (time.tv_sec <= t.tv_sec); } bool Packet::Outgoing () { /* must be initialised with getLocal("eth0:1");) */ assert (local_addrs != NULL); switch (dir) { case dir_outgoing: return true; case dir_incoming: return false; case dir_unknown: bool islocal; if (sa_family == AF_INET) islocal = local_addrs->contains(sip.s_addr); else islocal = local_addrs->contains(sip6); if (islocal) { dir = dir_outgoing; return true; } else { if (DEBUG) { if (sa_family == AF_INET) islocal = local_addrs->contains(dip.s_addr); else islocal = local_addrs->contains(dip6); if (!islocal) { std::cerr << "Neither dip nor sip are local: "; char addy [50]; inet_ntop (AF_INET6, &sip6, addy, 49); std::cerr << addy << std::endl; inet_ntop (AF_INET6, &dip6, addy, 49); std::cerr << addy << std::endl; return false; } } dir = dir_incoming; return false; } } return false; } /* returns the packet in '1.2.3.4:5-1.2.3.4:5'-form, for use in the 'conninode' table */ /* '1.2.3.4' should be the local address. */ /* the calling code should take care of deletion of the hash string */ char * Packet::gethashstring () { if (hashstring != NULL) { return hashstring; } hashstring = (char *) malloc (HASHKEYSIZE * sizeof(char)); char * local_string = (char *) malloc (50); char * remote_string = (char *) malloc (50); if (sa_family == AF_INET) { inet_ntop(sa_family, &sip, local_string, 49); inet_ntop(sa_family, &dip, remote_string, 49); } else { inet_ntop(sa_family, &sip6, local_string, 49); inet_ntop(sa_family, &dip6, remote_string, 49); } if (Outgoing()) { snprintf(hashstring, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string, sport, remote_string, dport); } else { snprintf(hashstring, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", remote_string, dport, local_string, sport); } free (local_string); free (remote_string); //if (DEBUG) // std::cout << "Returning newly created hash string: " << hashstring << std::endl; return hashstring; } /* 2 packets match if they have the same * source and destination ports and IP's. */ bool Packet::match (Packet * other) { return (sport == other->sport) && (dport == other->dport) && (sameinaddr(sip, other->sip)) && (sameinaddr(dip, other->dip)); } bool Packet::matchSource (Packet * other) { return (sport == other->sport) && (sameinaddr(sip, other->sip)); } nethogs-0.8.1/packet.h000066400000000000000000000043121263557704200146220ustar00rootroot00000000000000/* * packet.h * * Copyright (c) 2004,2006 Arnout Engelen * * 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 __PACKET_H #define __PACKET_H #define _BSD_SOURCE 1 #include #include #include #include #include "nethogs.h" enum direction { dir_unknown, dir_incoming, dir_outgoing }; /* To initialise this module, call getLocal with the currently * monitored device (e.g. "eth0:1") */ void getLocal (const char *device, bool tracemode); class Packet { public: in6_addr sip6; in6_addr dip6; in_addr sip; in_addr dip; unsigned short sport; unsigned short dport; u_int32_t len; timeval time; Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction dir = dir_unknown); Packet (in6_addr m_sip, unsigned short m_sport, in6_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction dir = dir_unknown); /* copy constructor */ Packet (const Packet &old); ~Packet () { if (hashstring != NULL) { free (hashstring); hashstring = NULL; } } /* Packet (const Packet &old_packet); */ /* copy constructor that turns the packet around */ Packet * newInverted (); bool isOlderThan(timeval t); /* is this packet coming from the local host? */ bool Outgoing (); bool match (Packet * other); bool matchSource (Packet * other); /* returns '1.2.3.4:5-1.2.3.4:6'-style string */ char * gethashstring(); private: direction dir; short int sa_family; char * hashstring; }; #endif nethogs-0.8.1/process.cpp000066400000000000000000000165221263557704200153720ustar00rootroot00000000000000/* * process.cpp * * Copyright (c) 2004,2005,2008,2011 Arnout Engelen * * 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 #include "process.h" #include "nethogs.h" /* #include "inodeproc.cpp" */ #include "inode2prog.h" #include "conninode.h" extern local_addr * local_addrs; /* * connection-inode table. takes information from /proc/net/tcp. * key contains source ip, source port, destination ip, destination * port in format: '1.2.3.4:5-1.2.3.4:5' */ extern std::map conninode; /* this file includes: * - calls to inodeproc to get the pid that belongs to that inode */ /* * Initialise the global process-list with some special processes: * * unknown TCP traffic * * UDP traffic * * unknown IP traffic * We must take care these never get removed from the list. */ Process * unknowntcp; Process * unknownudp; Process * unknownip; ProcList * processes; /* We're migrating to having several `unknown' processes that are added as * normal processes, instead of hard-wired unknown processes. * This mapping maps from unknown processes descriptions to processes */ std::map unknownprocs; void process_init () { unknowntcp = new Process (0, "", "unknown TCP"); //unknownudp = new Process (0, "", "unknown UDP"); //unknownip = new Process (0, "", "unknown IP"); processes = new ProcList (unknowntcp, NULL); //processes = new ProcList (unknownudp, processes); //processes = new ProcList (unknownip, processes); } int Process::getLastPacket() { int lastpacket=0; ConnList * curconn=connections; while (curconn != NULL) { assert (curconn != NULL); assert (curconn->getVal() != NULL); if (curconn->getVal()->getLastPacket() > lastpacket) lastpacket = curconn->getVal()->getLastPacket(); curconn = curconn->getNext(); } return lastpacket; } Process * findProcess (struct prg_node * node) { ProcList * current = processes; while (current != NULL) { Process * currentproc = current->getVal(); assert (currentproc != NULL); if (node->pid == currentproc->pid) return current->getVal(); current = current->next; } return NULL; } /* finds process based on inode, if any */ /* should be done quickly after arrival of the packet, * otherwise findPID will be outdated */ Process * findProcess (unsigned long inode) { struct prg_node * node = findPID(inode); if (node == NULL) return NULL; return findProcess (node); } int ProcList::size () { int i=1; if (next != NULL) i += next->size(); return i; } void check_all_procs () { ProcList * curproc = processes; while (curproc != NULL) { curproc->getVal()->check(); curproc = curproc->getNext(); } } /* * returns the process from proclist with matching pid * if the inode is not associated with any PID, return NULL * if the process is not yet in the proclist, add it */ Process * getProcess (unsigned long inode, const char * devicename) { struct prg_node * node = findPID(inode); if (node == NULL) { if (DEBUG || bughuntmode) std::cout << "No PID information for inode " << inode << std::endl; return NULL; } Process * proc = findProcess (node); if (proc != NULL) return proc; Process * newproc = new Process (inode, devicename, node->name.c_str()); newproc->pid = node->pid; char procdir [100]; sprintf(procdir , "/proc/%d", node->pid); struct stat stats; int retval = stat(procdir, &stats); /* 0 seems a proper default. * used in case the PID disappeared while nethogs was running * TODO we can store node->uid this while info on the inodes, * right? */ /* if (!ROBUST && (retval != 0)) { std::cerr << "Couldn't stat " << procdir << std::endl; assert (false); } */ if (retval != 0) newproc->setUid(0); else newproc->setUid(stats.st_uid); /*if (getpwuid(stats.st_uid) == NULL) { std::stderr << "uid for inode if (!ROBUST) assert(false); }*/ processes = new ProcList (newproc, processes); return newproc; } /* * Used when a new connection is encountered. Finds corresponding * process and adds the connection. If the connection doesn't belong * to any known process, the process list is updated and a new process * is made. If no process can be found even then, it's added to the * 'unknown' process. */ Process * getProcess (Connection * connection, const char * devicename) { unsigned long inode = conninode[connection->refpacket->gethashstring()]; if (inode == 0) { // no? refresh and check conn/inode table if (bughuntmode) { std::cout << "? new connection not in connection-to-inode table before refresh.\n"; } // refresh the inode->pid table first. Presumably processing the renewed connection->inode table // is slow, making this worthwhile. // We take the fact for granted that we might already know the inode->pid (unlikely anyway if we // haven't seen the connection->inode yet though). reread_mapping(); refreshconninode(); inode = conninode[connection->refpacket->gethashstring()]; if (bughuntmode) { if (inode == 0) { std::cout << ":( inode for connection not found after refresh.\n"; } else { std::cout << ":) inode for connection found after refresh.\n"; } } #if REVERSEHACK if (inode == 0) { /* HACK: the following is a hack for cases where the * 'local' addresses aren't properly recognised, as is * currently the case for IPv6 */ /* we reverse the direction of the stream if * successful. */ Packet * reversepacket = connection->refpacket->newInverted(); inode = conninode[reversepacket->gethashstring()]; if (inode == 0) { delete reversepacket; if (bughuntmode || DEBUG) std::cout << "LOC: " << connection->refpacket->gethashstring() << " STILL not in connection-to-inode table - adding to the unknown process\n"; unknowntcp->connections = new ConnList (connection, unknowntcp->connections); return unknowntcp; } delete connection->refpacket; connection->refpacket = reversepacket; } #endif } else if (bughuntmode) { std::cout << ";) new connection in connection-to-inode table before refresh.\n"; } if (bughuntmode) { std::cout << " inode # " << inode << std::endl; } Process * proc = NULL; if (inode != 0) proc = getProcess(inode, devicename); if (proc == NULL) { proc = new Process (inode, "", connection->refpacket->gethashstring()); processes = new ProcList (proc, processes); } proc->connections = new ConnList (connection, proc->connections); return proc; } void procclean () { //delete conninode; prg_cache_clear(); } nethogs-0.8.1/process.h000066400000000000000000000054551263557704200150420ustar00rootroot00000000000000/* * process.h * * Copyright (c) 2004-2006,2008,2011 Arnout Engelen * * 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 __PROCESS_H #define __PROCESS_H #include #include "nethogs.h" #include "connection.h" extern bool tracemode; extern bool bughuntmode; void check_all_procs (); class ConnList { public: ConnList (Connection * m_val, ConnList * m_next) { assert (m_val != NULL); val = m_val; next = m_next; } ~ConnList () { /* does not delete its value, to allow a connection to * remove itself from the global connlist in its destructor */ } Connection * getVal () { return val; } void setNext (ConnList * m_next) { next = m_next; } ConnList * getNext () { return next; } private: Connection * val; ConnList * next; }; class Process { public: /* the process makes a copy of the name. the device name needs to be stable. */ Process (const unsigned long m_inode, const char * m_devicename, const char * m_name = NULL) : inode (m_inode) { //std::cout << "ARN: Process created with dev " << m_devicename << std::endl; if (DEBUG) std::cout << "PROC: Process created at " << this << std::endl; if (m_name == NULL) name = NULL; else name = strdup(m_name); devicename = m_devicename; connections = NULL; pid = 0; uid = 0; } void check () { assert (pid >= 0); } ~Process () { free (name); if (DEBUG) std::cout << "PROC: Process deleted at " << this << std::endl; } int getLastPacket (); char * name; const char * devicename; int pid; ConnList * connections; uid_t getUid() { return uid; } void setUid(uid_t m_uid) { uid = m_uid; } unsigned long getInode() { return inode; } private: const unsigned long inode; uid_t uid; }; class ProcList { public: ProcList (Process * m_val, ProcList * m_next) { assert (m_val != NULL); val = m_val; next = m_next; } int size (); Process * getVal () { return val; } ProcList * getNext () { return next; } ProcList * next; private: Process * val; }; Process * getProcess (Connection * connection, const char * devicename = NULL); void process_init (); void refreshconninode (); void procclean (); #endif nethogs-0.8.1/refresh.cpp000066400000000000000000000020701263557704200153430ustar00rootroot00000000000000/* * refresh.cpp * * Copyright (c) 2004 Arnout Engelen * * 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 "nethogs.h" extern bool needrefresh; extern unsigned refreshdelay; void alarm_cb (int /*i*/) { needrefresh = true; //cout << "Setting needrefresh\n"; signal (SIGALRM, &alarm_cb); alarm(refreshdelay); } nethogs-0.8.1/refresh.h000066400000000000000000000014531263557704200150140ustar00rootroot00000000000000/* * refresh.h * * Copyright (c) 2004 Arnout Engelen * * 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. * */ void alarm_cb (int i);