pax_global_header00006660000000000000000000000064142272525360014522gustar00rootroot0000000000000052 comment=1e7e487a019a7c02f1f429c4d3a4647fa3787a13 dropwatch-1.5.4/000077500000000000000000000000001422725253600135245ustar00rootroot00000000000000dropwatch-1.5.4/.travis.yml000066400000000000000000000005101422725253600156310ustar00rootroot00000000000000language: c dist: focal arch: - arm64 - amd64 - ppc64le compiler: - clang - gcc addons: apt: packages: binutils-dev libreadline-dev libnl-3-dev libnl-genl-3-dev libpcap-dev script: ./autogen.sh && ./configure && make && make check after_script: cat ./tests/rundropwatch.sh.log dropwatch-1.5.4/COPYING000066400000000000000000000431331422725253600145630ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. dropwatch-1.5.4/Makefile.am000066400000000000000000000003471422725253600155640ustar00rootroot00000000000000# Makefile.am -- # Copyright 2018 Neil Horman # All Rights Reserved. # SPDX-License-Identifier: GPL-2.0-or-later AUTOMAKE_OPTIONS = no-dependencies ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = COPYING autogen.sh SUBDIRS = src doc tests dropwatch-1.5.4/README.md000066400000000000000000000044731422725253600150130ustar00rootroot00000000000000DropWatch ========= [![Build Status](https://travis-ci.org/nhorman/dropwatch.svg?branch=master)](https://travis-ci.org/nhorman/dropwatch) Thanks for Downloading Dropwatch! What is Dropwatch? ------------------ Dropwatch is a project I started in an effort to improve the ability for developers and system administrators to diagnose problems in the Linux Networking stack, specifically in our ability to diagnose where packets are getting dropped. From my probing, I've come to the conclusion that there are four main shortcomings in our current environment: 1) _Consolidation, or lack thereof._ Currently, if you would like to check on the status of dropped packets in the kernel, you need to check at least 4 places, and possibly more: The /proc/net/snmp file, the netstat utility, the tc utility, and ethtool. This project aims to consolidate several of those checks into one tool, making it easier for a sysadmin or developer to detect lost packets 2) _Clarity of information._ Dropped packets are not obvious. A sysadmin needs to be intimately familiar with each of the above tools to understand which events or statistics correlate to a dropped packet and which do not. While that is often self evident, it is also often not. Dropwatch aims to improve that clarity 3) _Ambiguity._ Even when a dropped packet is detected, the causes for those dropped packets are not always clear. Does a UDPInError mean the application receive buffer was full, or does it mean its checksum was bad? Dropwatch attempts to disambiguate the causes for dropped packets. 4) _Performance._ Utilities can be written to aggregate the data in the various other utilities to solve some of these problems, but such solutions require periodic polling of several interfaces, which is far from optimal, especially when lost packets are rare. This solution improves on the performance aspect by implementing a kernel feature which allows asynchronous notification of dropped packets when they happen. Building Dropwatch ------------------ Dropwatch uses the autotools suite (autoconf/automake) to build. To build and install the utility run the following commands: ``` ./autogen.sh ./configure make make install ``` Questions --------- Feel free to email me directly at nhorman@tuxdriver.com with question, or if you find a bug, open an issue here on the github page dropwatch-1.5.4/autogen.sh000077500000000000000000000002461422725253600155270ustar00rootroot00000000000000#! /bin/sh # SPDX-License-Identifier: GPL-2.0-or-later set -x -e mkdir -p m4 # --no-recursive is available only in recent autoconf versions autoreconf -fv --install dropwatch-1.5.4/configure.ac000066400000000000000000000025501422725253600160140ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later AC_INIT(dropwatch,1.5.4) AC_PREREQ(2.12)dnl AC_CONFIG_HEADERS(config.h) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([foreign] [subdir-objects]) LT_INIT AC_SUBST(LIBTOOL_DEPS) AC_PROG_CC AC_PROG_INSTALL AC_PROG_AWK AC_CHECK_FUNCS(getopt_long) PKG_CHECK_MODULES([LIBNL3], [libnl-3.0], [], [AC_MSG_ERROR([libnl-3.0 is required])]) PKG_CHECK_MODULES([LIBNLG3], [libnl-genl-3.0], [], [AC_MSG_ERROR([libnl-genl-3.0 is required])]) PKG_CHECK_MODULES([READLINE], [readline], [], [AC_MSG_ERROR([libreadline is required])]) PKG_CHECK_MODULES([LIBPCAP], [libpcap], [], [ AC_CHECK_LIB(pcap, pcap_open_live,[], [AC_MSG_ERROR([libpcap is required])])]) AC_ARG_WITH([bfd], [AS_HELP_STRING([--without-bfd], [Build without bfd library (default: yes)])], [with_bfd=$withval], [with_bfd=yes]) AS_IF([test "x$with_bfd" != "xno"], [ AC_CHECK_HEADERS([bfd.h], [], [AC_MSG_ERROR([Couldn't find or include bfd.h])]) ]) AM_CONDITIONAL(USE_BFD, test "x$with_bfd" != "xno") AC_OUTPUT(Makefile src/Makefile doc/Makefile tests/Makefile) AC_MSG_NOTICE() AC_MSG_NOTICE([dropwatch Version: $VERSION]) AC_MSG_NOTICE([Target: $target]) AC_MSG_NOTICE([Installation prefix: $prefix]) AC_MSG_NOTICE([Compiler: $CC]) AC_MSG_NOTICE([Compiler flags: $CFLAGS]) AC_MSG_NOTICE([BFD library support: $with_bfd]) dropwatch-1.5.4/doc/000077500000000000000000000000001422725253600142715ustar00rootroot00000000000000dropwatch-1.5.4/doc/Makefile.am000066400000000000000000000001221422725253600163200ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later dist_man_MANS = dropwatch.1 dwdump.1 dropwatch-1.5.4/doc/dropwatch.1000066400000000000000000000054061422725253600163530ustar00rootroot00000000000000.TH dropwatch "1" "Mar 2009" "Neil Horman" .SH NAME dropwatch \- kernel dropped packet monitoring utility .SH SYNOPSIS \fBdropwatch\fP [\fB\-l\fP \fImethod\fP | \fBlist\fP] .SH DESCRIPTION .B dropwatch is an interactive utility for monitoring and recording packets that are dropped by the kernel. .SH OPTIONS .TP \fB\-l\fP \fImethod\fP | \fBlist\fP, \fB\-\-lmethod\fP \fImethod\fP | \fBlist\fP Select the translation method to use when a drop alert arrives. By default the raw instruction pointer of a drop location is output, but by the use of the \fB\-l\fP option, we can assign a translation method so that the instruction pointer can be translated into function names. Currently supported lookup \fImethods\fP are: .RS .TP .B list Show all supported methods. .TP .B kas Use \fI/proc/kallsyms\fP to lookup instruction pointers to function mappings. .RE .SH INTERACTIVE COMMANDS .TP .B start Tells the kernel to start reporting dropped packets. .TP .B stop Tells the kernel to discontinue reporting dropped packets. .TP .B exit Exits the \fBdropmonitor\fP program. .TP .B help Displays summary of all commands. .TP \fBset alertlimit\fP \fIvalue\fP Sets a triggerpoint to stop monitoring for dropped packets after \fIvalue\fP alerts have been received. .TP .BR "set alertmode " "{ " summary " | " packet " }" .I summary - Default mode. A summary of recent packet drops is sent to user space. The alert includes drop locations and number of drops in each location. .I packet - Each dropped packet is sent to user space. The alert includes the packet itself and various metadata such as drop location and timestamp. .TP .BI "set trunc " "len" Sets the truncation length. Reported packets will be truncated to length \fIlen\fP. This setting is only applicable when \fIalertmode\fP is set to \fIpacket\fP. By default packets are not truncated. A value of \fI0\fP disables truncation. .TP .BI "set queue " "len" Sets the queue length in the kernel. When \fIalertmode\fP is set to \fIpacket\fP, the kernel queues dropped packets in a per-CPU drop list before preparing a netlink message for each. This setting controls the queue's length. By default this is limited by the kernel to 1,000 packets. Increasing this value will increase the memory utilization of the system. .TP .BR "set sw " "{ " true " | " false " }" Enables or disables the monitoring of software originated drops. By default software originated drops are monitored. .TP .BR "set hw " "{ " true " | " false " }" Enables or disables the monitoring of hardware originated drops. By default hardware originated drops are not monitored. .TP .B show Query existing configuration from the kernel and show it. .TP .B stats Query statistics from the kernel and show it. .I "Tail dropped" - Number of packets that could not be enqueued to the per-CPU drop list(s). dropwatch-1.5.4/doc/dwdump.1000066400000000000000000000032341422725253600156550ustar00rootroot00000000000000.TH dwdump 1 "Jan 2020" "Ido Schimmel" .SH NAME dwdump \- dump kernel dropped packets to a file .SH SYNOPSIS .sp .ad l .in +8 .ti -8 .B dwdump .RI "[ " OPTIONS " ]" .sp .SH OPTIONS .TP .BR "\-w" , " --write " \fIFILE Dump packets to provided file in pcap format. Defaults to standard output. .TP .BR "\-t", " --trunc " \fILENGTH Ask the kernel to truncate packets to provided length. Defaults to no truncation. .TP .BR "-q", " --query" Query the kernel for current configuration and exit. .TP .BR "-l", " --limit " \fILIMIT Ask the kernel to set the per-CPU packet queue limit to provided limit. Defaults to 1,000 packets. .TP .BR "-p", " --passive" Only listen on notified packets with no configuration. This is useful if the kernel is already monitoring dropped packets and you only want to open another listening socket. .TP .BR "-s", " --stats" Query the kernel for statistics and exit. .TP .BR "-b", " --bufsize " \fISIZE Set the socket's receive buffer to provided size. Defaults to 1MB. .TP .BR "-o", " --origin " "{ " sw " | " hw " }" Ask the kernel to only monitor software or hardware originated drops. Defaults to both. See \fBdevlink-trap\fR(8) for details on how to get hardware originated drops to the kernel. .TP .BR "-e", " --exit" Ask the kernel to stop monitoring and exit. .SH "EXAMPLES" .PP dwdump -w drops.pcap .RS 4 Dump dropped packets to a file. .RE .PP dwdump | tshark -V -r - .RS 4 Pipe dropped packets to Wireshark. .RE .PP dwdump -o sw -w drops.pcap .RS 4 Only monitor software originated drops. .RE .PP dwdump -q .RS 4 Query current configuration from the kernel and exit. .RE .SH SEE ALSO .BR dropwatch (1), .BR devlink-trap (8), .BR tshark (1), .br dropwatch-1.5.4/spec/000077500000000000000000000000001422725253600144565ustar00rootroot00000000000000dropwatch-1.5.4/spec/dropwatch.spec000066400000000000000000000026371422725253600173350ustar00rootroot00000000000000%define uversion MAKEFILE_VERSION Summary: Kernel dropped packet monitor Name: dropwatch Version: %{uversion} Release: 0%{?dist} Source0: https://fedorahosted.org/releases/d/r/dropwatch/dropwatch-%{uversion}.tbz2 URL: http://fedorahosted.org/dropwatch License: GPLv2+ Group: Applications/System BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: kernel-devel, libnl-devel, readline-devel BuildRequires: binutils-devel, binutils-static pkgconfig Requires: libnl, readline %description dropwatch is an utility to interface to the kernel to monitor for dropped network packets. %prep %setup -q %build cd src export CFLAGS=$RPM_OPT_FLAGS make %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT%{_bindir} mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1 install -m0755 src/dropwatch $RPM_BUILD_ROOT%{_bindir} install -m0644 doc/dropwatch.1 $RPM_BUILD_ROOT%{_mandir}/man1 %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %{_bindir}/* %{_mandir}/man1/* %doc README %doc COPYING %changelog * Thu Apr 08 2010 Neil Horman 1.1-0 - Fixing BuildRequires in spec, and removing release variable * Thu Mar 26 2009 Neil Horman 1.0-3 - Updating Makefile to include release num in tarball * Fri Mar 20 2009 Neil Horman 1.0-2 - Fixed up Errors found in package review (bz 491240) * Tue Mar 17 2009 Neil Horman 1.0-1 - Initial build dropwatch-1.5.4/src/000077500000000000000000000000001422725253600143135ustar00rootroot00000000000000dropwatch-1.5.4/src/Makefile.am000066400000000000000000000006261422725253600163530ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # bin_PROGRAMS = dropwatch dwdump AM_CFLAGS = -g -Wall -Werror $(LIBNL3_CFLAGS) $(LIBNLG3_CFLAGS) $(READLINE_CFLAGS) AM_LDFLAGS = $(LIBNL3_LIBS) $(LIBNLG3_LIBS) $(READLINE_LIBS) -lpcap AM_CPPFLAGS = -D_GNU_SOURCE dropwatch_SOURCES = main.c lookup.c lookup_kas.c dwdump_SOURCES = dwdump.c if USE_BFD dropwatch_SOURCES += lookup_bfd.c AM_LDFLAGS += -lbfd endif dropwatch-1.5.4/src/dwdump.c000066400000000000000000000422451422725253600157660ustar00rootroot00000000000000/* * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "net_dropmon.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static struct nla_policy net_dm_policy[NET_DM_ATTR_MAX + 1] = { [NET_DM_ATTR_ALERT_MODE] = { .type = NLA_U8 }, [NET_DM_ATTR_TRUNC_LEN] = { .type = NLA_U32 }, [NET_DM_ATTR_QUEUE_LEN] = { .type = NLA_U32 }, [NET_DM_ATTR_STATS] = { .type = NLA_NESTED }, [NET_DM_ATTR_HW_STATS] = { .type = NLA_NESTED }, }; static struct nla_policy net_dm_stats_policy[NET_DM_ATTR_STATS_MAX + 1] = { [NET_DM_ATTR_STATS_DROPPED] = { .type = NLA_U64 }, }; static bool stop; enum dwdump_pkt_origin { DWDUMP_PKT_ORIGIN_ALL, DWDUMP_PKT_ORIGIN_SW, DWDUMP_PKT_ORIGIN_HW, }; struct dwdump_options { const char *dumpfile; __u32 trunc_len; bool query; __u32 queue_len; bool passive; bool stats; int rxbuf; bool exit; enum dwdump_pkt_origin origin; bool need_pcap; bool need_mon; }; struct dwdump { struct nl_sock *csk, *dsk; pcap_t *pcap_handle; pcap_dumper_t *pcap_dumper; struct dwdump_options options; int family; long snaplen; char *pcap_buf, *pkt; }; /* Based on https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html */ struct linux_sll { __be16 pkttype; __be16 hatype; __be16 halen; unsigned char addr[8]; __be16 family; }; static int dwdump_data_init(struct dwdump *dwdump) { int fd, optval, family, err; struct nl_sock *sk; sk = nl_socket_alloc(); if (!sk) { fprintf(stderr, "Failed to allocate data socket\n"); return -1; } /* Add wiggle room for other netlink attributes in addition to the * payload. */ dwdump->snaplen = dwdump->options.trunc_len + 2048; dwdump->pkt = calloc(1, dwdump->snaplen); if (!dwdump->pkt) { perror("calloc"); goto err_pkt_alloc; } err = genl_connect(sk); if (err) { fprintf(stderr, "Failed to connect data socket\n"); goto err_genl_connect; } family = genl_ctrl_resolve(sk, "NET_DM"); if (family < 0) { fprintf(stderr, "Failed to resolve ID of \"NET_DM\" family\n"); goto err_genl_ctrl_resolve; } err = nl_socket_set_buffer_size(sk, dwdump->options.rxbuf, 0); if (err < 0) { fprintf(stderr, "Failed to set receive buffer size of data socket\n"); goto err_set_buffer_size; } optval = 1; fd = nl_socket_get_fd(sk); err = setsockopt(fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &optval, sizeof(optval)); if (err < 0) { fprintf(stderr, "Failed to set NETLINK_NO_ENOBUFS socket option\n"); goto err_setsockopt; } err = nl_socket_add_memberships(sk, NET_DM_GRP_ALERT, NFNLGRP_NONE); if (err) { fprintf(stderr, "Failed to join multicast group\n"); goto err_add_memberships; } dwdump->dsk = sk; dwdump->family = family; return 0; err_add_memberships: err_setsockopt: err_set_buffer_size: err_genl_ctrl_resolve: err_genl_connect: free(dwdump->pkt); err_pkt_alloc: nl_socket_free(sk); return -1; } static void dwdump_data_fini(struct dwdump *dwdump) { nl_socket_drop_memberships(dwdump->dsk, NET_DM_GRP_ALERT, NFNLGRP_NONE); free(dwdump->pkt); nl_socket_free(dwdump->dsk); } static const char *dwdump_alert_mode(uint8_t alert_mode) { switch (alert_mode) { case NET_DM_ALERT_MODE_SUMMARY: return "summary"; case NET_DM_ALERT_MODE_PACKET: return "packet"; } return "invalid alert mode"; } static int dwdump_config_set(struct dwdump *dwdump) { struct nl_sock *sk = dwdump->csk; struct nl_msg *msg; int err; msg = nlmsg_alloc(); if (!msg) { fprintf(stderr, "Failed to allocate netlink message\n"); return -1; } if (!genlmsg_put(msg, 0, NL_AUTO_SEQ, dwdump->family, 0, NLM_F_REQUEST|NLM_F_ACK, NET_DM_CMD_CONFIG, 0)) goto genlmsg_put_failure; if (nla_put_u8(msg, NET_DM_ATTR_ALERT_MODE, NET_DM_ALERT_MODE_PACKET)) goto nla_put_failure; if (nla_put_u32(msg, NET_DM_ATTR_TRUNC_LEN, dwdump->options.trunc_len)) goto nla_put_failure; if (nla_put_u32(msg, NET_DM_ATTR_QUEUE_LEN, dwdump->options.queue_len)) goto nla_put_failure; err = nl_send_sync(sk, msg); if (err < 0) { fprintf(stderr, "Failed to configure drop monitor kernel module\n"); return err; } return 0; nla_put_failure: genlmsg_put_failure: nlmsg_free(msg); return -EMSGSIZE; } static int dwdump_config_get(struct dwdump *dwdump) { struct nlattr *attrs[NET_DM_ATTR_MAX + 1]; struct sockaddr_nl nla; unsigned char *buf; uint8_t alert_mode; int len, err; err = genl_send_simple(dwdump->csk, dwdump->family, NET_DM_CMD_CONFIG_GET, 0, NLM_F_REQUEST); if (err < 0) { fprintf(stderr, "Failed to query configuration\n"); return -1; } len = nl_recv(dwdump->csk, &nla, &buf, NULL); if (len < 0) return -1; err = genlmsg_parse((void *) buf, 0, attrs, NET_DM_ATTR_MAX, net_dm_policy); if (err < 0) return -1; if (!attrs[NET_DM_ATTR_ALERT_MODE] || !attrs[NET_DM_ATTR_TRUNC_LEN] || !attrs[NET_DM_ATTR_QUEUE_LEN]) return -1; alert_mode = nla_get_u8(attrs[NET_DM_ATTR_ALERT_MODE]); printf("Alert mode: %s\n", dwdump_alert_mode(alert_mode)); printf("Truncation length: %u\n", nla_get_u32(attrs[NET_DM_ATTR_TRUNC_LEN])); printf("Queue length: %u\n", nla_get_u32(attrs[NET_DM_ATTR_QUEUE_LEN])); return 0; } static void dwdump_nested_stats_print(struct nlattr *attr) { struct nlattr *attrs[NET_DM_ATTR_STATS_MAX + 1]; int err; err = nla_parse_nested(attrs, NET_DM_ATTR_STATS_MAX, attr, net_dm_stats_policy); if (err) return; if (attrs[NET_DM_ATTR_STATS_DROPPED]) printf("Tail dropped: %" PRIu64 "\n", nla_get_u64(attrs[NET_DM_ATTR_STATS_DROPPED])); } static int dwdump_stats_get(struct dwdump *dwdump) { struct nlattr *attrs[NET_DM_ATTR_MAX + 1]; struct sockaddr_nl nla; unsigned char *buf; int len, err; err = genl_send_simple(dwdump->csk, dwdump->family, NET_DM_CMD_STATS_GET, 0, NLM_F_REQUEST); if (err < 0) { fprintf(stderr, "Failed to query statistics\n"); return -1; } len = nl_recv(dwdump->csk, &nla, &buf, NULL); if (len < 0) return -1; err = genlmsg_parse((void *) buf, 0, attrs, NET_DM_ATTR_MAX, net_dm_policy); if (err < 0) return -1; if (attrs[NET_DM_ATTR_STATS]) { printf("Software statistics:\n"); dwdump_nested_stats_print(attrs[NET_DM_ATTR_STATS]); } if (attrs[NET_DM_ATTR_HW_STATS]) { printf("Hardware statistics:\n"); dwdump_nested_stats_print(attrs[NET_DM_ATTR_HW_STATS]); } return 0; } static int dwdump_monitor_origin_put(const struct dwdump *dwdump, struct nl_msg *msg) { switch (dwdump->options.origin) { case DWDUMP_PKT_ORIGIN_ALL: if (nla_put_flag(msg, NET_DM_ATTR_SW_DROPS) || nla_put_flag(msg, NET_DM_ATTR_HW_DROPS)) return -EMSGSIZE; break; case DWDUMP_PKT_ORIGIN_SW: if (nla_put_flag(msg, NET_DM_ATTR_SW_DROPS)) return -EMSGSIZE; break; case DWDUMP_PKT_ORIGIN_HW: if (nla_put_flag(msg, NET_DM_ATTR_HW_DROPS)) return -EMSGSIZE; break; } return 0; } static int dwdump_monitor(struct dwdump *dwdump, bool start) { uint8_t cmd = start ? NET_DM_CMD_START : NET_DM_CMD_STOP; struct nl_sock *sk = dwdump->csk; struct nl_msg *msg; int err; msg = nlmsg_alloc(); if (!msg) { fprintf(stderr, "Failed to allocate netlink message\n"); return -1; } if (!genlmsg_put(msg, 0, NL_AUTO_SEQ, dwdump->family, 0, NLM_F_REQUEST|NLM_F_ACK, cmd, 0)) goto genlmsg_put_failure; err = dwdump_monitor_origin_put(dwdump, msg); if (err < 0) goto genlmsg_put_failure; err = nl_send_sync(sk, msg); if (err < 0) { fprintf(stderr, "Failed to %s monitoring\n", start ? "start" : "stop"); return err; } return 0; genlmsg_put_failure: nlmsg_free(msg); return -EMSGSIZE; } static void dwdump_handler(int sig) { stop = true; } static int dwdump_sighandler_install(void) { static const int signals_catch[] = { SIGINT, SIGQUIT, SIGTERM, SIGPIPE, SIGHUP, }; struct sigaction saction; int i, err; memset(&saction, 0, sizeof(saction)); saction.sa_handler = dwdump_handler; for (i = 0; i < ARRAY_SIZE(signals_catch); i++) { err = sigaction(signals_catch[i], &saction, NULL); if (err) { perror("sigaction"); return err; } } return 0; } static int dwdump_ctrl_init(struct dwdump *dwdump) { struct nl_sock *sk; int err; sk = nl_socket_alloc(); if (!sk) { fprintf(stderr, "Failed to allocate control socket\n"); return -1; } err = genl_connect(sk); if (err) { fprintf(stderr, "Failed to connect control socket"); goto err_genl_connect; } dwdump->csk = sk; if (!dwdump->options.need_mon) return 0; err = dwdump_config_set(dwdump); if (err) goto err_config_set; err = dwdump_monitor(dwdump, true); if (err) goto err_monitor; return 0; err_monitor: err_config_set: err_genl_connect: nl_socket_free(sk); return err; } static void dwdump_ctrl_fini(struct dwdump *dwdump) { if (!dwdump->options.need_mon) goto out; dwdump_monitor(dwdump, false); out: nl_socket_free(dwdump->csk); } static void dwdump_pcap_write(struct dwdump *dwdump, unsigned char *buf, int len) { struct pcap_pkthdr hdr; int pkt_len; if (len + sizeof(struct linux_sll) < dwdump->snaplen) pkt_len = len + sizeof(struct linux_sll); else pkt_len = dwdump->snaplen - sizeof(struct linux_sll); memcpy(dwdump->pcap_buf + sizeof(struct linux_sll), buf, pkt_len); hdr.caplen = pkt_len; hdr.len = pkt_len; gettimeofday(&hdr.ts, NULL); pcap_dump((unsigned char *) dwdump->pcap_dumper, &hdr, (const unsigned char *) dwdump->pcap_buf); /* In case packets are written to stdout, make sure each packet is * immediately written and not buffered. */ fflush(NULL); } static int dwdump_pcap_buf_init(struct dwdump *dwdump) { struct linux_sll sll; /* The wireshark netlink dissector expects netlink messages to start * with a Linux cooked header (SLL), so include it before each packet. */ memset(&sll, 0, sizeof(sll)); sll.pkttype = htons(PACKET_OUTGOING); sll.hatype = htons(ARPHRD_NETLINK); sll.family = htons(AF_NETLINK); dwdump->pcap_buf = calloc(1, dwdump->snaplen); if (!dwdump->pcap_buf) { perror("calloc"); return -1; } memcpy(dwdump->pcap_buf, &sll, sizeof(sll)); return 0; } static void dwdump_pcap_buf_fini(struct dwdump *dwdump) { free(dwdump->pcap_buf); } static int dwdump_pcap_genl_init(struct dwdump *dwdump) { struct sockaddr_nl nla; unsigned char *buf; int len, err; /* In order for wireshark to be able to invoke the net_dm dissector, * it must learn about the mapping between the generic netlink * family ID and its name from this dump. * * Reference: * https://www.wireshark.org/lists/wireshark-users/201907/msg00027.html */ err = genl_send_simple(dwdump->csk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1, NLM_F_DUMP); if (err < 0) { fprintf(stderr, "Failed to dump generic netlink families\n"); return -1; } len = nl_recv(dwdump->csk, &nla, &buf, NULL); if (len < 0) return -1; dwdump_pcap_write(dwdump, buf, len); return 0; } static int dwdump_pcap_init(struct dwdump *dwdump) { int err; if (!dwdump->options.need_pcap) return 0; dwdump->pcap_handle = pcap_open_dead(DLT_NETLINK, dwdump->snaplen); if (!dwdump->pcap_handle) { perror("pcap_open_dead"); return -1; } dwdump->pcap_dumper = pcap_dump_open(dwdump->pcap_handle, dwdump->options.dumpfile); if (!dwdump->pcap_dumper) { pcap_perror(dwdump->pcap_handle, "pcap_dump_open"); goto err_dump_open; } err = dwdump_pcap_buf_init(dwdump); if (err) goto err_buf_init; err = dwdump_pcap_genl_init(dwdump); if (err) goto err_genl_init; return 0; err_genl_init: dwdump_pcap_buf_fini(dwdump); err_buf_init: pcap_dump_close(dwdump->pcap_dumper); err_dump_open: pcap_close(dwdump->pcap_handle); return -1; } static void dwdump_pcap_fini(struct dwdump *dwdump) { if (!dwdump->options.need_pcap) return; dwdump_pcap_buf_fini(dwdump); pcap_dump_close(dwdump->pcap_dumper); pcap_close(dwdump->pcap_handle); } static int dwdump_init(struct dwdump *dwdump) { int err; err = dwdump_data_init(dwdump); if (err) return err; err = dwdump_ctrl_init(dwdump); if (err) goto err_ctrl_init; err = dwdump_pcap_init(dwdump); if (err) goto err_pcap_init; err = dwdump_sighandler_install(); if (err) goto err_sighandler_install; return 0; err_sighandler_install: dwdump_pcap_fini(dwdump); err_pcap_init: dwdump_ctrl_fini(dwdump); err_ctrl_init: dwdump_data_fini(dwdump); return err; } static void dwdump_fini(struct dwdump *dwdump) { dwdump_pcap_fini(dwdump); dwdump_ctrl_fini(dwdump); dwdump_data_fini(dwdump); } static int dwdump_main(struct dwdump *dwdump) { int fd = nl_socket_get_fd(dwdump->dsk); if (dwdump->options.query) return dwdump_config_get(dwdump); if (dwdump->options.stats) return dwdump_stats_get(dwdump); if (dwdump->options.exit) return dwdump_monitor(dwdump, false); while (!stop) { int len; /* Use recv() instead of nl_recv() since interruption of * nl_recv() causes the operation to be retried. */ len = recv(fd, dwdump->pkt, dwdump->snaplen, 0); if (len < 0) { switch (errno) { case EINTR: continue; default: perror("recv"); return -1; } } dwdump_pcap_write(dwdump, (unsigned char *) dwdump->pkt, len); } return 0; } static int dwdump_origin_parse(struct dwdump *dwdump, const char *origin) { if (strcmp(origin, "sw") == 0) { dwdump->options.origin = DWDUMP_PKT_ORIGIN_SW; return 0; } else if (strcmp(origin, "hw") == 0) { dwdump->options.origin = DWDUMP_PKT_ORIGIN_HW; return 0; } else { fprintf(stderr, "Invalid origin: \'%s\'\n", origin); return -EINVAL; } } static void dwdump_usage(FILE *fp) { fprintf(fp, "Usage:\n"); fprintf(fp, "dwdump [ -w -t -q -l -p -s -b -e -o ]\n"); fprintf(fp, " -w dump packets to provided file. defaults to standard output\n"); fprintf(fp, " -t truncate packets to provided length. defaults to no truncation\n"); fprintf(fp, " -q query the kernel for current configuration and exit\n"); fprintf(fp, " -l set packet queue limit to provided limit\n"); fprintf(fp, " -p only listen on notified packets with no configuration\n"); fprintf(fp, " -s query kernel for statistics and exit\n"); fprintf(fp, " -b set the socket's receive buffer to provided size\n"); fprintf(fp, " -o monitor only originated drops. defaults to all\n"); fprintf(fp, " -e ask kernel to stop monitoring and exit\n"); } static int dwdump_opts_parse(struct dwdump *dwdump, int argc, char **argv) { static const struct option long_options[] = { { "write", required_argument, NULL, 'w' }, { "trunc", required_argument, NULL, 't' }, { "query", no_argument, NULL, 'q' }, { "limit", required_argument, NULL, 'l' }, { "passive", no_argument, NULL, 'p' }, { "stats", no_argument, NULL, 's' }, { "bufsize", required_argument, NULL, 'b' }, { "origin", required_argument, NULL, 'o' }, { "exit", no_argument, NULL, 'e' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 } }; static const char optstring[] = "w:t:ql:psb:o:eh"; int opt, err; /* Default values */ dwdump->options.dumpfile = "/dev/stdout"; dwdump->options.trunc_len = 0xffff; dwdump->options.queue_len = 1000; dwdump->options.rxbuf = 1024 * 1024; dwdump->options.origin = DWDUMP_PKT_ORIGIN_ALL; dwdump->options.need_pcap = true; dwdump->options.need_mon = true; while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) { switch (opt) { case 'w': dwdump->options.dumpfile = optarg; break; case 't': dwdump->options.trunc_len = atol(optarg); if (dwdump->options.trunc_len == 0 || dwdump->options.trunc_len > 0xffff) dwdump->options.trunc_len = 0xffff; break; case 'q': dwdump->options.query = true; dwdump->options.need_pcap = false; dwdump->options.need_mon = false; break; case 'l': dwdump->options.queue_len = atol(optarg); break; case 'p': dwdump->options.passive = true; dwdump->options.need_mon = false; break; case 's': dwdump->options.stats = true; dwdump->options.need_pcap = false; dwdump->options.need_mon = false; break; case 'b': dwdump->options.rxbuf = atol(optarg); break; case 'o': err = dwdump_origin_parse(dwdump, optarg); if (err) return err; break; case 'e': dwdump->options.exit = true; dwdump->options.need_pcap = false; dwdump->options.need_mon = false; break; case 'h': dwdump_usage(stdout); return -1; case '?': dwdump_usage(stderr); return -1; default: fprintf(stderr, "Unknown option: \'%c\'\n", opt); dwdump_usage(stderr); return -1; } } return 0; } int main(int argc, char **argv) { struct dwdump *dwdump; int err; dwdump = calloc(1, sizeof(*dwdump)); if (!dwdump) { perror("calloc"); goto err_dwdump_alloc; } err = dwdump_opts_parse(dwdump, argc, argv); if (err) goto err_opts_parse; err = dwdump_init(dwdump); if (err) goto err_dwdump_init; err = dwdump_main(dwdump); dwdump_fini(dwdump); free(dwdump); return err; err_dwdump_init: err_opts_parse: free(dwdump); err_dwdump_alloc: exit(EXIT_FAILURE); } dropwatch-1.5.4/src/lookup.c000066400000000000000000000036161422725253600157760ustar00rootroot00000000000000/* * Copyright (C) 2009, Neil Horman * SPDX-License-Identifier: GPL-2.0-or-later */ /* * This is a translator. given an input address, this will convert it into a * function and offset. Unless overridden, it will automatically determine * translations using the following methods, in order of priority: * 1) /usr/lib/debug/ using libbfd * 2) /proc/kallsyms */ #include "config.h" #include #include #include #ifdef HAVE_BFD_H #include #endif #include #include #include #include #include "lookup.h" #ifdef HAVE_BFD_H extern struct lookup_methods bfd_methods; #endif extern struct lookup_methods kallsym_methods; static int lookup_null_init(void) { printf("Initializing null lookup method\n"); return 0; } static int lookup_null_sym(void *pc, struct loc_result *location) { /* * In the null method, every lookup fails */ return 1; } static struct lookup_methods null_methods = { lookup_null_init, lookup_null_sym, }; static struct lookup_methods *methods = NULL; int init_lookup(lookup_init_method_t method) { int rc; switch (method) { case METHOD_NULL: /* * Don't actually do any lookups, * just pretend everything is * not found */ methods = &null_methods; break; case METHOD_AUTO: #ifdef HAVE_BFD_H methods = &bfd_methods; if (methods->lookup_init() == 0) return 0; #endif methods = &kallsym_methods; if (methods->lookup_init() == 0) return 0; methods = NULL; return -1; #ifdef HAVE_BFD_H case METHOD_DEBUGINFO: methods = &bfd_methods; break; #endif case METHOD_KALLSYMS: methods = &kallsym_methods; break; } rc = methods->lookup_init(); if (rc < 0) methods = NULL; return rc; } int lookup_symbol(void *pc, struct loc_result *loc) { if (loc == NULL) return 1; return methods->get_symbol(pc, loc); } dropwatch-1.5.4/src/lookup.h000066400000000000000000000024501422725253600157760ustar00rootroot00000000000000/* * Copyright (C) 2009, Neil Horman * SPDX-License-Identifier: GPL-2.0-or-later */ /* * This is a translator. given an input address, this will convert it into a * function and offset. Unless overridden, it will automatically determine * translations using the following methods, in order of priority: * 1) /usr/lib/debug/ using libbfd * 2) /proc/kallsyms */ #include "config.h" #include #include /* * Initialization routine * INPUTS: * method - enum describing how to do translation * * METHOD_NULL : Just print pc values, not symbols * * METHOD_AUTO : automatic search for best method * * METHOD_DEBUGINFO : use debuginfo package * * METHOD_KALLSYMS : use /proc/kallsyms * returns: * * 0 : initalization succeeded * * < 0 : initalization failed */ typedef enum { METHOD_NULL = 0, METHOD_AUTO, #ifdef HAVE_BFD_H METHOD_DEBUGINFO, #endif METHOD_KALLSYMS } lookup_init_method_t; struct loc_result { const char *symbol; __u64 offset; }; int init_lookup(lookup_init_method_t method); int lookup_symbol(void *pc, struct loc_result *location); struct lookup_methods { int (*lookup_init)(void); int(*get_symbol)(void *pc, struct loc_result *location); }; dropwatch-1.5.4/src/lookup_bfd.c000066400000000000000000000020651422725253600166060ustar00rootroot00000000000000/* * Copyright (C) 2009, Neil Horman * SPDX-License-Identifier: GPL-2.0-or-later */ /* * This is a translator. given an input address, this will convert it into a * symbollic name using the bfd library */ #include "config.h" #include #include #include #include #include #include #include #include #include "lookup.h" static int lookup_bfd_init(void) { struct utsname uts; struct stat sb; char *dbibuf; /* *Start by determining if we have the required debuginfo package *here */ if(uname(&uts)<0) return-1; dbibuf = malloc(strlen("/usr/lib/debug/lib/modules/") + sizeof(uts.release)); sprintf(dbibuf,"/usr/lib/debug/lib/modules/%s", uts.release); if (stat(dbibuf,&sb) < 0) { free(dbibuf); goto out_fail; } free(dbibuf); bfd_init(); return 0; out_fail: return-1; } static int lookup_bfd_sym(void *pc, struct loc_result *location) { return 1; } struct lookup_methods bfd_methods = { lookup_bfd_init, lookup_bfd_sym, }; dropwatch-1.5.4/src/lookup_kas.c000066400000000000000000000054101422725253600166260ustar00rootroot00000000000000/* * Copyright (C) 2009, Neil Horman * SPDX-License-Identifier: GPL-2.0-or-later */ /* * This is a translator. given an input address, this will convert it into a * symbolic name using /proc/kallsyms */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "lookup.h" struct symbol_entry { char *sym_name; __u64 start; __u64 end; LIST_ENTRY(symbol_entry) list; }; LIST_HEAD(sym_list, symbol_entry); /* * This is our cache of symbols that we've previously looked up */ static struct sym_list sym_list_head = {NULL}; static int lookup_kas_cache( __u64 pc, struct loc_result *location) { struct symbol_entry *sym; LIST_FOREACH(sym, &sym_list_head, list) { if ((pc >= sym->start) && (pc <= sym->end)) { location->symbol = sym->sym_name; location->offset = (pc - sym->start); return 0; } } return 1; } static void kas_add_cache(__u64 start, __u64 end, char *name) { struct symbol_entry *sym = NULL; sym = malloc(sizeof(struct symbol_entry)); if (!sym) return; sym->start = start; sym->end = end; sym->sym_name = name; LIST_INSERT_HEAD(&sym_list_head, sym, list); return; } static int lookup_kas_proc(__u64 pc, struct loc_result *location) { FILE *pf; __u64 ppc; __u64 uppc, ulpc, uipc; char *name, *last_name; pf = fopen("/proc/kallsyms", "r"); if (!pf) return 1; last_name = NULL; uipc = pc; ulpc = 0; while (!feof(pf)) { /* * Each line of /proc/kallsyms is formatteded as: * - "%pK %c %s\n" (for kernel internal symbols), or * - "%pK %c %s\t[%s]\n" (for module-provided symbols) */ if (fscanf(pf, "%llx %*s %ms [ %*[^]] ]", (unsigned long long *)&ppc, &name) < 0) { if (ferror(pf)) { perror("Error Scanning File: "); break; } if (feof(pf)) { continue; } } uppc = (__u64)ppc; if ((uipc >= ulpc) && (uipc < uppc)) { /* * The last symbol we looked at * was a hit, record and return it * Note that we don't free last_name * here, because the cache is using it */ kas_add_cache(ulpc, uppc-1, last_name); fclose(pf); free(name); return lookup_kas_cache(pc, location); } /* * Advance all our state holders */ free(last_name); last_name = name; ulpc = uppc; } fclose(pf); return 1; } static int lookup_kas_init(void) { printf("Initializing kallsyms db\n"); return 0; } static int lookup_kas_sym(void *pc, struct loc_result *location) { __u64 pcv; pcv = (uintptr_t)pc; if (!lookup_kas_cache(pcv, location)) return 0; return lookup_kas_proc(pcv, location); } struct lookup_methods kallsym_methods = { lookup_kas_init, lookup_kas_sym, }; dropwatch-1.5.4/src/main.c000066400000000000000000000635101422725253600154100ustar00rootroot00000000000000/* * Copyright (C) 2009, Neil Horman * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Opens our netlink socket. Returns the socket descriptor or < 0 on error */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "net_dropmon.h" #include "lookup.h" /* * This is just in place until the kernel changes get committed */ #ifndef NETLINK_DRPMON #define NETLINK_DRPMON 20 #endif struct netlink_message { void *msg; struct nl_msg *nlbuf; int refcnt; LIST_ENTRY(netlink_message) ack_list_element; int seq; void (*ack_cb)(struct netlink_message *amsg, struct netlink_message *msg, int err); }; LIST_HEAD(ack_list, netlink_message); struct ack_list ack_list_head = {NULL}; unsigned long alimit = 0; unsigned long acount = 0; unsigned long trunc_len = 0; unsigned long queue_len = 0; bool monitor_sw = false; bool monitor_hw = false; void handle_dm_alert_msg(struct netlink_message *msg, int err); void handle_dm_packet_alert_msg(struct netlink_message *msg, int err); void handle_dm_config_new_msg(struct netlink_message *msg, int err); void handle_dm_stats_new_msg(struct netlink_message *msg, int err); void handle_dm_config_msg(struct netlink_message *amsg, struct netlink_message *msg, int err); void handle_dm_start_msg(struct netlink_message *amsg, struct netlink_message *msg, int err); void handle_dm_stop_msg(struct netlink_message *amsg, struct netlink_message *msg, int err); int disable_drop_monitor(); static void(*type_cb[_NET_DM_CMD_MAX])(struct netlink_message *, int err) = { NULL, handle_dm_alert_msg, NULL, NULL, NULL, handle_dm_packet_alert_msg, NULL, handle_dm_config_new_msg, NULL, handle_dm_stats_new_msg, }; static struct nl_sock *nsd; static int nsf; enum { STATE_IDLE = 0, STATE_ACTIVATING, STATE_RECEIVING, STATE_RQST_DEACTIVATE, STATE_RQST_ACTIVATE, STATE_DEACTIVATING, STATE_FAILED, STATE_EXIT, STATE_RQST_ALERT_MODE_SUMMARY, STATE_RQST_ALERT_MODE_PACKET, STATE_ALERT_MODE_SETTING, STATE_RQST_TRUNC_LEN, STATE_TRUNC_LEN_SETTING, STATE_RQST_QUEUE_LEN, STATE_QUEUE_LEN_SETTING, STATE_RQST_CONFIG, STATE_CONFIG_GETTING, STATE_RQST_STATS, STATE_STATS_GETTING, }; static int state = STATE_IDLE; static struct nla_policy net_dm_policy[NET_DM_ATTR_MAX + 1] = { [NET_DM_ATTR_ALERT_MODE] = { .type = NLA_U8 }, [NET_DM_ATTR_PC] = { .type = NLA_U64 }, [NET_DM_ATTR_SYMBOL] = { .type = NLA_STRING }, [NET_DM_ATTR_IN_PORT] = { .type = NLA_NESTED }, [NET_DM_ATTR_TIMESTAMP] = { .type = NLA_U64 }, [NET_DM_ATTR_PROTO] = { .type = NLA_U16 }, [NET_DM_ATTR_PAYLOAD] = { .type = NLA_UNSPEC }, [NET_DM_ATTR_TRUNC_LEN] = { .type = NLA_U32 }, [NET_DM_ATTR_ORIG_LEN] = { .type = NLA_U32 }, [NET_DM_ATTR_QUEUE_LEN] = { .type = NLA_U32 }, [NET_DM_ATTR_STATS] = { .type = NLA_NESTED }, [NET_DM_ATTR_HW_STATS] = { .type = NLA_NESTED }, [NET_DM_ATTR_ORIGIN] = { .type = NLA_U16 }, [NET_DM_ATTR_HW_TRAP_GROUP_NAME] = { .type = NLA_STRING }, [NET_DM_ATTR_HW_TRAP_NAME] = { .type = NLA_STRING }, [NET_DM_ATTR_HW_ENTRIES] = { .type = NLA_NESTED }, [NET_DM_ATTR_HW_ENTRY] = { .type = NLA_NESTED }, [NET_DM_ATTR_HW_TRAP_COUNT] = { .type = NLA_U32 }, }; static struct nla_policy net_dm_port_policy[NET_DM_ATTR_PORT_MAX + 1] = { [NET_DM_ATTR_PORT_NETDEV_IFINDEX] = { .type = NLA_U32 }, [NET_DM_ATTR_PORT_NETDEV_NAME] = { .type = NLA_STRING }, }; static struct nla_policy net_dm_stats_policy[NET_DM_ATTR_STATS_MAX + 1] = { [NET_DM_ATTR_STATS_DROPPED] = { .type = NLA_U64 }, }; int strtobool(const char *str, bool *p_val) { bool val; if (!strcmp(str, "true") || !strcmp(str, "1")) val = true; else if (!strcmp(str, "false") || !strcmp(str, "0")) val = false; else return -EINVAL; *p_val = val; return 0; } void sigint_handler(int signum) { if ((state == STATE_RECEIVING) || (state == STATE_RQST_DEACTIVATE)) { disable_drop_monitor(); state = STATE_DEACTIVATING; } else { printf("Got a sigint while not receiving\n"); } return; } struct nl_sock *setup_netlink_socket() { struct nl_sock *sd; int family; sd = nl_socket_alloc(); genl_connect(sd); family = genl_ctrl_resolve(sd, "NET_DM"); if (family < 0) { printf("Unable to find NET_DM family, dropwatch can't work\n"); goto out_close; } nsf = family; nl_close(sd); nl_socket_free(sd); sd = nl_socket_alloc(); nl_join_groups(sd, NET_DM_GRP_ALERT); nl_connect(sd, NETLINK_GENERIC); return sd; out_close: nl_close(sd); nl_socket_free(sd); return NULL; } struct netlink_message *alloc_netlink_msg(uint32_t type, uint16_t flags, size_t size) { struct netlink_message *msg; static uint32_t seq = 0; msg = (struct netlink_message *)malloc(sizeof(struct netlink_message)); if (!msg) return NULL; msg->refcnt = 1; msg->nlbuf = nlmsg_alloc(); msg->msg = genlmsg_put(msg->nlbuf, 0, seq, nsf, size, flags, type, 1); msg->ack_cb = NULL; msg->seq = seq++; return msg; } void set_ack_cb(struct netlink_message *msg, void (*cb)(struct netlink_message *, struct netlink_message *, int)) { if (msg->ack_cb) return; msg->ack_cb = cb; msg->refcnt++; LIST_INSERT_HEAD(&ack_list_head, msg, ack_list_element); } struct netlink_message *wrap_netlink_msg(struct nlmsghdr *buf) { struct netlink_message *msg; msg = (struct netlink_message *)malloc(sizeof(struct netlink_message)); if (msg) { msg->refcnt = 1; msg->msg = buf; msg->nlbuf = NULL; } return msg; } int free_netlink_msg(struct netlink_message *msg) { int refcnt; msg->refcnt--; refcnt = msg->refcnt; if (!refcnt) { if (msg->nlbuf) nlmsg_free(msg->nlbuf); else free(msg->msg); free(msg); } return refcnt; } int send_netlink_message(struct netlink_message *msg) { return nl_send(nsd, msg->nlbuf); } struct netlink_message *recv_netlink_message(int *err) { static struct nlmsghdr *buf; struct netlink_message *msg; struct genlmsghdr *glm; struct sockaddr_nl nla; int type; int rc; *err = 0; do { rc = nl_recv(nsd, &nla, (unsigned char **)&buf, NULL); if (rc < 0) { switch (errno) { case EINTR: /* * Take a pass through the state loop */ return NULL; break; default: perror("Receive operation failed:"); return NULL; break; } } } while (rc == 0); msg = wrap_netlink_msg(buf); type = ((struct nlmsghdr *)msg->msg)->nlmsg_type; /* * Note the NLMSG_ERROR is overloaded * Its also used to deliver ACKs */ if (type == NLMSG_ERROR) { struct netlink_message *am; struct nlmsgerr *errm = nlmsg_data(msg->msg); LIST_FOREACH(am, &ack_list_head, ack_list_element) { if (am->seq == errm->msg.nlmsg_seq) break; } if (am) { LIST_REMOVE(am, ack_list_element); am->ack_cb(msg, am, errm->error); free_netlink_msg(am); } else { printf("Got an unexpected ack for sequence %d\n", errm->msg.nlmsg_seq); } free_netlink_msg(msg); return NULL; } glm = nlmsg_data(msg->msg); type = glm->cmd; if ((type > NET_DM_CMD_MAX) || (type <= NET_DM_CMD_UNSPEC)) { printf("Received message of unknown type %d\n", type); free_netlink_msg(msg); return NULL; } return msg; } void process_rx_message(void) { struct netlink_message *msg; int err; int type; sigset_t bs; sigemptyset(&bs); sigaddset(&bs, SIGINT); sigprocmask(SIG_UNBLOCK, &bs, NULL); msg = recv_netlink_message(&err); sigprocmask(SIG_BLOCK, &bs, NULL); if (msg) { struct nlmsghdr *nlh = msg->msg; struct genlmsghdr *glh = nlmsg_data(nlh); type = glh->cmd; type_cb[type](msg, err); } return; } void print_nested_hw_entry(struct nlattr *hw_entry) { struct nlattr *attrs[NET_DM_ATTR_MAX + 1]; int err; err = nla_parse_nested(attrs, NET_DM_ATTR_MAX, hw_entry, net_dm_policy); if (err) return; if (!attrs[NET_DM_ATTR_HW_TRAP_NAME] || !attrs[NET_DM_ATTR_HW_TRAP_COUNT]) return; printf("%d drops at %s [hardware]\n", nla_get_u32(attrs[NET_DM_ATTR_HW_TRAP_COUNT]), nla_get_string(attrs[NET_DM_ATTR_HW_TRAP_NAME])); } void print_nested_hw_entries(struct nlattr *hw_entries) { struct nlattr *attr; int rem; nla_for_each_nested(attr, hw_entries, rem) { if (nla_type(attr) != NET_DM_ATTR_HW_ENTRY) continue; print_nested_hw_entry(attr); acount++; if (alimit && (acount == alimit)) { printf("Alert limit reached, deactivating!\n"); state = STATE_RQST_DEACTIVATE; } } } /* * These are the received message handlers */ void handle_dm_alert_msg(struct netlink_message *msg, int err) { int i; struct nlmsghdr *nlh = msg->msg; struct genlmsghdr *glh = nlmsg_data(nlh); struct loc_result res; struct net_dm_alert_msg *alert = nla_data(genlmsg_data(glh)); struct nlattr *attrs[NET_DM_ATTR_MAX + 1]; if (state != STATE_RECEIVING) goto out_free; err = genlmsg_parse(msg->msg, 0, attrs, NET_DM_ATTR_MAX, net_dm_policy); if (err) goto out_free; for (i=0; i < alert->entries; i++) { void *location; memcpy(&location, alert->points[i].pc, sizeof(void *)); if (lookup_symbol(location, &res)) printf ("%d drops at location %p [software]\n", alert->points[i].count, location); else printf ("%d drops at %s+%llx (%p) [software]\n", alert->points[i].count, res.symbol, (unsigned long long)res.offset, location); acount++; if (alimit && (acount == alimit)) { printf("Alert limit reached, deactivating!\n"); state = STATE_RQST_DEACTIVATE; } } if (attrs[NET_DM_ATTR_HW_ENTRIES]) print_nested_hw_entries(attrs[NET_DM_ATTR_HW_ENTRIES]); out_free: free_netlink_msg(msg); } void print_nested_port(struct nlattr *attr, const char *dir) { struct nlattr *attrs[NET_DM_ATTR_PORT_MAX + 1]; int err; err = nla_parse_nested(attrs, NET_DM_ATTR_PORT_MAX, attr, net_dm_port_policy); if (err) return; if (attrs[NET_DM_ATTR_PORT_NETDEV_IFINDEX]) printf("%s port ifindex: %d\n", dir, nla_get_u32(attrs[NET_DM_ATTR_PORT_NETDEV_IFINDEX])); if (attrs[NET_DM_ATTR_PORT_NETDEV_NAME]) printf("%s port name: %s\n", dir, nla_get_string(attrs[NET_DM_ATTR_PORT_NETDEV_NAME])); } void print_packet_origin(struct nlattr *attr) { const char *origin; uint16_t val; val = nla_get_u16(attr); switch (val) { case NET_DM_ORIGIN_SW: origin = "software"; break; case NET_DM_ORIGIN_HW: origin = "hardware"; break; default: origin = "unknown"; break; } printf("origin: %s\n", origin); } void handle_dm_packet_alert_msg(struct netlink_message *msg, int err) { struct nlattr *attrs[NET_DM_ATTR_MAX + 1]; if (state != STATE_RECEIVING) goto out_free; err = genlmsg_parse(msg->msg, 0, attrs, NET_DM_ATTR_MAX, net_dm_policy); if (err) goto out_free; if (attrs[NET_DM_ATTR_PC] && attrs[NET_DM_ATTR_SYMBOL]) printf("drop at: %s (0x%" PRIx64 ")\n", nla_get_string(attrs[NET_DM_ATTR_SYMBOL]), nla_get_u64(attrs[NET_DM_ATTR_PC])); else if (attrs[NET_DM_ATTR_HW_TRAP_GROUP_NAME] && attrs[NET_DM_ATTR_HW_TRAP_NAME]) printf("drop at: %s (%s)\n", nla_get_string(attrs[NET_DM_ATTR_HW_TRAP_NAME]), nla_get_string(attrs[NET_DM_ATTR_HW_TRAP_GROUP_NAME])); if (attrs[NET_DM_ATTR_ORIGIN]) print_packet_origin(attrs[NET_DM_ATTR_ORIGIN]); if (attrs[NET_DM_ATTR_IN_PORT]) print_nested_port(attrs[NET_DM_ATTR_IN_PORT], "input"); if (attrs[NET_DM_ATTR_FLOW_ACTION_COOKIE]) { unsigned char *cookie = nla_data(attrs[NET_DM_ATTR_FLOW_ACTION_COOKIE]); int cookie_len = nla_len(attrs[NET_DM_ATTR_FLOW_ACTION_COOKIE]); int i; printf("cookie: "); for (i = 0; i < cookie_len; i++) printf("%02x", cookie[i]); printf("\n"); } if (attrs[NET_DM_ATTR_TIMESTAMP]) { time_t tv_sec; struct tm *tm; uint64_t ts; char *tstr; ts = nla_get_u64(attrs[NET_DM_ATTR_TIMESTAMP]); tv_sec = ts / 1000000000; tm = localtime(&tv_sec); tstr = asctime(tm); tstr[strlen(tstr) - 1] = 0; printf("timestamp: %s %09" PRId64 " nsec\n", tstr, ts % 1000000000); } if (attrs[NET_DM_ATTR_PROTO]) printf("protocol: 0x%x\n", nla_get_u16(attrs[NET_DM_ATTR_PROTO])); if (attrs[NET_DM_ATTR_PAYLOAD]) printf("length: %u\n", nla_len(attrs[NET_DM_ATTR_PAYLOAD])); if (attrs[NET_DM_ATTR_ORIG_LEN]) printf("original length: %u\n", nla_get_u32(attrs[NET_DM_ATTR_ORIG_LEN])); if (attrs[NET_DM_ATTR_REASON]) printf("drop reason: %s\n", nla_get_string(attrs[NET_DM_ATTR_REASON])); printf("\n"); acount++; if (alimit && (acount == alimit)) { printf("Alert limit reached, deactivating!\n"); state = STATE_RQST_DEACTIVATE; } out_free: free_netlink_msg(msg); } void handle_dm_config_new_msg(struct netlink_message *msg, int err) { struct nlattr *attrs[NET_DM_ATTR_MAX + 1]; if (state != STATE_CONFIG_GETTING) goto out_free; err = genlmsg_parse(msg->msg, 0, attrs, NET_DM_ATTR_MAX, net_dm_policy); if (err) goto out_free; if (!attrs[NET_DM_ATTR_ALERT_MODE] || !attrs[NET_DM_ATTR_TRUNC_LEN] || !attrs[NET_DM_ATTR_QUEUE_LEN]) goto out_free; printf("Alert mode: "); switch (nla_get_u8(attrs[NET_DM_ATTR_ALERT_MODE])) { case NET_DM_ALERT_MODE_SUMMARY: printf("Summary\n"); break; case NET_DM_ALERT_MODE_PACKET: printf("Packet\n"); break; default: printf("Invalid alert mode\n"); break; } printf("Truncation length: %u\n", nla_get_u32(attrs[NET_DM_ATTR_TRUNC_LEN])); printf("Queue length: %u\n", nla_get_u32(attrs[NET_DM_ATTR_QUEUE_LEN])); out_free: state = STATE_IDLE; free_netlink_msg(msg); } void print_nested_stats(struct nlattr *attr) { struct nlattr *attrs[NET_DM_ATTR_STATS_MAX + 1]; int err; err = nla_parse_nested(attrs, NET_DM_ATTR_STATS_MAX, attr, net_dm_stats_policy); if (err) return; if (attrs[NET_DM_ATTR_STATS_DROPPED]) printf("Tail dropped: %" PRIu64 "\n", nla_get_u64(attrs[NET_DM_ATTR_STATS_DROPPED])); } void handle_dm_stats_new_msg(struct netlink_message *msg, int err) { struct nlattr *attrs[NET_DM_ATTR_MAX + 1]; if (state != STATE_STATS_GETTING) goto out_free; err = genlmsg_parse(msg->msg, 0, attrs, NET_DM_ATTR_MAX, net_dm_policy); if (err) goto out_free; if (attrs[NET_DM_ATTR_STATS]) { printf("Software statistics:\n"); print_nested_stats(attrs[NET_DM_ATTR_STATS]); } if (attrs[NET_DM_ATTR_HW_STATS]) { printf("Hardware statistics:\n"); print_nested_stats(attrs[NET_DM_ATTR_HW_STATS]); } out_free: state = STATE_IDLE; free_netlink_msg(msg); } void handle_dm_config_msg(struct netlink_message *amsg, struct netlink_message *msg, int err) { if (err != 0) { char *erm = strerror(-err); printf("Failed config request, error: %s\n", erm); state = STATE_FAILED; return; } switch (state) { case STATE_ALERT_MODE_SETTING: printf("Alert mode successfully set\n"); state = STATE_IDLE; break; case STATE_TRUNC_LEN_SETTING: printf("Truncation length successfully set\n"); state = STATE_IDLE; break; case STATE_QUEUE_LEN_SETTING: printf("Queue length successfully set\n"); state = STATE_IDLE; break; default: printf("Received acknowledgement for non-solicited config request\n"); state = STATE_FAILED; } } void handle_dm_start_msg(struct netlink_message *amsg, struct netlink_message *msg, int err) { if (err != 0) { char *erm = strerror(err*-1); printf("Failed activation request, error: %s\n", erm); state = STATE_FAILED; goto out; } if (state == STATE_ACTIVATING) { struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = sigint_handler; act.sa_flags = SA_RESETHAND; printf("Kernel monitoring activated.\n"); printf("Issue Ctrl-C to stop monitoring\n"); sigaction(SIGINT, &act, NULL); state = STATE_RECEIVING; } else { printf("Odd, the kernel told us that it activated and we didn't ask\n"); state = STATE_FAILED; } out: return; } void handle_dm_stop_msg(struct netlink_message *amsg, struct netlink_message *msg, int err) { char *erm; if ((err == 0) || (err == -EAGAIN)) { printf("Got a stop message\n"); state = STATE_IDLE; } else { erm = strerror(err*-1); printf("Stop request failed, error: %s\n", erm); } } int enable_drop_monitor() { struct netlink_message *msg; msg = alloc_netlink_msg(NET_DM_CMD_START, NLM_F_REQUEST|NLM_F_ACK, 0); if (monitor_sw && nla_put_flag(msg->nlbuf, NET_DM_ATTR_SW_DROPS)) goto nla_put_failure; if (monitor_hw && nla_put_flag(msg->nlbuf, NET_DM_ATTR_HW_DROPS)) goto nla_put_failure; set_ack_cb(msg, handle_dm_start_msg); return send_netlink_message(msg); nla_put_failure: free_netlink_msg(msg); return -EMSGSIZE; } int disable_drop_monitor() { struct netlink_message *msg; msg = alloc_netlink_msg(NET_DM_CMD_STOP, NLM_F_REQUEST|NLM_F_ACK, 0); if (monitor_sw && nla_put_flag(msg->nlbuf, NET_DM_ATTR_SW_DROPS)) goto nla_put_failure; if (monitor_hw && nla_put_flag(msg->nlbuf, NET_DM_ATTR_HW_DROPS)) goto nla_put_failure; set_ack_cb(msg, handle_dm_stop_msg); return send_netlink_message(msg); nla_put_failure: free_netlink_msg(msg); return -EMSGSIZE; } int set_alert_mode() { enum net_dm_alert_mode alert_mode; struct netlink_message *msg; switch (state) { case STATE_RQST_ALERT_MODE_SUMMARY: alert_mode = NET_DM_ALERT_MODE_SUMMARY; break; case STATE_RQST_ALERT_MODE_PACKET: alert_mode = NET_DM_ALERT_MODE_PACKET; break; default: return -EINVAL; } msg = alloc_netlink_msg(NET_DM_CMD_CONFIG, NLM_F_REQUEST|NLM_F_ACK, 0); if (!msg) return -ENOMEM; if (nla_put_u8(msg->nlbuf, NET_DM_ATTR_ALERT_MODE, alert_mode)) goto nla_put_failure; set_ack_cb(msg, handle_dm_config_msg); return send_netlink_message(msg); nla_put_failure: free_netlink_msg(msg); return -EMSGSIZE; } int set_trunc_len() { struct netlink_message *msg; msg = alloc_netlink_msg(NET_DM_CMD_CONFIG, NLM_F_REQUEST|NLM_F_ACK, 0); if (!msg) return -ENOMEM; if (nla_put_u32(msg->nlbuf, NET_DM_ATTR_TRUNC_LEN, trunc_len)) goto nla_put_failure; set_ack_cb(msg, handle_dm_config_msg); return send_netlink_message(msg); nla_put_failure: free_netlink_msg(msg); return -EMSGSIZE; } int set_queue_len() { struct netlink_message *msg; msg = alloc_netlink_msg(NET_DM_CMD_CONFIG, NLM_F_REQUEST|NLM_F_ACK, 0); if (!msg) return -ENOMEM; if (nla_put_u32(msg->nlbuf, NET_DM_ATTR_QUEUE_LEN, queue_len)) goto nla_put_failure; set_ack_cb(msg, handle_dm_config_msg); return send_netlink_message(msg); nla_put_failure: free_netlink_msg(msg); return -EMSGSIZE; } int get_config() { struct netlink_message *msg; msg = alloc_netlink_msg(NET_DM_CMD_CONFIG_GET, NLM_F_REQUEST, 0); if (!msg) return -ENOMEM; return send_netlink_message(msg); } int get_stats() { struct netlink_message *msg; msg = alloc_netlink_msg(NET_DM_CMD_STATS_GET, NLM_F_REQUEST, 0); if (!msg) return -ENOMEM; return send_netlink_message(msg); } void display_help() { printf("Command Syntax:\n"); printf("exit\t\t\t\t - Quit dropwatch\n"); printf("help\t\t\t\t - Display this message\n"); printf("set:\n"); printf("\talertlimit \t - capture only this many alert packets\n"); printf("\talertmode \t - set mode to \"summary\" or \"packet\"\n"); printf("\ttrunc \t\t - truncate packets to this length. "); printf("Only applicable when \"alertmode\" is set to \"packet\"\n"); printf("\tqueue \t\t - queue up to this many packets in the kernel. "); printf("Only applicable when \"alertmode\" is set to \"packet\"\n"); printf("\tsw \t - monitor software drops\n"); printf("\thw \t - monitor hardware drops\n"); printf("start\t\t\t\t - start capture\n"); printf("stop\t\t\t\t - stop capture\n"); printf("show\t\t\t\t - show existing configuration\n"); printf("stats\t\t\t\t - show statistics\n"); } void enter_command_line_mode() { char *input; int err; do { input = readline("dropwatch> "); if (input == NULL) { /* Someone closed stdin on us */ printf("Terminating dropwatch...\n"); state = STATE_EXIT; break; } if (!strcmp(input,"start")) { state = STATE_RQST_ACTIVATE; break; } if (!strcmp(input, "stop")) { state = STATE_RQST_DEACTIVATE; break; } if (!strcmp(input, "exit")) { state = STATE_EXIT; break; } if (!strcmp (input, "help")) { display_help(); goto next_input; } if (!strncmp(input, "set", 3)) { char *ninput = input+4; if (!strncmp(ninput, "alertlimit", 10)) { alimit = strtoul(ninput+10, NULL, 10); printf("setting alert capture limit to %lu\n", alimit); goto next_input; } else if (!strncmp(ninput, "alertmode", 9)) { ninput = ninput + 10; if (!strncmp(ninput, "summary", 7)) { state = STATE_RQST_ALERT_MODE_SUMMARY; break; } else if (!strncmp(ninput, "packet", 6)) { state = STATE_RQST_ALERT_MODE_PACKET; break; } } else if (!strncmp(ninput, "trunc", 5)) { trunc_len = strtoul(ninput + 6, NULL, 10); state = STATE_RQST_TRUNC_LEN; break; } else if (!strncmp(ninput, "queue", 5)) { queue_len = strtoul(ninput + 6, NULL, 10); state = STATE_RQST_QUEUE_LEN; break; } else if (!strncmp(ninput, "sw", 2)) { err = strtobool(ninput + 3, &monitor_sw); if (err) { printf("invalid boolean value\n"); state = STATE_FAILED; break; } printf("setting software drops monitoring to %d\n", monitor_sw); goto next_input; } else if (!strncmp(ninput, "hw", 2)) { err = strtobool(ninput + 3, &monitor_hw); if (err) { printf("invalid boolean value\n"); state = STATE_FAILED; break; } printf("setting hardware drops monitoring to %d\n", monitor_hw); goto next_input; } } if (!strncmp(input, "show", 4)) { state = STATE_RQST_CONFIG; break; } if (!strncmp(input, "stats", 5)) { state = STATE_RQST_STATS; break; } next_input: free(input); } while(1); free(input); } void enter_state_loop(void) { int should_rx = 0; while (1) { switch(state) { case STATE_IDLE: should_rx = 0; enter_command_line_mode(); break; case STATE_RQST_ACTIVATE: printf("Enabling monitoring...\n"); if (enable_drop_monitor() < 0) { perror("Unable to send activation msg:"); state = STATE_FAILED; } else { state = STATE_ACTIVATING; should_rx = 1; } break; case STATE_ACTIVATING: printf("Waiting for activation ack....\n"); break; case STATE_RECEIVING: break; case STATE_RQST_DEACTIVATE: printf("Deactivation requested, turning off monitoring\n"); if (disable_drop_monitor() < 0) { perror("Unable to send deactivation msg:"); state = STATE_FAILED; } else state = STATE_DEACTIVATING; should_rx = 1; break; case STATE_DEACTIVATING: printf("Waiting for deactivation ack...\n"); break; case STATE_EXIT: case STATE_FAILED: should_rx = 0; return; case STATE_RQST_ALERT_MODE_SUMMARY: case STATE_RQST_ALERT_MODE_PACKET: printf("Setting alert mode\n"); if (set_alert_mode() < 0) { perror("Failed to set alert mode"); state = STATE_FAILED; } else { state = STATE_ALERT_MODE_SETTING; should_rx = 1; } break; case STATE_ALERT_MODE_SETTING: printf("Waiting for alert mode setting ack...\n"); break; case STATE_RQST_TRUNC_LEN: printf("Setting truncation length to %lu\n", trunc_len); if (set_trunc_len() < 0) { perror("Failed to set truncation length"); state = STATE_FAILED; } else { state = STATE_TRUNC_LEN_SETTING; should_rx = 1; } break; case STATE_TRUNC_LEN_SETTING: printf("Waiting for truncation length setting ack...\n"); break; case STATE_RQST_QUEUE_LEN: printf("Setting queue length to %lu\n", queue_len); if (set_queue_len() < 0) { perror("Failed to set queue length"); state = STATE_FAILED; } else { state = STATE_QUEUE_LEN_SETTING; should_rx = 1; } break; case STATE_QUEUE_LEN_SETTING: printf("Waiting for queue length setting ack...\n"); break; case STATE_RQST_CONFIG: printf("Getting existing configuration\n"); if (get_config() < 0) { perror("Failed to get existing configuration"); state = STATE_FAILED; } else { state = STATE_CONFIG_GETTING; should_rx = 1; } break; case STATE_CONFIG_GETTING: printf("Waiting for existing configuration query response\n"); break; case STATE_RQST_STATS: printf("Getting statistics\n"); if (get_stats() < 0) { perror("Failed to get statistics"); state = STATE_FAILED; } else { state = STATE_STATS_GETTING; should_rx = 1; } break; case STATE_STATS_GETTING: printf("Waiting for statistics query response\n"); break; default: printf("Unknown state received! exiting!\n"); state = STATE_FAILED; should_rx = 0; break; } /* * After we process our state loop, look to see if we have messages */ if (should_rx) process_rx_message(); } } struct option options[] = { {"lmethod", 1, 0, 'l'}, {0, 0, 0, 0} }; void usage() { printf("dropwatch [-l|--lmethod ]\n"); } int main (int argc, char **argv) { int c, optind; lookup_init_method_t meth = METHOD_NULL; /* * parse the options */ for(;;) { c = getopt_long(argc, argv, "l:", options, &optind); /* are we done parsing ? */ if (c == -1) break; switch(c) { case '?': usage(); exit(1); /* NOTREACHED */ case 'l': /* select the lookup method we want to use */ if (!strncmp(optarg, "list", 4)) { printf("Available lookup methods:\n"); printf("kas - use /proc/kallsyms\n"); exit(0); } else if (!strncmp(optarg, "kas", 3)) { meth = METHOD_KALLSYMS; } else { printf("Unknown lookup method %s\n", optarg); exit(1); } break; default: printf("Unknown option\n"); usage(); exit(1); /* NOTREACHED */ } } /* * open up the netlink socket that we need to talk to our dropwatch socket */ nsd = setup_netlink_socket(); if (nsd == NULL) { printf("Cleaning up on socket creation error\n"); goto out; } /* * Initialize our lookup library */ init_lookup(meth); enter_state_loop(); printf("Shutting down ...\n"); nl_close(nsd); exit(0); out: exit(1); } dropwatch-1.5.4/src/net_dropmon.h000066400000000000000000000053641422725253600170200ustar00rootroot00000000000000/* * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __NET_DROPMON_H #define __NET_DROPMON_H #include #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif struct net_dm_drop_point { __u8 pc[8]; __u32 count; }; #define NET_DM_CFG_VERSION 0 #define NET_DM_CFG_ALERT_COUNT 1 #define NET_DM_CFG_ALERT_DELAY 2 #define NET_DM_CFG_MAX 3 struct net_dm_config_entry { __u32 type; __u64 data __attribute__((aligned(8))); }; struct net_dm_config_msg { __u32 entries; struct net_dm_config_entry options[0]; }; struct net_dm_alert_msg { __u32 entries; struct net_dm_drop_point points[0]; }; struct net_dm_user_msg { union { struct net_dm_config_msg user; struct net_dm_alert_msg alert; } u; }; /* These are the netlink message types for this protocol */ enum { NET_DM_CMD_UNSPEC = 0, NET_DM_CMD_ALERT, NET_DM_CMD_CONFIG, NET_DM_CMD_START, NET_DM_CMD_STOP, NET_DM_CMD_PACKET_ALERT, NET_DM_CMD_CONFIG_GET, NET_DM_CMD_CONFIG_NEW, NET_DM_CMD_STATS_GET, NET_DM_CMD_STATS_NEW, _NET_DM_CMD_MAX, }; #define NET_DM_CMD_MAX (_NET_DM_CMD_MAX - 1) /* * Our group identifiers */ #define NET_DM_GRP_ALERT 1 enum net_dm_attr { NET_DM_ATTR_UNSPEC, NET_DM_ATTR_ALERT_MODE, /* u8 */ NET_DM_ATTR_PC, /* u64 */ NET_DM_ATTR_SYMBOL, /* string */ NET_DM_ATTR_IN_PORT, /* nested */ NET_DM_ATTR_TIMESTAMP, /* u64 */ NET_DM_ATTR_PROTO, /* u16 */ NET_DM_ATTR_PAYLOAD, /* binary */ NET_DM_ATTR_PAD, NET_DM_ATTR_TRUNC_LEN, /* u32 */ NET_DM_ATTR_ORIG_LEN, /* u32 */ NET_DM_ATTR_QUEUE_LEN, /* u32 */ NET_DM_ATTR_STATS, /* nested */ NET_DM_ATTR_HW_STATS, /* nested */ NET_DM_ATTR_ORIGIN, /* u16 */ NET_DM_ATTR_HW_TRAP_GROUP_NAME, /* string */ NET_DM_ATTR_HW_TRAP_NAME, /* string */ NET_DM_ATTR_HW_ENTRIES, /* nested */ NET_DM_ATTR_HW_ENTRY, /* nested */ NET_DM_ATTR_HW_TRAP_COUNT, /* u32 */ NET_DM_ATTR_SW_DROPS, /* flag */ NET_DM_ATTR_HW_DROPS, /* flag */ NET_DM_ATTR_FLOW_ACTION_COOKIE, /* binary */ NET_DM_ATTR_REASON, /* string */ __NET_DM_ATTR_MAX, NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1 }; /** * enum net_dm_alert_mode - Alert mode. * @NET_DM_ALERT_MODE_SUMMARY: A summary of recent drops is sent to user space. * @NET_DM_ALERT_MODE_PACKET: Each dropped packet is sent to user space along * with metadata. */ enum net_dm_alert_mode { NET_DM_ALERT_MODE_SUMMARY, NET_DM_ALERT_MODE_PACKET, }; enum { NET_DM_ATTR_PORT_NETDEV_IFINDEX, /* u32 */ NET_DM_ATTR_PORT_NETDEV_NAME, /* string */ __NET_DM_ATTR_PORT_MAX, NET_DM_ATTR_PORT_MAX = __NET_DM_ATTR_PORT_MAX - 1 }; enum { NET_DM_ATTR_STATS_DROPPED, /* u64 */ __NET_DM_ATTR_STATS_MAX, NET_DM_ATTR_STATS_MAX = __NET_DM_ATTR_STATS_MAX - 1 }; enum net_dm_origin { NET_DM_ORIGIN_SW, NET_DM_ORIGIN_HW, }; #endif dropwatch-1.5.4/tests/000077500000000000000000000000001422725253600146665ustar00rootroot00000000000000dropwatch-1.5.4/tests/Makefile.am000066400000000000000000000000701422725253600167170ustar00rootroot00000000000000check_SCRIPTS = rundropwatch.sh TESTS = rundropwatch.sh dropwatch-1.5.4/tests/rundropwatch.sh000077500000000000000000000004751422725253600177530ustar00rootroot00000000000000#!/bin/sh abort_dropwatch() { sleep 5 killall -SIGINT dropwatch } abort_dropwatch & echo -e "set alertlimit 1\nstart\nstop\nexit" | ../src/dropwatch -l kas if [ $? -ne 0 ] then grep -q NET_DM ./rundropwatch.sh.log if [ $? -eq 0 ] then # This platform doesn't support NET_DM, skip this test exit 77 fi fi