From: Mark Huang Date: Wed, 22 Feb 2006 21:29:08 +0000 (+0000) Subject: This commit was generated by cvs2svn to compensate for changes in r1650, X-Git-Tag: iproute-2.6.16-1~11 X-Git-Url: http://git.onelab.eu/?p=iproute2.git;a=commitdiff_plain;h=4f7d3057911ebc5df78113896cb02e1b4ffbfabe This commit was generated by cvs2svn to compensate for changes in r1650, which included commits to RCS files with non-trunk default branches. --- diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..2b7b643 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy 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. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..53bd530 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,332 @@ +2005-03-14 Stephen Hemminger + + * cleanup batch mode, allow continuation, comments etc. + * recode reuse of netlink socket + +2005-03-14 Boian Bonev + + * enhancement to batch mode. + it does not exit on error, just report it + tc reuses the already open netlink socket for subsequent command(s) + +2005-03-14 Thomas Graf + + * ip link command + print NO-CARRIER flag if there is no carrier and the link is up. + +2005-03-14 Patrick McHardy + + * bug: Use USER_HZ where necessary + +2005-03-10 Jamal Hadi Salim + + * Fix bug with register_target + +2005-03-10 Stephen Hemminger + + * fix pkt_cls.h to have tc_u32_mark + * update include files to be stripped versions of 2.6.11 + * add documentation about netem distributions [from nistnet] + * turn off nup in document make [from FC3] + * don't build with extra debug info (-g) [from FC3] + +2005-03-10 Nix + + * make man3 directory + +2005-03-10 Pasi + + * add ESP-in-UDP encapsulation + +2005-03-10 Thomas Graf + * [NETEM] Fix off by one + * update local header file copies + * [NEIGH] print number of probes done so far (statistics mode only) + +2005-03-10 Herbert Xu + * Trivial typo in ip help + +2005-02-09 Stephen Hemminger + + * netem distribution data reorganization + +2005-02-09 Roland Dreier + + * ip over infiniband address display + +2005-02-09 Jim Gifford + + * make install fix for ip/ + +2005-02-07 Mads Martin Joergensen + + * Don't mix address families when flushing + +2005-02-07 Stephen Hemminger + + * Validate classid is not too large to cause loss of bits. + +2005-02-07 Jean-Marc Ranger + + * need to call getline() with null for first usage + * don't overwrite const arg + +2005-02-07 Stephen Hemminger + + * Add experimental distribution + +2005-01-18 Yun Mao + + * typo in ss + +2005-01-18 Thomas Graf + + * tc pedit/action cleanups + * add addraw_l + * rtattr_parse cleanups + +2005-01-17 Jamal Hadi Salim + + * typo in m_mirred + * add support for pedit + +2005-01-13 Jim Gifford + + * Fix allocation size error in nomal and paretonormal generation + programs. + +2005-01-12 Masahide Nakamura + + * ipmonitor shows IPv6 prefix list notification + * update to iproute2 xfrm for ipv6 + +2005-01-12 Stephen Hemminger + + * Fix compile warnings when building 64bit system since + u64 is unsigned long, but format is %llu + +2005-01-12 "Catalin(ux aka Dino) BOIE" + + * Add the possibility to use fwmark in u32 filters + +2005-01-12 Andi Kleen + + * Add netlink manual page + +2004-10-20 Stephen Hemminger + + * Add warning about "ip route nat" no longer supported + +2005-01-12 Thomas Graf + + * Tc testsuite + +2005-01-12 Jamal Hadi Salim + + * Add iptables tc support. This meant borrowing headers + from iptables *ugh* + +2004-12-08 Jamal Hadi Salim + + * Add mirror and redirect actions + +2004-10-20 Stephen Hemminger + + * Don't include since then we get dependant on + kernel headers on host machine + * Minor fix for building on old machine without IPPROTO_SCTP + +2004-10-19 Harald Welte + + * Replace rtstat (and ctstat) with new lnstat + +2004-10-19 Mads Martin Joergensen + + * Ip is using the wrong structure in ipaddress.c when showing stats + * Make sure no buffer overflow in nstat + +2004-10-19 Michal + + * fix scaling in print_rates (for bits) + +2004-09-28 Stephen Hemminger + + * fix build problems with arpd and pthread + * add pkt_sched.h + +2004-09-28 Mike Frysinger + + * make man8 directory + * install ifcfg and rtpr scripts + +2004-09-28 Andreas Haumer + + * make install symlink fix. + +2004-09-28 Masahide Nakamura + + * ICMP/ICMPv6's type and code in IPsec selector. + * fixes `ip xfrm`'s algorithm key when using hexadecimal + * support 'ip xfrm' protocol types + * flush message types for XFRM's policy/state + + +2004-09-01 Stephen Hemminger + + * Fix ip command to not crash when interface name is too long. + always use strncpy(.., IFNAMSIZ) + +2004-08-31 Stephen Hemminger + + * Add gact documentation from jamal + * Chang more arguments to rtnetlink API const + * Drop dead queuing disciplines + * Handle qdisc without xstats in core rather than + putting stub's everywhere + * Add requeue to tc_stats and handle new/old ABI issues + +2004-08-30 Stephen Hemminger + + * Make clean and install changes for man pages + * Patch from jamal to support gact + * Add support for loading distributions to netem + + +2004-08-23 Stephen Hemminger + + * Update from jamal for all the parts that got broken in the + last classification patch. + * Hfsc/sc patch from patrick + +2004-08-13 Stephen Hemminger + + * Add jamal's tc extensions for classification + * Get rid of old Patches/ directory for tcp_diag module + * Make get_rate table based. + +2004-08-11 Stephen Hemminger + + * Add xfrm message formatting from + Masahide Nakamura + +2004-08-09 Stephen Hemminger + + * Fix netem scheduler to handle case where psched us != real us + + * Remove configuration for everything that can depend on + extracted kernel headers + * Add kernel headers required to include/linux + +2004-08-04 Stephen Hemminger + + * Get rid of old tcp_diag module, it is part of kernel. + + * Add some kernel include files back (netlink, tcp_diag, pkt_sched) + +2004-07-30 Stephen Hemminger + + * Make ip xfrm stuff config option since it doesn't exist on 2.4 + + * HFSC doesn't exist on older 2.4 kernels so make it configurable + + * HTB API changed and won't build with mismatched version. + Rather than sticking user with a build error, just don't + build it in on mismatch. + + * Change configure script to make sure netem is the correct + version. I changed the structure def. a couple of times before + settling on the final API + +2004-07-16 Stephen Hemminger + + * Add htb mpu support + http://luxik.cdi.cz/~devik/qos/htb/v3/htb_tc_overhead.diff + * Three small xfrm updates + +2004-07-07 Stephen Hemminger + + * Fix if_ether.h to fix arpd build + * Add hfsc scheduler support + * Add ip xfrm support + * Add add jitter (instead of rate) to netem scheduler + +2004-07-02 Stephen Hemminger + + * use compile to test for ATM libraries + * put TC layered scheduler hooks in /usr/lib/tc as shared lib + before it looked in standard search path (/lib;/usr/lib;...) + which seems out of place. + * build netem as shared library (more for testing/example) + * build ATM as shared library since libatm may be on build + machine but not on deployment machine + * fix make install to not install SCCS directories + +2004-07-01 Stephen Hemminger + + * add more link options to ip command (from Mark Smith + * add rate and duplicate arguments to tc command + * add -iec flag for tc printout + * rename delay scheduler to netem + +2004-06-25 Stephen Hemminger + + * Add loss parameter to delay + * Rename delay qdisc to netsim + * Add autoconfiguration by building a Config file + and using it. + +2004-06-09 Stephen Hemminger + + * Report rates in K=1000 (requested by several people) + * Add GNU long style options + * For HTB use get_hz to pick up value of system HZ at runtime + * Delete unused funcs. + +2004-06-08 Stephen Hemminger + + * Cleanup ss + - use const char and local functions where possible + * Add man pages from SuSe + * SuSE patches + - path to db4.1 + - don't hardcode path to /tmp in ifstat + Alternat fix: was to use TMPDIR + - handle non-root user calling ip route flush going into + an infinite loop. + Alternate fix: was to timeout if route table doesn't empty. + * Try and get rid of dependency on kernel include files + Get rid of having private glibc-include headers + +2004-06-07 Stephen Hemminger + + * Import patches that make sense from Fedora Core 2 + - iproute2-2.4.7-hex + print fwmark in hex + - iproute2-2.4.7-netlink + handle getting right netlink mesg back + - iproute2-2.4.7-htb3-tc + add HTB scheduler + - iproute2-2.4.7-default + add entry default to rttable + +2004-06-04 Stephen Hemminger + + * Add support for vegas info to ss + +2004-06-02 Stephen Hemminger + + * Use const char in utility routines where appropriate + * Rearrange include files so can build with standard headers + * For "tc qdisc ls" see the default queuing discpline "pfifo_fast" + and understand it + * Get rid of private defintions of network headers which existed + only to handle old glibc + +2004-04-15 Stephen Hemminger + + * Add the delay (network simulation scheduler) + +2004-04-15 Stephen Hemminger + + * Starting point baseline based on iproute2-2.4.7-ss020116-try + diff --git a/Config b/Config new file mode 100644 index 0000000..290e783 --- /dev/null +++ b/Config @@ -0,0 +1 @@ +# Generated config based on /tmp/iproute2-2.6.11/include diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d11462 --- /dev/null +++ b/Makefile @@ -0,0 +1,68 @@ +DESTDIR= +SBINDIR=/usr/sbin +CONFDIR=/etc/iproute2 +DOCDIR=/usr/share/doc/iproute2 +MANDIR=/usr/share/man +KERNEL_INCLUDE=/usr/include + +# Path to db_185.h include +DBM_INCLUDE:=/usr/include + +DEFINES= -DRESOLVE_HOSTNAMES + +#options if you have a bind>=4.9.4 libresolv (or, maybe, glibc) +LDLIBS=-lresolv +ADDLIB= + +#options for decnet +ADDLIB+=dnet_ntop.o dnet_pton.o + +#options for ipx +ADDLIB+=ipx_ntop.o ipx_pton.o + +CC = gcc +HOSTCC = gcc +CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall +CFLAGS = $(CCOPTS) -I../include $(DEFINES) + +LDLIBS += -L../lib -lnetlink -lutil + +SUBDIRS=lib ip tc misc netem + +LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a + +all: Config + @for i in $(SUBDIRS); \ + do $(MAKE) $(MFLAGS) -C $$i; done + +Config: + ./configure $(KERNEL_INCLUDE) + +install: all + install -m 0755 -d $(DESTDIR)$(SBINDIR) + install -m 0755 -d $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples/diffserv + install -m 0644 README.iproute2+tc $(shell find examples -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples + install -m 0644 $(shell find examples/diffserv -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples/diffserv + @for i in $(SUBDIRS) doc; do $(MAKE) -C $$i install; done + install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(MANDIR)/man8 + install -m 0644 $(shell find man/man8 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-bfifo.8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-pfifo.8 + install -m 0755 -d $(DESTDIR)$(MANDIR)/man3 + install -m 0644 $(shell find man/man3 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man3 + +clean: + @for i in $(SUBDIRS) doc; \ + do $(MAKE) $(MFLAGS) -C $$i clean; done + +clobber: clean + rm -f Config + +distclean: clean clobber + +.EXPORT_ALL_VARIABLES: diff --git a/Makefile.kernel b/Makefile.kernel new file mode 100644 index 0000000..abd5aab --- /dev/null +++ b/Makefile.kernel @@ -0,0 +1,67 @@ +DESTDIR= +SBINDIR=/usr/sbin +CONFDIR=/etc/iproute2 +DOCDIR=/usr/share/doc/iproute2 +MANDIR=/usr/share/man + +# Path to db_185.h include +DBM_INCLUDE:=/usr/include + +DEFINES= -DRESOLVE_HOSTNAMES + +#options if you have a bind>=4.9.4 libresolv (or, maybe, glibc) +LDLIBS=-lresolv +ADDLIB= + +#options for decnet +ADDLIB+=dnet_ntop.o dnet_pton.o + +#options for ipx +ADDLIB+=ipx_ntop.o ipx_pton.o + +CC = gcc +HOSTCC = gcc +CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall +CFLAGS = $(CCOPTS) -I../include $(DEFINES) + +LDLIBS += -L../lib -lnetlink -lutil + +SUBDIRS=lib ip tc misc netem + +LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a + +all: Config + @for i in $(SUBDIRS); \ + do $(MAKE) $(MFLAGS) -C $$i; done + +Config: + ./configure $(KERNEL_INCLUDE) + +install: all + install -m 0755 -d $(DESTDIR)$(SBINDIR) + install -m 0755 -d $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples/diffserv + install -m 0644 README.iproute2+tc $(shell find examples -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples + install -m 0644 $(shell find examples/diffserv -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples/diffserv + @for i in $(SUBDIRS) doc; do $(MAKE) -C $$i install; done + install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(MANDIR)/man8 + install -m 0644 $(shell find man/man8 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-bfifo.8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-pfifo.8 + install -m 0755 -d $(DESTDIR)$(MANDIR)/man3 + install -m 0644 $(shell find man/man3 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man3 + +clean: + @for i in $(SUBDIRS) doc; \ + do $(MAKE) $(MFLAGS) -C $$i clean; done + +clobber: clean + rm -f Config + +distclean: clean clobber + +.EXPORT_ALL_VARIABLES: diff --git a/README b/README new file mode 100644 index 0000000..d86f605 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +Primary site is: + http://developer.osdl.org/dev/iproute2 + +Original FTP site is: + ftp://ftp.inr.ac.ru/ip-routing/ + +How to compile this. +-------------------- +1. Look at start of Makefile and set correct values for: + +KERNEL_INCLUDE should point to correct linux kernel include directory. +Default (/usr/src/linux/include) is right as rule. + +DBM_INCLUDE points to the directory with db_185.h which +is the include file used by arpd to get to the old format Berkely +database routines. Often this is in the db-devel package. + +2. make + +The makefile will automatically build a file Config which +contains whether or not ATM is available, etc. + +3. To make documentation, cd to doc/ directory , then + look at start of Makefile and set correct values for + PAGESIZE=a4 , ie: a4 , letter ... (string) + PAGESPERPAGE=2 , ie: 1 , 2 ... (numeric) + and make there. It assumes, that latex, dvips and psnup + are in your path. + +Stephen Hemminger +shemminger@osdl.org + +Alexey Kuznetsov +kuznet@ms2.inr.ac.ru diff --git a/README.decnet b/README.decnet new file mode 100644 index 0000000..4d7453a --- /dev/null +++ b/README.decnet @@ -0,0 +1,41 @@ + +Here are a few quick points about DECnet support... + + o No name resolution is available as yet, all addresses must be + entered numerically. + + o The neighbour cache may well list every entry as having the address + 0.170. This is due to a problem that I need to sort out kernel side. + It is harmless (but don't try and use neigh add yet) just look in + /proc/net/decnet_neigh to see the real addresses for now. + + o The rtnetlink support in the kernel is rather exprimental, expect a + few odd things to happen for the next few DECnet kernel releases. + + o Whilst you can use ip addr add to add more than one DECnet address to an + interface, don't expect addresses which are not the same as the + kernels node address to work properly. i.e. You will break the DECnet + protocol if you do add anything other than the automatically generated + interface addresses to ethernet cards. This option is there for future + link layer support, where the device will have to be configed for + DECnet explicitly. + + o The DECnet support is currently self contained. You do not need the + libdnet library to use it. In fact until I've sent the dnet_pton and + dnet_ntop functions to Patrick to add, you can't use libdnet. + + o If you are not using the very latest 2.3.xx series kernels, don't + try and list DECnet routes if you've got IPv6 compiled into the + kernel. It will oops. + + o My main reason for writing the DECnet support for iproute2 was to + check out the DECnet routing code, so the route get and + route show cache commands are likely to be the most debugged out of + all of them. + + o If you find bugs in the DECnet support, please send them to me in the + first instance, and then I'll send Alexey a patch to fix it. IPv4/6 + bugs should be sent to Alexey as before. + +Steve Whitehouse + diff --git a/README.distribution b/README.distribution new file mode 100644 index 0000000..fe78fb4 --- /dev/null +++ b/README.distribution @@ -0,0 +1,95 @@ +I. About the distribution tables + +The table used for "synthesizing" the distribution is essentially a scaled, +translated, inverse to the cumulative distribution function. + +Here's how to think about it: Let F() be the cumulative distribution +function for a probability distribution X. We'll assume we've scaled +things so that X has mean 0 and standard deviation 1, though that's not +so important here. Then: + + F(x) = P(X <= x) = \int_{-inf}^x f + +where f is the probability density function. + +F is monotonically increasing, so has an inverse function G, with range +0 to 1. Here, G(t) = the x such that P(X <= x) = t. (In general, G may +have singularities if X has point masses, i.e., points x such that +P(X = x) > 0.) + +Now we create a tabular representation of G as follows: Choose some table +size N, and for the ith entry, put in G(i/N). Let's call this table T. + +The claim now is, I can create a (discrete) random variable Y whose +distribution has the same approximate "shape" as X, simply by letting +Y = T(U), where U is a discrete uniform random variable with range 1 to N. +To see this, it's enough to show that Y's cumulative distribution function, +(let's call it H), is a discrete approximation to F. But + + H(x) = P(Y <= x) + = (# of entries in T <= x) / N -- as Y chosen uniformly from T + = i/N, where i is the largest integer such that G(i/N) <= x + = i/N, where i is the largest integer such that i/N <= F(x) + -- since G and F are inverse functions (and F is + increasing) + = floor(N*F(x))/N + +as desired. + +II. How to create distribution tables (in theory) + +How can we create this table in practice? In some cases, F may have a +simple expression which allows evaluating its inverse directly. The +pareto distribution is one example of this. In other cases, and +especially for matching an experimentally observed distribution, it's +easiest simply to create a table for F and "invert" it. Here, we give +a concrete example, namely how the new "experimental" distribution was +created. + +1. Collect enough data points to characterize the distribution. Here, I +collected 25,000 "ping" roundtrip times to a "distant" point (time.nist.gov). +That's far more data than is really necessary, but it was fairly painless to +collect it, so... + +2. Normalize the data so that it has mean 0 and standard deviation 1. + +3. Determine the cumulative distribution. The code I wrote creates a table +covering the range -10 to +10, with granularity .00005. Obviously, this +is absurdly over-precise, but since it's a one-time only computation, I +figured it hardly mattered. + +4. Invert the table: for each table entry F(x) = y, make the y*TABLESIZE +(here, 4096) entry be x*TABLEFACTOR (here, 8192). This creates a table +for the ("normalized") inverse of size TABLESIZE, covering its domain 0 +to 1 with granularity 1/TABLESIZE. Note that even with the granularity +used in creating the table for F, it's possible not all the entries in +the table for G will be filled in. So, make a pass through the +inverse's table, filling in any missing entries by linear interpolation. + +III. How to create distribution tables (in practice) + +If you want to do all this yourself, I've provided several tools to help: + +1. maketable does the steps 2-4 above, and then generates the appropriate +header file. So if you have your own time distribution, you can generate +the header simply by: + + maketable < time.values > header.h + +2. As explained in the other README file, the somewhat sleazy way I have +of generating correlated values needs correction. You can generate your +own correction tables by compiling makesigtable and makemutable with +your header file. Check the Makefile to see how this is done. + +3. Warning: maketable, makesigtable and especially makemutable do +enormous amounts of floating point arithmetic. Don't try running +these on an old 486. (NIST Net itself will run fine on such a +system, since in operation, it just needs to do a few simple integral +calculations. But getting there takes some work.) + +4. The tables produced are all normalized for mean 0 and standard +deviation 1. How do you know what values to use for real? Here, I've +provided a simple "stats" utility. Give it a series of floating point +values, and it will return their mean (mu), standard deviation (sigma), +and correlation coefficient (rho). You can then plug these values +directly into NIST Net. diff --git a/README.iproute2+tc b/README.iproute2+tc new file mode 100644 index 0000000..edd79c0 --- /dev/null +++ b/README.iproute2+tc @@ -0,0 +1,119 @@ +iproute2+tc* + +It's the first release of Linux traffic control engine. + + +NOTES. +* csz scheduler is inoperational at the moment, and probably + never will be repaired but replaced with h-pfq scheduler. +* To use "fw" classifier you will need ipfwchains patch. +* No manual available. Ask me, if you have problems (only try to guess + answer yourself at first 8)). + + +Micro-manual how to start it the first time +------------------------------------------- + +A. Attach CBQ to eth1: + +tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 cell 8 \ +avpkt 1000 mpu 64 + +B. Add root class: + +tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit \ +allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20 avpkt 1000 + +C. Add default interactive class: + +tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 1Mbit \ +allot 1514 cell 8 weight 100Kbit prio 3 maxburst 20 avpkt 1000 split 1:0 \ +defmap c0 + +D. Add default class: + +tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 8Mbit \ +allot 1514 cell 8 weight 800Kbit prio 7 maxburst 20 avpkt 1000 split 1:0 \ +defmap 3f + +etc. etc. etc. Well, it is enough to start 8) The rest can be guessed 8) +Look also at more elaborated example, ready to start rsvpd, +in rsvp/cbqinit.eth1. + + +Terminology and advices about setting CBQ parameters may be found in Sally Floyd +papers. + + +Pairs X:Y are class handles, X:0 are qdisc heandles. +weight should be proportional to rate for leaf classes +(I choosed it ten times less, but it is not necessary) + +defmap is bitmap of logical priorities served by this class. + +E. Another qdiscs are simpler. F.e. let's join TBF on class 1:2 + +tc qdisc add dev eth1 parent 1:2 tbf rate 64Kbit buffer 5Kb/8 limit 10Kb + +F. Look at all that we created: + +tc qdisc ls dev eth1 +tc class ls dev eth1 + +G. Install "route" classifier on root of cbq and map destination from realm +1 to class 1:2 + +tc filter add dev eth1 parent 1:0 protocol ip prio 100 route to 1 classid 1:2 + +H. Assign routes to 10.11.12.0/24 to realm 1 + +ip route add 10.11.12.0/24 dev eth1 via whatever realm 1 + +etc. The same thing can be made with rules. +I still did not test ipchains, but they should work too. + +Setup of rsvp and u32 classifiers is more hairy. +If you read RSVP specs, you will understand how rsvp classifier +works easily. What's about u32... That's example: + + + +#! /bin/sh + +TC=/home/root/tc + +# Setup classifier root on eth1 root (it is cbq) +$TC filter add dev eth1 parent 1:0 prio 5 protocol ip u32 + +# Create hash table of 256 slots with ID 1: +$TC filter add dev eth1 parent 1:0 prio 5 handle 1: u32 divisor 256 + +# Add to 6th slot of hash table rule to select tcp/telnet to 193.233.7.75 +# direct it to class 1:4 and prescribe to fall to best effort, +# if traffic violate TBF (32kbit,5K) +$TC filter add dev eth1 parent 1:0 prio 5 u32 ht 1:6: \ + match ip dst 193.233.7.75 \ + match tcp dst 0x17 0xffff \ + flowid 1:4 \ + police rate 32kbit buffer 5kb/8 mpu 64 mtu 1514 index 1 + +# Add to 1th slot of hash table rule to select icmp to 193.233.7.75 +# direct it to class 1:4 and prescribe to fall to best effort, +# if traffic violate TBF (10kbit,5K) +$TC filter add dev eth1 parent 1:0 prio 5 u32 ht 1:: \ + sample ip protocol 1 0xff \ + match ip dst 193.233.7.75 \ + flowid 1:4 \ + police rate 10kbit buffer 5kb/8 mpu 64 mtu 1514 index 2 + +# Lookup hash table, if it is not fragmented frame +# Use protocol as hash key +$TC filter add dev eth1 parent 1:0 prio 5 handle ::1 u32 ht 800:: \ + match ip nofrag \ + offset mask 0x0F00 shift 6 \ + hashkey mask 0x00ff0000 at 8 \ + link 1: + + +Alexey Kuznetsov +kuznet@ms2.inr.ac.ru diff --git a/README.lnstat b/README.lnstat new file mode 100644 index 0000000..057925f --- /dev/null +++ b/README.lnstat @@ -0,0 +1,81 @@ +lnstat - linux networking statistics +(C) 2004 Harald Welte , various useful fixups: compilation + with old kernels, cross-compiling, "all" == "any" in prefix spec. + * Collected from my disk, cleaned and packed to directory iproute2/misc/ + several utilities: ss, nstat, ifstat, rtacct, arpd and module tcp_diag. + Writing some docs. me. + * prepared patchlet for pidentd to use tcp_diag. + * David Miller: 64bit (and even worse 64bit kernel/32 bit user :-) fixes + to above. tcp_diag is merged to main tree. + * Alexandr D. Kanevskiy : various flaws in ss + * Alexandr D. Kanevskiy : oops, more aggressive caching + of names opened old bugs: ip started to print garbage in some places. + * Robert Olsson, rt_cache_stat. Renamed to rtstat. + * An old bug in "ip maddr ls": reduntant empty lines in output. + Seeing this crap for ages but lucky match of desire/ability to repair + and a huff about this happened only today. :-) + * "Mr. James W. Laferriere" + doc: option to produce ps output for non-a4 and not only 2 pages/sheet. + * Jamal's patch for ingres qdisc. + * Bernd Eckenfels : deleted orphaned bogus #include + in include/utils.h. + * Julian Anastasov : uninitialized fields in nexthop + producing funny "dead" nexthops in multipath routes. + Stupid me, look at the first line in [010803]... Was it difficult to guess + this that time? People blame for several months. :-) + Special thanks to bert hubert who raised the issue in netdev. + Thanks and apologies to Terry Schmidt , + Ruben Puettmann , + Mark Ivens . + * willy tarreau : "make install" target. + * Tunable limit for sch_sfq. Patch to kernel activating this + is about to be submitted. Reminded by Adi Nugroho . + +[010824] + * ip address add sets scope of loopback addreses to "host". + Advised by David Miller. + * ZIP! and David Ford + Some strcpy's changed to strncpy's. + * David Ford , test for compilation with gcc3. + * David Ford . Damn, I broke rtnl_talk in previous + snapshot. + +[010803] + * If "dev" is not specified in multipath route, ifindex remained + uninitialized. Grr. Thanks to Kunihiro Ishiguro . + * Rafal Maszkowski , batch mode tc. The most old patch. + * Updates list of data protocol ids. + Lots of reporters. I bring my apologies. + * Jan Rekorajski . Updated list of datalink types. + * Christina Chen . Bug in parsing IPv6 address match in u32. + * Pekka Savola . ip -6 route flush dev lo stuck + on deleting root of the table. + * Werner. dsmark fixes. + * Alexander Demenshin . Old miracleous bug + in ip monitor. It was puzzle, people permanently blame that + it prints some crap. + * Rui Prior . f_route failed to resolve fromif. + Werner also noticed this and sent patch. Bad place... [RETHINK] + * Kim Woelders . + - changes in Makefile for cross-compile + - understand "all" as alias for "any" + - bug in iprule.c +! [ NB. Also he sent patch for kernel. Do not forget! ] + * Werner. Fix to tc core files: wrong exits etc. + * Bernd Jendrissek . Some sanitizations of tc.c +!* Marian Jancar . He say q_tbf prints wrong latency! +! Seems, he is wrong. + * Werner (and Nikolai Vladychevski ) check ->print_copts + to avoid segfault. + +[001007] + * Compiles under rh-7.0 + +[000928] + * Sorry. I have lost all the CVS with changes made since 000305. + If someone sent me a patch after this date, please, resubmit. + Restored from the last backup and mailboxes: + + * Edit ip-cref.tex by raf . + * RTAX_REORDERING support. + * IFLA_MASTER support. + * Bug in rtnl_talk(), libnetlink.c. Reported by David P. Olshfski + + +[000305] + * Bugs in RESOLVE_HOSTNAMES. Bratislav Ilich + * ARPHRD_IEEE802_TR + +[000225] + * ECN in q_red.c. + +[000221] + * diffserv update from Jamal Hadi Salim + * Some bits of IPX from Steve Whitehouse. + * ATM qdisc from Werner Almesberger + * Support for new attributes on routes in linux-2.3. + +[991023] + No news, only several bugs are fixed. + * Since ss990630 "ip rule list" printed wrong prefix length. + Vladimir V. Ivanov + * "ip rule" parsed >INT_MAX values of metric incorrectly. + Matthew G. Marsh + * Some improvements in doc/Makefile advised by + Andi Kleen and Werner Almesberger. + +[990824] + * new attributes in "ip route": rtt, rttvar, cwnd, ssthresh and advmss. + * some updates in documentaion to reflect new status. + +[990630] + * DiffServ support. + Werner Almesberger + Jamal Hadi Salim + * DECnet support. + Steve Whitehouse + * Some minor tweaks in docs and code. + +[990530] + * routel script. Stephen R. van den Berg + * Bug in tc/q_prio.c resetting priomap. Reported by + Ole Husgaard and + Jan Kasprzak + * IP command reference manual is published (ip-cref.tex). + I am sorry, but tc-cref.tex is still not ready, to be more + exact the draft does not describe current tc 8-) + * ip, rtmon, rtacct utilities are updated according to manual 8-) + Lots of changes: + - (MAIN) "flush" command for addr, neigh and route. + - error messages are sanitized; now it does not print + usage() page on each error. + - output format is improved. + - "oneline" mode is added. + - etc. + * Name databases; resolution acsii <-> numeric is split out to lib/* + * scripts ifcfg, ifone and rtpr. + * examples/dhcp-client-script is copied from my patch to ISC dhcp. + * Makefile in doc/ directory. + +[990417] + * "pmtudisc" flag to "ip tunnel". Phil Karn + * bug in tc/q_tbf.c preventing setting peak_rate, Martin Mares + * doc/flowlabels.tex + +[990329] + + * This snapshot fixes some compatibility problems, which I introduced + occasionally to previous snapshots. + * Namely, "allot" to "tc qdisc add ... cbq" is accepted but ignored. + * Another changes are supposed to be shown in the next snapshot, but + because of troubles with "allot" I am forced to release premature + version. Namely, "cell", "prio", "weight" etc. are optional now. + * doc/ip-tunnels.tex + +[990327] + * History was not recorded. + +[981002] + * Rani Assaf contributed resolving + addresses to names. + BEWARE! DO NOT USE THIS OPTION, WHEN REPORTING BUGS IN + IPROUTE OR IN KERENEL. ALL THE BUG REPORTS MUST CONTAIN + ONLY NUMERIC ADDRESSES. + +[981101] + * now it should compile for any libc. diff --git a/configure b/configure new file mode 100755 index 0000000..dc14e54 --- /dev/null +++ b/configure @@ -0,0 +1,27 @@ +#! /bin/bash +# This is not an autconf generated configure +# +INCLUDE=${1:-"$PWD/include"} + +echo "# Generated config based on" $INCLUDE >Config + +echo "TC schedulers" + +echo -n " ATM " +cat >/tmp/atmtest.c < +int main(int argc, char **argv) { + struct atm_qos qos; + (void) text2qos("aal5,ubr:sdu=9180,rx:none",&qos,0); + return 0; +} +EOF +gcc -I$INCLUDE -o /tmp/atmtest /tmp/atmtest.c -latm >/dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "TC_CONFIG_ATM:=y" >>Config + echo yes +else + echo no +fi +rm -f /tmp/atmtest.c /tmp/atmtest diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..84836a2 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,55 @@ +PSFILES=ip-cref.ps ip-tunnels.ps api-ip6-flowlabels.ps ss.ps nstat.ps arpd.ps rtstat.ps +# tc-cref.ps +# api-rtnl.tex api-pmtudisc.tex api-news.tex +# iki-netdev.ps iki-neighdst.ps + + +LATEX=latex +DVIPS=dvips +SGML2DVI=sgml2latex --output=dvi +SGML2HTML=sgml2html -s 0 +LPR=lpr -Zsduplex +SHELL=bash +PAGESIZE=a4 +PAGESPERPAGE=2 + +HTMLFILES=$(subst .sgml,.html,$(shell echo *.sgml)) +DVIFILES=$(subst .ps,.dvi,$(PSFILES)) + + +all: pstwocol + +pstwocol: $(PSFILES) + +html: $(HTMLFILES) + +dvi: $(DVIFILES) + +print: $(PSFILES) + $(LPR) $(PSFILES) + +%.dvi: %.sgml + $(SGML2DVI) $< + +%.dvi: %.tex + @set -e; pass=2; echo "Running LaTeX $<"; \ + while [ `$(LATEX) $< &1 | \ + grep -c '^\(LaTeX Warning: Label(s) may\|No file \|! Emergency stop\)'` -ge 1 ]; do \ + if [ $$pass -gt 3 ]; then \ + echo "Seems, something is wrong. Try by hands." ; exit 1 ; \ + fi; \ + echo "Re-running LaTeX $<, $${pass}d pass"; pass=$$[$$pass + 1]; \ + done + +%.ps: %.dvi + $(DVIPS) $< -o $@ + +%.html: %.sgml + $(SGML2HTML) $< + +install: + install -m 0644 $(shell echo *.tex) $(DESTDIR)$(DOCDIR) + install -m 0644 $(shell echo *.sgml) $(DESTDIR)$(DOCDIR) + +clean: + rm -f *.aux *.log *.toc $(PSFILES) $(DVIFILES) *.html diff --git a/doc/Plan b/doc/Plan new file mode 100644 index 0000000..55f478e --- /dev/null +++ b/doc/Plan @@ -0,0 +1,16 @@ +Partially finished work. + +1. User Reference manuals. +1.1 IP Command reference (ip-cref.tex, published) +1.2 TC Command reference (tc-cref.tex) +1.3 IP tunnels (ip-tunnels.tex, published) + +2. Linux-2.2 Networking API +2.1 RTNETLINK (api-rtnl.tex) +2.2 Path MTU Discovery (api-pmtudisc.tex) +2.3 IPv6 Flow Labels (api-ip6-flowlabels.tex, published) +2.4 Miscellaneous extensions (api-misc.tex) + +3. Linux-2.2 Networking Intra-Kernel Interfaces +3.1 NetDev --- Networking Devices and netdev... (iki-netdev.tex) +3.2 Neighbour cache and destination cache. (iki-neighdst.tex) diff --git a/doc/SNAPSHOT.tex b/doc/SNAPSHOT.tex new file mode 100644 index 0000000..7ed0298 --- /dev/null +++ b/doc/SNAPSHOT.tex @@ -0,0 +1 @@ +\def\Draft{020116} diff --git a/doc/actions/gact-usage b/doc/actions/gact-usage new file mode 100644 index 0000000..de1308d --- /dev/null +++ b/doc/actions/gact-usage @@ -0,0 +1,79 @@ + +gact [RAND] [INDEX] + +Where: + ACTION := reclassify | drop | continue | pass | ok + RAND := random + RANDTYPE := netrand | determ + VAL : = value not exceeding 10000 + INDEX := index value used + +ACTION semantics +- pass and ok are equivalent to accept +- continue allows to restart classification lookup +- drop drops packets +- reclassify implies continue classification where we left off + +randomization +-------------- + +At the moment there are only two algorithms. One is deterministic +and the other uses internal kernel netrand. + +Examples: + +Rules can be installed on both ingress and egress - this shows ingress +only + +tc qdisc add dev eth0 ingress + +# example 1 +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop + +ping -c 20 10.0.0.9 + +-- +filter u32 +filter u32 fh 800: ht divisor 1 +filter u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 32 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type none pass val 0 + index 1 ref 1 bind 1 installed 59 sec used 35 sec + Sent 1680 bytes 20 pkts (dropped 20, overlimits 0 ) + +---- + +# example 2 +#allow 1 out 10 randomly using the netrand generator +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop random netrand ok 10 + +ping -c 20 10.0.0.9 + +---- +filter protocol ip pref 6 u32 filter protocol ip pref 6 u32 fh 800: ht divisor 1filter protocol ip pref 6 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 20 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type netrand pass val 10 + index 5 ref 1 bind 1 installed 49 sec used 25 sec + Sent 1680 bytes 20 pkts (dropped 16, overlimits 0 ) + +-------- +#alternative: deterministically accept every second packet +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop random determ ok 2 + +ping -c 20 10.0.0.9 + +tc -s filter show parent ffff: dev eth0 +----- +filter protocol ip pref 6 u32 filter protocol ip pref 6 u32 fh 800: ht divisor 1filter protocol ip pref 6 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 20 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type determ pass val 2 + index 4 ref 1 bind 1 installed 118 sec used 82 sec + Sent 1680 bytes 20 pkts (dropped 10, overlimits 0 ) +----- + diff --git a/doc/actions/mirred-usage b/doc/actions/mirred-usage new file mode 100644 index 0000000..3e135a0 --- /dev/null +++ b/doc/actions/mirred-usage @@ -0,0 +1,71 @@ + +Very funky action. I do plan to add to a few more things to it +This is the basic stuff. Idea borrowed from the way ethernet switches +mirror and redirect packets. + +Usage: + +mirred [index INDEX] +where: +DIRECTION := +ACTION := +INDEX is the specific policy instance id +DEVICENAME is the devicename + + +Mirroring essentially takes a copy of the packet whereas redirecting +steals the packet and redirects to specified destination. + +Some examples: +Host A is hooked up to us on eth0 + +tc qdisc add dev lo ingress +# redirect all packets arriving on ingress of lo to eth0 +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress redirect dev eth0 + +On host A start a tcpdump on interface connecting to us. + +on our host ping -c 2 127.0.0.1 + +Ping would fail sinc all packets are heading out eth0 +tcpudmp on host A would show them + +if you substitute the redirect with mirror above as in: +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress mirror dev eth0 + +Then you should see the packets on both host A and the local +stack (i.e ping would work). + +Even more funky example: + +# +#allow 1 out 10 packets to randomly make it to the +# host A (Randomness uses the netrand generator) +# +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 \ +action drop random determ ok 10\ +action mirred egress mirror dev eth0 + +------ +Example 2: +# for packets coming from 10.0.0.9: +#Redirect packets on egress (to ISP A) if you exceed a certain rate +# to eth1 (to ISP B) if you exceed a certain rate +# + +tc qdisc add dev eth0 handle 1:0 root prio + +tc filter add dev eth0 parent 1:0 protocol ip prio 6 u32 \ +match ip src 10.0.0.9/32 flowid 1:16 \ +action police rate 100kbit burst 90k ok \ +action mirred egress mirror dev eth1 + +--- + +A more interesting example is when you mirror flows to a dummy device +so you could tcpdump them (dummy by defaults drops all devices it sees). +This is a very useful debug feature. + diff --git a/doc/api-ip6-flowlabels.tex b/doc/api-ip6-flowlabels.tex new file mode 100644 index 0000000..aa34e94 --- /dev/null +++ b/doc/api-ip6-flowlabels.tex @@ -0,0 +1,429 @@ +\documentstyle[12pt,twoside]{article} +\def\TITLE{IPv6 Flow Labels} +\input preamble +\begin{center} +\Large\bf IPv6 Flow Labels in Linux-2.2. +\end{center} + + +\begin{center} +{ \large Alexey~N.~Kuznetsov } \\ +\em Institute for Nuclear Research, Moscow \\ +\verb|kuznet@ms2.inr.ac.ru| \\ +\rm April 11, 1999 +\end{center} + +\vspace{5mm} + +\tableofcontents + +\section{Introduction.} + +Every IPv6 packet carries 28 bits of flow information. RFC2460 splits +these bits to two fields: 8 bits of traffic class (or DS field, if you +prefer this term) and 20 bits of flow label. Currently there exist +no well-defined API to manage IPv6 flow information. In this document +I describe an attempt to design the API for Linux-2.2 IPv6 stack. + +\vskip 1mm + +The API must solve the following tasks: + +\begin{enumerate} + +\item To allow user to set traffic class bits. + +\item To allow user to read traffic class bits of received packets. +This feature is not so useful as the first one, however it will be +necessary f.e.\ to implement ECN [RFC2481] for datagram oriented services +or to implement receiver side of SRP or another end-to-end protocol +using traffic class bits. + +\item To assign flow labels to packets sent by user. + +\item To get flow labels of received packets. I do not know +any applications of this feature, but it is possible that receiver will +want to use flow labels to distinguish sub-flows. + +\item To allocate flow labels in the way, compliant to RFC2460. Namely: + +\begin{itemize} +\item +Flow labels must be uniformly distributed (pseudo-)random numbers, +so that any subset of 20 bits can be used as hash key. + +\item +Flows with coinciding source address and flow label must have identical +destination address and not-fragmentable extensions headers (i.e.\ +hop by hop options and all the headers up to and including routing header, +if it is present.) + +\begin{NB} +There is a hole in specs: some hop-by-hop options can be +defined only on per-packet base (f.e.\ jumbo payload option). +Essentially, it means that such options cannot present in packets +with flow labels. +\end{NB} +\begin{NB} +NB notes here and below reflect only my personal opinion, +they should be read with smile or should not be read at all :-). +\end{NB} + + +\item +Flow labels have finite lifetime and source is not allowed to reuse +flow label for another flow within the maximal lifetime has expired, +so that intermediate nodes will be able to invalidate flow state before +the label is taken over by another flow. +Flow state, including lifetime, is propagated along datagram path +by some application specific methods +(f.e.\ in RSVP PATH messages or in some hop-by-hop option). + + +\end{itemize} + +\end{enumerate} + +\section{Sending/receiving flow information.} + +\paragraph{Discussion.} +\addcontentsline{toc}{subsection}{Discussion} +It was proposed (Where? I do not remember any explicit statement) +to solve the first four tasks using +\verb|sin6_flowinfo| field added to \verb|struct| \verb|sockaddr_in6| +(see RFC2553). + +\begin{NB} + This method is difficult to consider as reasonable, because it + puts additional overhead to all the services, despite of only + very small subset of them (none, to be more exact) really use it. + It contradicts both to IETF spirit and the letter. Before RFC2553 + one justification existed, IPv6 address alignment left 4 byte + hole in \verb|sockaddr_in6| in any case. Now it has no justification. +\end{NB} + +We have two problems with this method. The first one is common for all OSes: +if \verb|recvmsg()| initializes \verb|sin6_flowinfo| to flow info +of received packet, we loose one very important property of BSD socket API, +namely, we are not allowed to use received address for reply directly +and have to mangle it, even if we are not interested in flowinfo subtleties. + +\begin{NB} + RFC2553 adds new requirement: to clear \verb|sin6_flowinfo|. + Certainly, it is not solution but rather attempt to force applications + to make unnecessary work. Well, as usually, one mistake in design + is followed by attempts to patch the hole and more mistakes... +\end{NB} + +Another problem is Linux specific. Historically Linux IPv6 did not +initialize \verb|sin6_flowinfo| at all, so that, if kernel does not +support flow labels, this field is not zero, but a random number. +Some applications also did not take care about it. + +\begin{NB} +Following RFC2553 such applications can be considered as broken, +but I still think that they are right: clearing all the address +before filling known fields is robust but stupid solution. +Useless wasting CPU cycles and +memory bandwidth is not a good idea. Such patches are acceptable +as temporary hacks, but not as standard of the future. +\end{NB} + + +\paragraph{Implementation.} +\addcontentsline{toc}{subsection}{Implementation} +By default Linux IPv6 does not read \verb|sin6_flowinfo| field +assuming that common applications are not obliged to initialize it +and are permitted to consider it as pure alignment padding. +In order to tell kernel that application +is aware of this field, it is necessary to set socket option +\verb|IPV6_FLOWINFO_SEND|. + +\begin{verbatim} + int on = 1; + setsockopt(sock, SOL_IPV6, IPV6_FLOWINFO_SEND, + (void*)&on, sizeof(on)); +\end{verbatim} + +Linux kernel never fills \verb|sin6_flowinfo| field, when passing +message to user space, though the kernels which support flow labels +initialize it to zero. If user wants to get received flowinfo, he +will set option \verb|IPV6_FLOWINFO| and after this he will receive +flowinfo as ancillary data object of type \verb|IPV6_FLOWINFO| +(cf.\ RFC2292). + +\begin{verbatim} + int on = 1; + setsockopt(sock, SOL_IPV6, IPV6_FLOWINFO, (void*)&on, sizeof(on)); +\end{verbatim} + +Flowinfo received and latched by a connected TCP socket also may be fetched +with \verb|getsockopt()| \verb|IPV6_PKTOPTIONS| together with +another optional information. + +Besides that, in the spirit of RFC2292 the option \verb|IPV6_FLOWINFO| +may be used as alternative way to send flowinfo with \verb|sendmsg()| or +to latch it with \verb|IPV6_PKTOPTIONS|. + +\paragraph{Note about IPv6 options and destination address.} +\addcontentsline{toc}{subsection}{IPv6 options and destination address} +If \verb|sin6_flowinfo| does contain not zero flow label, +destination address in \verb|sin6_addr| and non-fragmentable +extension headers are ignored. Instead, kernel uses the values +cached at flow setup (see below). However, for connected sockets +kernel prefers the values set at connection time. + +\paragraph{Example.} +\addcontentsline{toc}{subsection}{Example} +After setting socket option \verb|IPV6_FLOWINFO| +flowlabel and DS field are received as ancillary data object +of type \verb|IPV6_FLOWINFO| and level \verb|SOL_IPV6|. +In the cases when it is convenient to use \verb|recvfrom(2)|, +it is possible to replace library variant with your own one, +sort of: + +\begin{verbatim} +#include +#include + +size_t recvfrom(int fd, char *buf, size_t len, int flags, + struct sockaddr *addr, int *addrlen) +{ + size_t cc; + char cbuf[128]; + struct cmsghdr *c; + struct iovec iov = { buf, len }; + struct msghdr msg = { addr, *addrlen, + &iov, 1, + cbuf, sizeof(cbuf), + 0 }; + + cc = recvmsg(fd, &msg, flags); + if (cc < 0) + return cc; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; + *addrlen = msg.msg_namelen; + for (c=CMSG_FIRSTHDR(&msg); c; c = CMSG_NEXTHDR(&msg, c)) { + if (c->cmsg_level != SOL_IPV6 || + c->cmsg_type != IPV6_FLOWINFO) + continue; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = *(__u32*)CMSG_DATA(c); + } + return cc; +} +\end{verbatim} + + + +\section{Flow label management.} + +\paragraph{Discussion.} +\addcontentsline{toc}{subsection}{Discussion} +Requirements of RFC2460 are pretty tough. Particularly, lifetimes +longer than boot time require to store allocated labels at stable +storage, so that the full implementation necessarily includes user space flow +label manager. There are at least three different approaches: + +\begin{enumerate} +\item {\bf ``Cooperative''. } We could leave flow label allocation wholly +to user space. When user needs label he requests manager directly. The approach +is valid, but as any ``cooperative'' approach it suffers of security problems. + +\begin{NB} +One idea is to disallow not privileged user to allocate flow +labels, but instead to pass the socket to manager via \verb|SCM_RIGHTS| +control message, so that it will allocate label and assign it to socket +itself. Hmm... the idea is interesting. +\end{NB} + +\item {\bf ``Indirect''.} Kernel redirects requests to user level daemon +and does not install label until the daemon acknowledged the request. +The approach is the most promising, it is especially pleasant to recognize +parallel with IPsec API [RFC2367,Craig]. Actually, it may share API with +IPsec. + +\item {\bf ``Stupid''.} To allocate labels in kernel space. It is the simplest +method, but it suffers of two serious flaws: the first, +we cannot lease labels with lifetimes longer than boot time, the second, +it is sensitive to DoS attacks. Kernel have to remember all the obsolete +labels until their expiration and malicious user may fastly eat all the +flow label space. + +\end{enumerate} + +Certainly, I choose the most ``stupid'' method. It is the cheapest one +for implementor (i.e.\ me), and taking into account that flow labels +still have no serious applications it is not useful to work on more +advanced API, especially, taking into account that eventually we +will get it for no fee together with IPsec. + + +\paragraph{Implementation.} +\addcontentsline{toc}{subsection}{Implementation} +Socket option \verb|IPV6_FLOWLABEL_MGR| allows to +request flow label manager to allocate new flow label, to reuse +already allocated one or to delete old flow label. +Its argument is \verb|struct| \verb|in6_flowlabel_req|: + +\begin{verbatim} +struct in6_flowlabel_req +{ + struct in6_addr flr_dst; + __u32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_reserved; + /* Options in format of IPV6_PKTOPTIONS */ +}; +\end{verbatim} + +\begin{itemize} + +\item \verb|dst| is IPv6 destination address associated with the label. + +\item \verb|label| is flow label value in network byte order. If it is zero, +kernel will allocate new pseudo-random number. Otherwise, kernel will try +to lease flow label ordered by user. In this case, it is user task to provide +necessary flow label randomness. + +\item \verb|action| is requested operation. Currently, only three operations +are defined: + +\begin{verbatim} +#define IPV6_FL_A_GET 0 /* Get flow label */ +#define IPV6_FL_A_PUT 1 /* Release flow label */ +#define IPV6_FL_A_RENEW 2 /* Update expire time */ +\end{verbatim} + +\item \verb|flags| are optional modifiers. Currently +only \verb|IPV6_FL_A_GET| has modifiers: + +\begin{verbatim} +#define IPV6_FL_F_CREATE 1 /* Allowed to create new label */ +#define IPV6_FL_F_EXCL 2 /* Do not create new label */ +\end{verbatim} + + +\item \verb|share| defines who is allowed to reuse the same flow label. + +\begin{verbatim} +#define IPV6_FL_S_NONE 0 /* Not defined */ +#define IPV6_FL_S_EXCL 1 /* Label is private */ +#define IPV6_FL_S_PROCESS 2 /* May be reused by this process */ +#define IPV6_FL_S_USER 3 /* May be reused by this user */ +#define IPV6_FL_S_ANY 255 /* Anyone may reuse it */ +\end{verbatim} + +\item \verb|linger| is time in seconds. After the last user releases flow +label, it will not be reused with different destination and options at least +during this time. If \verb|share| is not \verb|IPV6_FL_S_EXCL| the label +still can be shared by another sockets. Current implementation does not allow +unprivileged user to set linger longer than 60 sec. + +\item \verb|expires| is time in seconds. Flow label will be kept at least +for this time, but it will not be destroyed before user released it explicitly +or closed all the sockets using it. Current implementation does not allow +unprivileged user to set timeout longer than 60 sec. Proviledged applications +MAY set longer lifetimes, but in this case they MUST save allocated +labels at stable storage and restore them back after reboot before the first +application allocates new flow. + +\end{itemize} + +This structure is followed by optional extension headers associated +with this flow label in format of \verb|IPV6_PKTOPTIONS|. Only +\verb|IPV6_HOPOPTS|, \verb|IPV6_RTHDR| and, if \verb|IPV6_RTHDR| presents, +\verb|IPV6_DSTOPTS| are allowed. + +\paragraph{Example.} +\addcontentsline{toc}{subsection}{Example} + The function \verb|get_flow_label| allocates +private flow label. + +\begin{verbatim} +int get_flow_label(int fd, struct sockaddr_in6 *dst, __u32 fl) +{ + int on = 1; + struct in6_flowlabel_req freq; + + memset(&freq, 0, sizeof(freq)); + freq.flr_label = htonl(fl); + freq.flr_action = IPV6_FL_A_GET; + freq.flr_flags = IPV6_FL_F_CREATE | IPV6_FL_F_EXCL; + freq.flr_share = IPV6_FL_S_EXCL; + memcpy(&freq.flr_dst, &dst->sin6_addr, 16); + if (setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &freq, sizeof(freq)) == -1) { + perror ("can't lease flowlabel"); + return -1; + } + dst->sin6_flowinfo |= freq.flr_label; + + if (setsockopt(fd, SOL_IPV6, IPV6_FLOWINFO_SEND, + &on, sizeof(on)) == -1) { + perror ("can't send flowinfo"); + + freq.flr_action = IPV6_FL_A_PUT; + setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &freq, sizeof(freq)); + return -1; + } + return 0; +} +\end{verbatim} + +A bit more complicated example using routing header can be found +in \verb|ping6| utility (\verb|iputils| package). Linux rsvpd backend +contains an example of using operation \verb|IPV6_FL_A_RENEW|. + +\paragraph{Listing flow labels.} +\addcontentsline{toc}{subsection}{Listing flow labels} +List of currently allocated +flow labels may be read from \verb|/proc/net/ip6_flowlabel|. + +\begin{verbatim} +Label S Owner Users Linger Expires Dst Opt +A1BE5 1 0 0 6 3 3ffe2400000000010a0020fffe71fb30 0 +\end{verbatim} + +\begin{itemize} +\item \verb|Label| is hexadecimal flow label value. +\item \verb|S| is sharing style. +\item \verb|Owner| is ID of creator, it is zero, pid or uid, depending on + sharing style. +\item \verb|Users| is number of applications using the label now. +\item \verb|Linger| is \verb|linger| of this label in seconds. +\item \verb|Expires| is time until expiration of the label in seconds. It may + be negative, if the label is in use. +\item \verb|Dst| is IPv6 destination address. +\item \verb|Opt| is length of options, associated with the label. Option + data are not accessible. +\end{itemize} + + +\paragraph{Flow labels and RSVP.} +\addcontentsline{toc}{subsection}{Flow labels and RSVP} +RSVP daemon supports IPv6 flow labels +without any modifications to standard ISI RAPI. Sender must allocate +flow label, fill corresponding sender template and submit it to local rsvp +daemon. rsvpd will check the label and start to announce it in PATH +messages. Rsvpd on sender node will renew the flow label, so that it will not +be reused before path state expires and all the intermediate +routers and receiver purge flow state. + +\verb|rtap| utility is modified to parse flow labels. F.e.\ if user allocated +flow label \verb|0xA1234|, he may write: + +\begin{verbatim} +RTAP> sender 3ffe:2400::1/FL0xA1234 +\end{verbatim} + +Receiver makes reservation with command: +\begin{verbatim} +RTAP> reserve ff 3ffe:2400::1/FL0xA1234 +\end{verbatim} + +\end{document} diff --git a/doc/arpd.sgml b/doc/arpd.sgml new file mode 100644 index 0000000..0ab79c6 --- /dev/null +++ b/doc/arpd.sgml @@ -0,0 +1,130 @@ + + +
+ +ARPD Daemon +<author>Alexey Kuznetsov, <tt/kuznet@ms2.inr.ac.ru/ +<date>some_negative_number, 20 Sep 2001 +<abstract> +<tt/arpd/ is daemon collecting gratuitous ARP information, saving +it on local disk and feeding it to kernel on demand to avoid +redundant broadcasting due to limited size of kernel ARP cache. +</abstract> + + +<p><bf/Description/ + +<p>The format of the command is: + +<tscreen><verb> + arpd OPTIONS [ INTERFACE [ INTERFACE ... ] ] +</verb></tscreen> + +<p> <tt/OPTIONS/ are: + +<itemize> + +<item><tt/-l/ - dump <tt/arpd/ database to stdout and exit. Output consists +of three columns: interface index, IP address and MAC address. +Negative entries for dead hosts are also shown, in this case MAC address +is replaced by word <tt/FAILED/ followed by colon and time when the fact +that host is dead was proven the last time. + +<item><tt/-f FILE/ - read and load <tt/arpd/ database from <tt/FILE/ +in text format similar dumped by option <tt/-l/. Exit after load, +probably listing resulting database, if option <tt/-l/ is also given. +If <tt/FILE/ is <tt/-/, <tt/stdin/ is read to get ARP table. + +<item><tt/-b DATABASE/ - location of database file. Default location is +<tt>/var/lib/arpd/arpd.db</tt>. + +<item><tt/-a NUMBER/ - <tt/arpd/ not only passively listens ARP on wire, but +also send brodcast queries itself. <tt/NUMBER/ is number of such queries +to make before destination is considered as dead. When <tt/arpd/ is started +as kernel helper (i.e. with <tt/app_solicit/ enabled in <tt/sysctl/ +or even with option <tt/-k/) without this option and still did not learn enough +information, you can observe 1 second gaps in service. Not fatal, but +not good. + +<item><tt/-k/ - suppress sending broadcast queries by kernel. It takes +sense together with option <tt/-a/. + +<item><tt/-n TIME/ - timeout of negative cache. When resolution fails <tt/arpd/ +suppresses further attempts to resolve for this period. It makes sense +only together with option <tt/-k/. This timeout should not be too much +longer than boot time of a typical host not supporting gratuitous ARP. +Default value is 60 seconds. + +<item><tt/-R RATE/ - maximal steady rate of broadcasts sent by <tt/arpd/ +in packets per second. Default value is 1. + +<item><tt/-B NUMBER/ - number of broadcasts sent by <tt/arpd/ back to back. +Default value is 3. Together with option <tt/-R/ this option allows +to police broadcasting not to exceed <tt/B+R*T/ over any interval +of time <tt/T/. + +</itemize> + +<p><tt/INTERFACE/ is name of networking inteface to watch. +If no interfaces given, <tt/arpd/ monitors all the interfaces. +In this case <tt/arpd/ does not adjust <tt/sysctl/ parameters, +it is supposed user does this himself after <tt/arpd/ is started. + + +<p> Signals + +<p> <tt/arpd/ exits gracefully syncing database and restoring adjusted +<tt/sysctl/ parameters, when receives <tt/SIGINT/ or <tt/SIGTERM/. +<tt/SIGHUP/ syncs database to disk. <tt/SIGUSR1/ sends some statistics +to <tt/syslog/. Effect of another signals is undefined, they may corrupt +database and leave <tt/sysctl/ parameters in an unpredictable state. + +<p> Note + +<p> In order to <tt/arpd/ be able to serve as ARP resolver, kernel must be +compiled with the option <tt/CONFIG_ARPD/ and, in the case when interface list +is not given on command line, variable <tt/app_solicit/ +on interfaces of interest should be set in <tt>/proc/sys/net/ipv4/neigh/*</tt>. +If this is not made <tt/arpd/ still collects gratuitous ARP information +in its database. + +<p> Examples + +<enum> +<item> Start <tt/arpd/ to collect gratuitous ARP, but not messing +with kernel functionality: + +<tscreen><verb> + arpd -b /var/tmp/arpd.db +</verb></tscreen> + +<item> Look at result after some time: + +<tscreen><verb> + killall arpd + arpd -l -b /var/tmp/arpd.db +</verb></tscreen> + +<item> To enable kernel helper, leaving leading role to kernel: + +<tscreen><verb> + arpd -b /var/tmp/arpd.db -a 1 eth0 eth1 +</verb></tscreen> + +<item> Completely replace kernel resolution on interfaces <tt/eth0/ +and <tt/eth1/. In this case kernel still does unicast probing to +validate entries, but all the broadcast activity is suppressed +and made under authority of <tt/arpd/: + +<tscreen><verb> + arpd -b /var/tmp/arpd.db -a 3 -k eth0 eth1 +</verb></tscreen> + +This is mode which <tt/arpd/ is supposed to work normally. +It is not default just to prevent occasional enabling of too aggressive +mode occasionally. + +</enum> + +</article> + diff --git a/doc/do-psnup b/doc/do-psnup new file mode 100755 index 0000000..2dce848 --- /dev/null +++ b/doc/do-psnup @@ -0,0 +1,16 @@ +#! /bin/bash +# $1 = Temporary file . "string" +# $2 = File to process . "string" +# $3 = Page size . ie: a4 , letter ... "string" +# $4 = Number of pages to fit on a single sheet . "numeric" + +if type psnup >&/dev/null; then + echo "psnup -$4 -p$3 $1 $2" + psnup -$4 -p$3 $1 $2 +elif type psmulti >&/dev/null; then + echo "psmulti $1 > $2" + psmulti $1 > $2 +else + echo "cp $1 $2" + cp $1 $2 +fi diff --git a/doc/ip-cref.tex b/doc/ip-cref.tex new file mode 100644 index 0000000..5eaa4a8 --- /dev/null +++ b/doc/ip-cref.tex @@ -0,0 +1,3316 @@ +\documentstyle[12pt,twoside]{article} +\def\TITLE{IP Command Reference} +\input preamble +\begin{center} +\Large\bf IP Command Reference. +\end{center} + + +\begin{center} +{ \large Alexey~N.~Kuznetsov } \\ +\em Institute for Nuclear Research, Moscow \\ +\verb|kuznet@ms2.inr.ac.ru| \\ +\rm April 14, 1999 +\end{center} + +\vspace{5mm} + +\tableofcontents + +\newpage + +\section{About this document} + +This document presents a comprehensive description of the \verb|ip| utility +from the \verb|iproute2| package. It is not a tutorial or user's guide. +It is a {\em dictionary\/}, not explaining terms, +but translating them into other terms, which may also be unknown to the reader. +However, the document is self-contained and the reader, provided they have a +basic networking background, will find enough information +and examples to understand and configure Linux-2.2 IP and IPv6 +networking. + +This document is split into sections explaining \verb|ip| commands +and options, decrypting \verb|ip| output and containing a few examples. +More voluminous examples and some topics, which require more elaborate +discussion, are in the appendix. + +The paragraphs beginning with NB contain side notes, warnings about +bugs and design drawbacks. They may be skipped at the first reading. + +\section{{\tt ip} --- command syntax} + +The generic form of an \verb|ip| command is: +\begin{verbatim} +ip [ OPTIONS ] OBJECT [ COMMAND [ ARGUMENTS ]] +\end{verbatim} +where \verb|OPTIONS| is a set of optional modifiers affecting the +general behaviour of the \verb|ip| utility or changing its output. All options +begin with the character \verb|'-'| and may be used in either long or abbreviated +forms. Currently, the following options are available: + +\begin{itemize} +\item \verb|-V|, \verb|-Version| + +--- print the version of the \verb|ip| utility and exit. + + +\item \verb|-s|, \verb|-stats|, \verb|-statistics| + +--- output more information. If the option +appears twice or more, the amount of information increases. +As a rule, the information is statistics or some time values. + + +\item \verb|-f|, \verb|-family| followed by a protocol family +identifier: \verb|inet|, \verb|inet6| or \verb|link|. + +--- enforce the protocol family to use. If the option is not present, +the protocol family is guessed from other arguments. If the rest of the command +line does not give enough information to guess the family, \verb|ip| falls back to the default +one, usually \verb|inet| or \verb|any|. \verb|link| is a special family +identifier meaning that no networking protocol is involved. + +\item \verb|-4| + +--- shortcut for \verb|-family inet|. + +\item \verb|-6| + +--- shortcut for \verb|-family inet6|. + +\item \verb|-0| + +--- shortcut for \verb|-family link|. + + +\item \verb|-o|, \verb|-oneline| + +--- output each record on a single line, replacing line feeds +with the \verb|'\'| character. This is convenient when you want to +count records with \verb|wc| or to \verb|grep| the output. The trivial +script \verb|rtpr| converts the output back into readable form. + +\item \verb|-r|, \verb|-resolve| + +--- use the system's name resolver to print DNS names instead of +host addresses. + +\begin{NB} + Do not use this option when reporting bugs or asking for advice. +\end{NB} +\begin{NB} + \verb|ip| never uses DNS to resolve names to addresses. +\end{NB} + +\end{itemize} + +\verb|OBJECT| is the object to manage or to get information about. +The object types currently understood by \verb|ip| are: + +\begin{itemize} +\item \verb|link| --- network device +\item \verb|address| --- protocol (IP or IPv6) address on a device +\item \verb|neighbour| --- ARP or NDISC cache entry +\item \verb|route| --- routing table entry +\item \verb|rule| --- rule in routing policy database +\item \verb|maddress| --- multicast address +\item \verb|mroute| --- multicast routing cache entry +\item \verb|tunnel| --- tunnel over IP +\end{itemize} + +Again, the names of all objects may be written in full or +abbreviated form, f.e.\ \verb|address| is abbreviated as \verb|addr| +or just \verb|a|. + +\verb|COMMAND| specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to \verb|add|, \verb|delete| and +\verb|show| (or \verb|list|) objects, but some objects +do not allow all of these operations or have some additional commands. +The \verb|help| command is available for all objects. It prints +out a list of available commands and argument syntax conventions. + +If no command is given, some default command is assumed. +Usually it is \verb|list| or, if the objects of this class +cannot be listed, \verb|help|. + +\verb|ARGUMENTS| is a list of arguments to the command. +The arguments depend on the command and object. There are two types of arguments: +{\em flags\/}, consisting of a single keyword, and {\em parameters\/}, +consisting of a keyword followed by a value. For convenience, +each command has some {\em default parameter\/} +which may be omitted. F.e.\ parameter \verb|dev| is the default +for the {\tt ip link} command, so {\tt ip link ls eth0} is equivalent +to {\tt ip link ls dev eth0}. +In the command descriptions below such parameters +are distinguished with the marker: ``(default)''. + +Almost all keywords may be abbreviated with several first (or even single) +letters. The shortcuts are convenient when \verb|ip| is used interactively, +but they are not recommended in scripts or when reporting bugs +or asking for advice. ``Officially'' allowed abbreviations are listed +in the document body. + + + +\section{{\tt ip} --- error messages} + +\verb|ip| may fail for one of the following reasons: + +\begin{itemize} +\item +A syntax error on the command line: an unknown keyword, incorrectly formatted +IP address {\em et al\/}. In this case \verb|ip| prints an error message +and exits. As a rule, the error message will contain information +about the reason for the failure. Sometimes it also prints a help page. + +\item +The arguments did not pass verification for self-consistency. + +\item +\verb|ip| failed to compile a kernel request from the arguments +because the user didn't give enough information. + +\item +The kernel returned an error to some syscall. In this case \verb|ip| +prints the error message, as it is output with \verb|perror(3)|, +prefixed with a comment and a syscall identifier. + +\item +The kernel returned an error to some RTNETLINK request. +In this case \verb|ip| prints the error message, as it is output +with \verb|perror(3)| prefixed with ``RTNETLINK answers:''. + +\end{itemize} + +All the operations are atomic, i.e.\ +if the \verb|ip| utility fails, it does not change anything +in the system. One harmful exception is \verb|ip link| command +(Sec.\ref{IP-LINK}, p.\pageref{IP-LINK}), +which may change only some of the device parameters given +on command line. + +It is difficult to list all the error messages (especially +syntax errors). However, as a rule, their meaning is clear +from the context of the command. + +The most common mistakes are: + +\begin{enumerate} +\item Netlink is not configured in the kernel. The message is: +\begin{verbatim} +Cannot open netlink socket: Invalid value +\end{verbatim} + +\item RTNETLINK is not configured in the kernel. In this case +one of the following messages may be printed, depending on the command: +\begin{verbatim} +Cannot talk to rtnetlink: Connection refused +Cannot send dump request: Connection refused +\end{verbatim} + +\item The \verb|CONFIG_IP_MULTIPLE_TABLES| option was not selected +when configuring the kernel. In this case any attempt to use the +\verb|ip| \verb|rule| command will fail, f.e. +\begin{verbatim} +kuznet@kaiser $ ip rule list +RTNETLINK error: Invalid argument +dump terminated +\end{verbatim} + +\end{enumerate} + + +\section{{\tt ip link} --- network device configuration} +\label{IP-LINK} + +\paragraph{Object:} A \verb|link| is a network device and the corresponding +commands display and change the state of devices. + +\paragraph{Commands:} \verb|set| and \verb|show| (or \verb|list|). + +\subsection{{\tt ip link set} --- change device attributes} + +\paragraph{Abbreviations:} \verb|set|, \verb|s|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|dev NAME| (default) + +--- \verb|NAME| specifies the network device on which to operate. + +\item \verb|up| and \verb|down| + +--- change the state of the device to \verb|UP| or \verb|DOWN|. + +\item \verb|arp on| or \verb|arp off| + +--- change the \verb|NOARP| flag on the device. + +\begin{NB} +This operation is {\em not allowed\/} if the device is in state \verb|UP|. +Though neither the \verb|ip| utility nor the kernel check for this condition. +You can get unpredictable results changing this flag while the +device is running. +\end{NB} + +\item \verb|multicast on| or \verb|multicast off| + +--- change the \verb|MULTICAST| flag on the device. + +\item \verb|dynamic on| or \verb|dynamic off| + +--- change the \verb|DYNAMIC| flag on the device. + +\item \verb|name NAME| + +--- change the name of the device. This operation is not +recommended if the device is running or has some addresses +already configured. + +\item \verb|txqueuelen NUMBER| or \verb|txqlen NUMBER| + +--- change the transmit queue length of the device. + +\item \verb|mtu NUMBER| + +--- change the MTU of the device. + +\item \verb|address LLADDRESS| + +--- change the station address of the interface. + +\item \verb|broadcast LLADDRESS|, \verb|brd LLADDRESS| or \verb|peer LLADDRESS| + +--- change the link layer broadcast address or the peer address when +the interface is \verb|POINTOPOINT|. + +\vskip 1mm +\begin{NB} +For most devices (f.e.\ for Ethernet) changing the link layer +broadcast address will break networking. +Do not use it, if you do not understand what this operation really does. +\end{NB} + +\end{itemize} + +\vskip 1mm +\begin{NB} +The {\tt ip} utility does not change the \verb|PROMISC| +or \verb|ALLMULTI| flags. These flags are considered +obsolete and should not be changed administratively. +\end{NB} + +\paragraph{Warning:} If multiple parameter changes are requested, +\verb|ip| aborts immediately after any of the changes have failed. +This is the only case when \verb|ip| can move the system to +an unpredictable state. The solution is to avoid changing +several parameters with one {\tt ip link set} call. + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip link set dummy address 00:00:00:00:00:01| + +--- change the station address of the interface \verb|dummy|. + +\item \verb|ip link set dummy up| + +--- start the interface \verb|dummy|. + +\end{itemize} + + +\subsection{{\tt ip link show} --- display device attributes} +\label{IP-LINK-SHOW} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|lst|, \verb|sh|, \verb|ls|, +\verb|l|. + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|dev NAME| (default) + +--- \verb|NAME| specifies the network device to show. +If this argument is omitted all devices are listed. + +\item \verb|up| + +--- only display running interfaces. + +\end{itemize} + + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip link ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff +kuznet@alisa:~ $ ip link ls sit0 +5: sit0@NONE: <NOARP,UP> mtu 1480 qdisc noqueue + link/sit 0.0.0.0 brd 0.0.0.0 +kuznet@alisa:~ $ ip link ls dummy +2: dummy: <BROADCAST,NOARP> mtu 1500 qdisc noop + link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff +kuznet@alisa:~ $ +\end{verbatim} + + +The number before each colon is an {\em interface index\/} or {\em ifindex\/}. +This number uniquely identifies the interface. This is followed by the {\em interface name\/} +(\verb|eth0|, \verb|sit0| etc.). The interface name is also +unique at every given moment. However, the interface may disappear from the +list (f.e.\ when the corresponding driver module is unloaded) and another +one with the same name may be created later. Besides that, +the administrator may change the name of any device with +\verb|ip| \verb|link| \verb|set| \verb|name| +to make it more intelligible. + +The interface name may have another name or \verb|NONE| appended +after the \verb|@| sign. This means that this device is bound to some other +device, +i.e.\ packets send through it are encapsulated and sent via the ``master'' +device. If the name is \verb|NONE|, the master is unknown. + +Then we see the interface {\em mtu\/} (``maximal transfer unit''). This determines +the maximal size of data which can be sent as a single packet over this interface. + +{\em qdisc\/} (``queuing discipline'') shows the queuing algorithm used +on the interface. Particularly, \verb|noqueue| means that this interface +does not queue anything and \verb|noop| means that the interface is in blackhole +mode i.e.\ all packets sent to it are immediately discarded. +{\em qlen\/} is the default transmit queue length of the device measured +in packets. + +The interface flags are summarized in the angle brackets. + +\begin{itemize} +\item \verb|UP| --- the device is turned on. It is ready to accept +packets for transmission and it may inject into the kernel packets received +from other nodes on the network. + +\item \verb|LOOPBACK| --- the interface does not communicate with other +hosts. All packets sent through it will be returned +and nothing but bounced packets can be received. + +\item \verb|BROADCAST| --- the device has the facility to send packets +to all hosts sharing the same link. A typical example is an Ethernet link. + +\item \verb|POINTOPOINT| --- the link has only two ends with one node +attached to each end. All packets sent to this link will reach the peer +and all packets received by us came from this single peer. + +If neither \verb|LOOPBACK| nor \verb|BROADCAST| nor \verb|POINTOPOINT| +are set, the interface is assumed to be NMBA (Non-Broadcast Multi-Access). +This is the most generic type of device and the most complicated one, because +the host attached to a NBMA link has no means to send to anyone +without additionally configured information. + +\item \verb|MULTICAST| --- is an advisory flag indicating that the interface +is aware of multicasting i.e.\ sending packets to some subset of neighbouring +nodes. Broadcasting is a particular case of multicasting, where the multicast +group consists of all nodes on the link. It is important to emphasize +that software {\em must not\/} interpret the absence of this flag as the inability +to use multicasting on this interface. Any \verb|POINTOPOINT| and +\verb|BROADCAST| link is multicasting by definition, because we have +direct access to all the neighbours and, hence, to any part of them. +Certainly, the use of high bandwidth multicast transfers is not recommended +on broadcast-only links because of high expense, but it is not strictly +prohibited. + +\item \verb|PROMISC| --- the device listens to and feeds to the kernel all +traffic on the link even if it is not destined for us, not broadcasted +and not destined for a multicast group of which we are member. Usually +this mode exists only on broadcast links and is used by bridges and for network +monitoring. + +\item \verb|ALLMULTI| --- the device receives all multicast packets +wandering on the link. This mode is used by multicast routers. + +\item \verb|NOARP| --- this flag is different from the other ones. It has +no invariant value and its interpretation depends on the network protocols +involved. As a rule, it indicates that the device needs no address +resolution and that the software or hardware knows how to deliver packets +without any help from the protocol stacks. + +\item \verb|DYNAMIC| --- is an advisory flag indicating that the interface is +dynamically created and destroyed. + +\item \verb|SLAVE| --- this interface is bonded to some other interfaces +to share link capacities. + +\end{itemize} + +\vskip 1mm +\begin{NB} +There are other flags but they are either obsolete (\verb|NOTRAILERS|) +or not implemented (\verb|DEBUG|) or specific to some devices +(\verb|MASTER|, \verb|AUTOMEDIA| and \verb|PORTSEL|). We do not discuss +them here. +\end{NB} +\begin{NB} +The values of \verb|PROMISC| and \verb|ALLMULTI| flags +shown by the \verb|ifconfig| utility and by the \verb|ip| utility +are {\em different\/}. \verb|ip link ls| shows the true device state, +while \verb|ifconfig| shows the virtual state which was set with +\verb|ifconfig| itself. +\end{NB} + + +The second line contains information on the link layer addresses +associated with the device. The first word (\verb|ether|, \verb|sit|) +defines the interface hardware type. This type determines the format and semantics +of the addresses and is logically part of the address. +The default format of the station address and the broadcast address +(or the peer address for pointopoint links) is a +sequence of hexadecimal bytes separated by colons, but some link +types may have their natural address format, f.e.\ addresses +of tunnels over IP are printed as dotted-quad IP addresses. + +\vskip 1mm +\begin{NB} + NBMA links have no well-defined broadcast or peer address, + however this field may contain useful information, f.e.\ + about the address of broadcast relay or about the address of the ARP server. +\end{NB} +\begin{NB} +Multicast addresses are not shown by this command, see +\verb|ip maddr ls| in~Sec.\ref{IP-MADDR} (p.\pageref{IP-MADDR} of this +document). +\end{NB} + + +\paragraph{Statistics:} With the \verb|-statistics| option, \verb|ip| also +prints interface statistics: + +\begin{verbatim} +kuznet@alisa:~ $ ip -s link ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff + RX: bytes packets errors dropped overrun mcast + 2449949362 2786187 0 0 0 0 + TX: bytes packets errors dropped carrier collsns + 178558497 1783945 332 0 332 35172 +kuznet@alisa:~ $ +\end{verbatim} +\verb|RX:| and \verb|TX:| lines summarize receiver and transmitter +statistics. They contain: +\begin{itemize} +\item \verb|bytes| --- the total number of bytes received or transmitted +on the interface. This number wraps when the maximal length of the data type +natural for the architecture is exceeded, so continuous monitoring requires +a user level daemon snapping it periodically. +\item \verb|packets| --- the total number of packets received or transmitted +on the interface. +\item \verb|errors| --- the total number of receiver or transmitter errors. +\item \verb|dropped| --- the total number of packets dropped due to lack +of resources. +\item \verb|overrun| --- the total number of receiver overruns resulting +in dropped packets. As a rule, if the interface is overrun, it means +serious problems in the kernel or that your machine is too slow +for this interface. +\item \verb|mcast| --- the total number of received multicast packets. This option +is only supported by a few devices. +\item \verb|carrier| --- total number of link media failures f.e.\ because +of lost carrier. +\item \verb|collsns| --- the total number of collision events +on Ethernet-like media. This number may have a different sense on other +link types. +\item \verb|compressed| --- the total number of compressed packets. This is +available only for links using VJ header compression. +\end{itemize} + + +If the \verb|-s| option is entered twice or more, +\verb|ip| prints more detailed statistics on receiver +and transmitter errors. + +\begin{verbatim} +kuznet@alisa:~ $ ip -s -s link ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff + RX: bytes packets errors dropped overrun mcast + 2449949362 2786187 0 0 0 0 + RX errors: length crc frame fifo missed + 0 0 0 0 0 + TX: bytes packets errors dropped carrier collsns + 178558497 1783945 332 0 332 35172 + TX errors: aborted fifo window heartbeat + 0 0 0 332 +kuznet@alisa:~ $ +\end{verbatim} +These error names are pure Ethernetisms. Other devices +may have non zero values in these fields but they may be +interpreted differently. + + +\section{{\tt ip address} --- protocol address management} + +\paragraph{Abbreviations:} \verb|address|, \verb|addr|, \verb|a|. + +\paragraph{Object:} The \verb|address| is a protocol (IP or IPv6) address attached +to a network device. Each device must have at least one address +to use the corresponding protocol. It is possible to have several +different addresses attached to one device. These addresses are not +discriminated, so that the term {\em alias\/} is not quite appropriate +for them and we do not use it in this document. + +The \verb|ip addr| command displays addresses and their properties, +adds new addresses and deletes old ones. + +\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|flush| and \verb|show| +(or \verb|list|). + + +\subsection{{\tt ip address add} --- add a new protocol address} +\label{IP-ADDR-ADD} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|dev NAME| + +\noindent--- the name of the device to add the address to. + +\item \verb|local ADDRESS| (default) + +--- the address of the interface. The format of the address depends +on the protocol. It is a dotted quad for IP and a sequence of hexadecimal halfwords +separated by colons for IPv6. The \verb|ADDRESS| may be followed by +a slash and a decimal number which encodes the network prefix length. + + +\item \verb|peer ADDRESS| + +--- the address of the remote endpoint for pointopoint interfaces. +Again, the \verb|ADDRESS| may be followed by a slash and a decimal number, +encoding the network prefix length. If a peer address is specified, +the local address {\em cannot\/} have a prefix length. The network prefix is associated +with the peer rather than with the local address. + + +\item \verb|broadcast ADDRESS| + +--- the broadcast address on the interface. + +It is possible to use the special symbols \verb|'+'| and \verb|'-'| +instead of the broadcast address. In this case, the broadcast address +is derived by setting/resetting the host bits of the interface prefix. + +\vskip 1mm +\begin{NB} +Unlike \verb|ifconfig|, the \verb|ip| utility {\em does not\/} set any broadcast +address unless explicitly requested. +\end{NB} + + +\item \verb|label NAME| + +--- Each address may be tagged with a label string. +In order to preserve compatibility with Linux-2.0 net aliases, +this string must coincide with the name of the device or must be prefixed +with the device name followed by colon. + + +\item \verb|scope SCOPE_VALUE| + +--- the scope of the area where this address is valid. +The available scopes are listed in file \verb|/etc/iproute2/rt_scopes|. +Predefined scope values are: + + \begin{itemize} + \item \verb|global| --- the address is globally valid. + \item \verb|site| --- (IPv6 only) the address is site local, + i.e.\ it is valid inside this site. + \item \verb|link| --- the address is link local, i.e.\ + it is valid only on this device. + \item \verb|host| --- the address is valid only inside this host. + \end{itemize} + +Appendix~\ref{ADDR-SEL} (p.\pageref{ADDR-SEL} of this document) +contains more details on address scopes. + +\end{itemize} + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip addr add 127.0.0.1/8 dev lo brd + scope host| + +--- add the usual loopback address to the loopback device. + +\item \verb|ip addr add 10.0.0.1/24 brd + dev eth0 label eth0:Alias| + +--- add the address 10.0.0.1 with prefix length 24 (i.e.\ netmask +\verb|255.255.255.0|), standard broadcast and label \verb|eth0:Alias| +to the interface \verb|eth0|. +\end{itemize} + + +\subsection{{\tt ip address delete} --- delete a protocol address} + +\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Arguments:} coincide with the arguments of \verb|ip addr add|. +The device name is a required argument. The rest are optional. +If no arguments are given, the first address is deleted. + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip addr del 127.0.0.1/8 dev lo| + +--- deletes the loopback address from the loopback device. +It would be best not to repeat this experiment. + +\item Disable IP on the interface \verb|eth0|: +\begin{verbatim} + while ip -f inet addr del dev eth0; do + : nothing + done +\end{verbatim} +Another method to disable IP on an interface using {\tt ip addr flush} +may be found in sec.\ref{IP-ADDR-FLUSH}, p.\pageref{IP-ADDR-FLUSH}. + +\end{itemize} + + +\subsection{{\tt ip address show} --- display protocol addresses} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|lst|, \verb|sh|, \verb|ls|, +\verb|l|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|dev NAME| (default) + +--- the name of the device. + +\item \verb|scope SCOPE_VAL| + +--- only list addresses with this scope. + +\item \verb|to PREFIX| + +--- only list addresses matching this prefix. + +\item \verb|label PATTERN| + +--- only list addresses with labels matching the \verb|PATTERN|. +\verb|PATTERN| is a usual shell style pattern. + + +\item \verb|dynamic| and \verb|permanent| + +--- (IPv6 only) only list addresses installed due to stateless +address configuration or only list permanent (not dynamic) addresses. + +\item \verb|tentative| + +--- (IPv6 only) only list addresses which did not pass duplicate +address detection. + +\item \verb|deprecated| + +--- (IPv6 only) only list deprecated addresses. + + +\item \verb|primary| and \verb|secondary| + +--- only list primary (or secondary) addresses. + +\end{itemize} + + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip addr ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff + inet 193.233.7.90/24 brd 193.233.7.255 scope global eth0 + inet6 3ffe:2400:0:1:2a0:ccff:fe66:1878/64 scope global dynamic + valid_lft forever preferred_lft 604746sec + inet6 fe80::2a0:ccff:fe66:1878/10 scope link +kuznet@alisa:~ $ +\end{verbatim} + +The first two lines coincide with the output of \verb|ip link ls|. +It is natural to interpret link layer addresses +as addresses of the protocol family \verb|AF_PACKET|. + +Then the list of IP and IPv6 addresses follows, accompanied by +additional address attributes: scope value (see Sec.\ref{IP-ADDR-ADD}, +p.\pageref{IP-ADDR-ADD} above), flags and the address label. + +Address flags are set by the kernel and cannot be changed +administratively. Currently, the following flags are defined: + +\begin{enumerate} +\item \verb|secondary| + +--- the address is not used when selecting the default source address +of outgoing packets (Cf.\ Appendix~\ref{ADDR-SEL}, p.\pageref{ADDR-SEL}.). +An IP address becomes secondary if another address with the same +prefix bits already exists. The first address is primary. +It is the leader of the group of all secondary addresses. When the leader +is deleted, all secondaries are purged too. + + +\item \verb|dynamic| + +--- the address was created due to stateless autoconfiguration~\cite{RFC-ADDRCONF}. +In this case the output also contains information on times, when +the address is still valid. After \verb|preferred_lft| expires the address is +moved to the deprecated state. After \verb|valid_lft| expires the address +is finally invalidated. + +\item \verb|deprecated| + +--- the address is deprecated, i.e.\ it is still valid, but cannot +be used by newly created connections. + +\item \verb|tentative| + +--- the address is not used because duplicate address detection~\cite{RFC-ADDRCONF} +is still not complete or failed. + +\end{enumerate} + + +\subsection{{\tt ip address flush} --- flush protocol addresses} +\label{IP-ADDR-FLUSH} + +\paragraph{Abbreviations:} \verb|flush|, \verb|f|. + +\paragraph{Description:}This command flushes the protocol addresses +selected by some criteria. + +\paragraph{Arguments:} This command has the same arguments as \verb|show|. +The difference is that it does not run when no arguments are given. + +\paragraph{Warning:} This command (and other \verb|flush| commands +described below) is pretty dangerous. If you make a mistake, it will +not forgive it, but will cruelly purge all the addresses. + +\paragraph{Statistics:} With the \verb|-statistics| option, the command +becomes verbose. It prints out the number of deleted addresses and the number +of rounds made to flush the address list. If this option is given +twice, \verb|ip addr flush| also dumps all the deleted addresses +in the format described in the previous subsection. + +\paragraph{Example:} Delete all the addresses from the private network +10.0.0.0/8: +\begin{verbatim} +netadm@amber:~ # ip -s -s a f to 10/8 +2: dummy inet 10.7.7.7/16 brd 10.7.255.255 scope global dummy +3: eth0 inet 10.10.7.7/16 brd 10.10.255.255 scope global eth0 +4: eth1 inet 10.8.7.7/16 brd 10.8.255.255 scope global eth1 + +*** Round 1, deleting 3 addresses *** +*** Flush is complete after 1 round *** +netadm@amber:~ # +\end{verbatim} +Another instructive example is disabling IP on all the Ethernets: +\begin{verbatim} +netadm@amber:~ # ip -4 addr flush label "eth*" +\end{verbatim} +And the last example shows how to flush all the IPv6 addresses +acquired by the host from stateless address autoconfiguration +after you enabled forwarding or disabled autoconfiguration. +\begin{verbatim} +netadm@amber:~ # ip -6 addr flush dynamic +\end{verbatim} + + + +\section{{\tt ip neighbour} --- neighbour/arp tables management} + +\paragraph{Abbreviations:} \verb|neighbour|, \verb|neighbor|, \verb|neigh|, +\verb|n|. + +\paragraph{Object:} \verb|neighbour| objects establish bindings between protocol +addresses and link layer addresses for hosts sharing the same link. +Neighbour entries are organized into tables. The IPv4 neighbour table +is known by another name --- the ARP table. + +The corresponding commands display neighbour bindings +and their properties, add new neighbour entries and delete old ones. + +\paragraph{Commands:} \verb|add|, \verb|change|, \verb|replace|, +\verb|delete|, \verb|flush| and \verb|show| (or \verb|list|). + +\paragraph{See also:} Appendix~\ref{PROXY-NEIGH}, p.\pageref{PROXY-NEIGH} +describes how to manage proxy ARP/NDISC with the \verb|ip| utility. + + +\subsection{{\tt ip neighbour add} --- add a new neighbour entry\\ + {\tt ip neighbour change} --- change an existing entry\\ + {\tt ip neighbour replace} --- add a new entry or change an existing one} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|; +\verb|replace|, \verb|repl|. + +\paragraph{Description:} These commands create new neighbour records +or update existing ones. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|to ADDRESS| (default) + +--- the protocol address of the neighbour. It is either an IPv4 or IPv6 address. + +\item \verb|dev NAME| + +--- the interface to which this neighbour is attached. + + +\item \verb|lladdr LLADDRESS| + +--- the link layer address of the neighbour. \verb|LLADDRESS| can also be +\verb|null|. + +\item \verb|nud NUD_STATE| + +--- the state of the neighbour entry. \verb|nud| is an abbreviation for ``Neighbour +Unreachability Detection''. The state can take one of the following values: + +\begin{enumerate} +\item \verb|permanent| --- the neighbour entry is valid forever and can be only be removed +administratively. +\item \verb|noarp| --- the neighbour entry is valid. No attempts to validate +this entry will be made but it can be removed when its lifetime expires. +\item \verb|reachable| --- the neighbour entry is valid until the reachability +timeout expires. +\item \verb|stale| --- the neighbour entry is valid but suspicious. +This option to \verb|ip neigh| does not change the neighbour state if +it was valid and the address is not changed by this command. +\end{enumerate} + +\end{itemize} + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip neigh add 10.0.0.3 lladdr 0:0:0:0:0:1 dev eth0 nud perm| + +--- add a permanent ARP entry for the neighbour 10.0.0.3 on the device \verb|eth0|. + +\item \verb|ip neigh chg 10.0.0.3 dev eth0 nud reachable| + +--- change its state to \verb|reachable|. +\end{itemize} + + +\subsection{{\tt ip neighbour delete} --- delete a neighbour entry} + +\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Description:} This command invalidates a neighbour entry. + +\paragraph{Arguments:} The arguments are the same as with \verb|ip neigh add|, +except that \verb|lladdr| and \verb|nud| are ignored. + + +\paragraph{Example:} +\begin{itemize} +\item \verb|ip neigh del 10.0.0.3 dev eth0| + +--- invalidate an ARP entry for the neighbour 10.0.0.3 on the device \verb|eth0|. + +\end{itemize} + +\begin{NB} + The deleted neighbour entry will not disappear from the tables + immediately. If it is in use it cannot be deleted until the last + client releases it. Otherwise it will be destroyed during + the next garbage collection. +\end{NB} + + +\paragraph{Warning:} Attempts to delete or manually change +a \verb|noarp| entry created by the kernel may result in unpredictable behaviour. +Particularly, the kernel may try to resolve this address even +on a \verb|NOARP| interface or if the address is multicast or broadcast. + + +\subsection{{\tt ip neighbour show} --- list neighbour entries} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|. + +\paragraph{Description:}This commands displays neighbour tables. + +\paragraph{Arguments:} + +\begin{itemize} + +\item \verb|to ADDRESS| (default) + +--- the prefix selecting the neighbours to list. + +\item \verb|dev NAME| + +--- only list the neighbours attached to this device. + +\item \verb|unused| + +--- only list neighbours which are not currently in use. + +\item \verb|nud NUD_STATE| + +--- only list neighbour entries in this state. \verb|NUD_STATE| takes +values listed below or the special value \verb|all| which means all states. +This option may occur more than once. If this option is absent, \verb|ip| +lists all entries except for \verb|none| and \verb|noarp|. + +\end{itemize} + + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip neigh ls +:: dev lo lladdr 00:00:00:00:00:00 nud noarp +fe80::200:cff:fe76:3f85 dev eth0 lladdr 00:00:0c:76:3f:85 router \ + nud stale +0.0.0.0 dev lo lladdr 00:00:00:00:00:00 nud noarp +193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 nud reachable +193.233.7.85 dev eth0 lladdr 00:e0:1e:63:39:00 nud stale +kuznet@alisa:~ $ +\end{verbatim} + +The first word of each line is the protocol address of the neighbour. +Then the device name follows. The rest of the line describes the contents of +the neighbour entry identified by the pair (device, address). + +\verb|lladdr| is the link layer address of the neighbour. + +\verb|nud| is the state of the ``neighbour unreachability detection'' machine +for this entry. The detailed description of the neighbour +state machine can be found in~\cite{RFC-NDISC}. Here is the full list +of the states with short descriptions: + +\begin{enumerate} +\item\verb|none| --- the state of the neighbour is void. +\item\verb|incomplete| --- the neighbour is in the process of resolution. +\item\verb|reachable| --- the neighbour is valid and apparently reachable. +\item\verb|stale| --- the neighbour is valid, but is probably already +unreachable, so the kernel will try to check it at the first transmission. +\item\verb|delay| --- a packet has been sent to the stale neighbour and the kernel is waiting +for confirmation. +\item\verb|probe| --- the delay timer expired but no confirmation was received. +The kernel has started to probe the neighbour with ARP/NDISC messages. +\item\verb|failed| --- resolution has failed. +\item\verb|noarp| --- the neighbour is valid. No attempts to check the entry +will be made. +\item\verb|permanent| --- it is a \verb|noarp| entry, but only the administrator +may remove the entry from the neighbour table. +\end{enumerate} + +The link layer address is valid in all states except for \verb|none|, +\verb|failed| and \verb|incomplete|. + +IPv6 neighbours can be marked with the additional flag \verb|router| +which means that the neighbour introduced itself as an IPv6 router~\cite{RFC-NDISC}. + +\paragraph{Statistics:} The \verb|-statistics| option displays some usage +statistics, f.e.\ + +\begin{verbatim} +kuznet@alisa:~ $ ip -s n ls 193.233.7.254 +193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 ref 5 used 12/13/20 \ + nud reachable +kuznet@alisa:~ $ +\end{verbatim} + +Here \verb|ref| is the number of users of this entry +and \verb|used| is a triplet of time intervals in seconds +separated by slashes. In this case they show that: + +\begin{enumerate} +\item the entry was used 12 seconds ago. +\item the entry was confirmed 13 seconds ago. +\item the entry was updated 20 seconds ago. +\end{enumerate} + +\subsection{{\tt ip neighbour flush} --- flush neighbour entries} + +\paragraph{Abbreviations:} \verb|flush|, \verb|f|. + +\paragraph{Description:}This command flushes neighbour tables, selecting +entries to flush by some criteria. + +\paragraph{Arguments:} This command has the same arguments as \verb|show|. +The differences are that it does not run when no arguments are given, +and that the default neighbour states to be flushed do not include +\verb|permanent| and \verb|noarp|. + + +\paragraph{Statistics:} With the \verb|-statistics| option, the command +becomes verbose. It prints out the number of deleted neighbours and the number +of rounds made to flush the neighbour table. If the option is given +twice, \verb|ip neigh flush| also dumps all the deleted neighbours +in the format described in the previous subsection. + +\paragraph{Example:} +\begin{verbatim} +netadm@alisa:~ # ip -s -s n f 193.233.7.254 +193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 ref 5 used 12/13/20 \ + nud reachable + +*** Round 1, deleting 1 entries *** +*** Flush is complete after 1 round *** +netadm@alisa:~ # +\end{verbatim} + + +\section{{\tt ip route} --- routing table management} +\label{IP-ROUTE} + +\paragraph{Abbreviations:} \verb|route|, \verb|ro|, \verb|r|. + +\paragraph{Object:} \verb|route| entries in the kernel routing tables keep +information about paths to other networked nodes. + +Each route entry has a {\em key\/} consisting of a {\em prefix\/} +(i.e.\ a pair containing a network address and the length of its mask) and, +optionally, the TOS value. An IP packet matches the route if the highest +bits of its destination address are equal to the route prefix at least +up to the prefix length and if the TOS of the route is zero or equal to +the TOS of the packet. + +If several routes match the packet, the following pruning rules +are used to select the best one (see~\cite{RFC1812}): +\begin{enumerate} +\item The longest matching prefix is selected. All shorter ones +are dropped. + +\item If the TOS of some route with the longest prefix is equal to the TOS +of the packet, the routes with different TOS are dropped. + +If no exact TOS match was found and routes with TOS=0 exist, +the rest of routes are pruned. + +Otherwise, the route lookup fails. + +\item If several routes remain after the previous steps, then +the routes with the best preference values are selected. + +\item If we still have several routes, then the {\em first\/} of them +is selected. + +\begin{NB} + Note the ambiguity of the last step. Unfortunately, Linux + historically allows such a bizarre situation. The sense of the +word ``first'' depends on the order of route additions and it is practically +impossible to maintain a bundle of such routes in this order. +\end{NB} + +For simplicity we will limit ourselves to the case where such a situation +is impossible and routes are uniquely identified by the triplet +\{prefix, tos, preference\}. Actually, it is impossible to create +non-unique routes with \verb|ip| commands described in this section. + +One useful exception to this rule is the default route on non-forwarding +hosts. It is ``officially'' allowed to have several fallback routes +when several routers are present on directly connected networks. +In this case, Linux-2.2 makes ``dead gateway detection''~\cite{RFC1122} +controlled by neighbour unreachability detection and by advice +from transport protocols to select a working router, so the order +of the routes is not essential. However, in this case, +fiddling with default routes manually is not recommended. Use the Router Discovery +protocol (see Appendix~\ref{EXAMPLE-SETUP}, p.\pageref{EXAMPLE-SETUP}) +instead. Actually, Linux-2.2 IPv6 does not give user level applications +any access to default routes. +\end{enumerate} + +Certainly, the steps above are not performed exactly +in this sequence. Instead, the routing table in the kernel is kept +in some data structure to achieve the final result +with minimal cost. However, not depending on a particular +routing algorithm implemented in the kernel, we can summarize +the statements above as: a route is identified by the triplet +\{prefix, tos, preference\}. This {\em key\/} lets us locate +the route in the routing table. + +\paragraph{Route attributes:} Each route key refers to a routing +information record containing +the data required to deliver IP packets (f.e.\ output device and +next hop router) and some optional attributes (f.e. the path MTU or +the preferred source address when communicating with this destination). +These attributes are described in the following subsection. + +\paragraph{Route types:} \label{IP-ROUTE-TYPES} +It is important that the set +of required and optional attributes depend on the route {\em type\/}. +The most important route type +is \verb|unicast|. It describes real paths to other hosts. +As a rule, common routing tables contain only such routes. However, +there are other types of routes with different semantics. The +full list of types understood by Linux-2.2 is: +\begin{itemize} +\item \verb|unicast| --- the route entry describes real paths to the +destinations covered by the route prefix. +\item \verb|unreachable| --- these destinations are unreachable. Packets +are discarded and the ICMP message {\em host unreachable\/} is generated. +The local senders get an \verb|EHOSTUNREACH| error. +\item \verb|blackhole| --- these destinations are unreachable. Packets +are discarded silently. The local senders get an \verb|EINVAL| error. +\item \verb|prohibit| --- these destinations are unreachable. Packets +are discarded and the ICMP message {\em communication administratively +prohibited\/} is generated. The local senders get an \verb|EACCES| error. +\item \verb|local| --- the destinations are assigned to this +host. The packets are looped back and delivered locally. +\item \verb|broadcast| --- the destinations are broadcast addresses. +The packets are sent as link broadcasts. +\item \verb|throw| --- a special control route used together with policy +rules (see sec.\ref{IP-RULE}, p.\pageref{IP-RULE}). If such a route is selected, lookup +in this table is terminated pretending that no route was found. +Without policy routing it is equivalent to the absence of the route in the routing +table. The packets are dropped and the ICMP message {\em net unreachable\/} +is generated. The local senders get an \verb|ENETUNREACH| error. +\item \verb|nat| --- a special NAT route. Destinations covered by the prefix +are considered to be dummy (or external) addresses which require translation +to real (or internal) ones before forwarding. The addresses to translate to +are selected with the attribute \verb|via|. More about NAT is +in Appendix~\ref{ROUTE-NAT}, p.\pageref{ROUTE-NAT}. +\item \verb|anycast| --- ({\em not implemented\/}) the destinations are +{\em anycast\/} addresses assigned to this host. They are mainly equivalent +to \verb|local| with one difference: such addresses are invalid when used +as the source address of any packet. +\item \verb|multicast| --- a special type used for multicast routing. +It is not present in normal routing tables. +\end{itemize} + +\paragraph{Route tables:} Linux-2.2 can pack routes into several routing +tables identified by a number in the range from 1 to 255 or by +name from the file \verb|/etc/iproute2/rt_tables|. By default all normal +routes are inserted into the \verb|main| table (ID 254) and the kernel only uses +this table when calculating routes. + +Actually, one other table always exists, which is invisible but +even more important. It is the \verb|local| table (ID 255). This table +consists of routes for local and broadcast addresses. The kernel maintains +this table automatically and the administrator usually need not modify it +or even look at it. + +The multiple routing tables enter the game when {\em policy routing\/} +is used. See sec.\ref{IP-RULE}, p.\pageref{IP-RULE}. +In this case, the table identifier effectively becomes +one more parameter, which should be added to the triplet +\{prefix, tos, preference\} to uniquely identify the route. + + +\subsection{{\tt ip route add} --- add a new route\\ + {\tt ip route change} --- change a route\\ + {\tt ip route replace} --- change a route or add a new one} +\label{IP-ROUTE-ADD} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|; + \verb|replace|, \verb|repl|. + + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|to PREFIX| or \verb|to TYPE PREFIX| (default) + +--- the destination prefix of the route. If \verb|TYPE| is omitted, +\verb|ip| assumes type \verb|unicast|. Other values of \verb|TYPE| +are listed above. \verb|PREFIX| is an IP or IPv6 address optionally followed +by a slash and the prefix length. If the length of the prefix is missing, +\verb|ip| assumes a full-length host route. There is also a special +\verb|PREFIX| --- \verb|default| --- which is equivalent to IP \verb|0/0| or +to IPv6 \verb|::/0|. + +\item \verb|tos TOS| or \verb|dsfield TOS| + +--- the Type Of Service (TOS) key. This key has no associated mask and +the longest match is understood as: First, compare the TOS +of the route and of the packet. If they are not equal, then the packet +may still match a route with a zero TOS. \verb|TOS| is either an 8 bit hexadecimal +number or an identifier from {\tt /etc/iproute2/rt\_dsfield}. + + +\item \verb|metric NUMBER| or \verb|preference NUMBER| + +--- the preference value of the route. \verb|NUMBER| is an arbitrary 32bit number. + +\item \verb|table TABLEID| + +--- the table to add this route to. +\verb|TABLEID| may be a number or a string from the file +\verb|/etc/iproute2/rt_tables|. If this parameter is omitted, +\verb|ip| assumes the \verb|main| table, with the exception of +\verb|local|, \verb|broadcast| and \verb|nat| routes, which are +put into the \verb|local| table by default. + +\item \verb|dev NAME| + +--- the output device name. + +\item \verb|via ADDRESS| + +--- the address of the nexthop router. Actually, the sense of this field depends +on the route type. For normal \verb|unicast| routes it is either the true nexthop +router or, if it is a direct route installed in BSD compatibility mode, +it can be a local address of the interface. +For NAT routes it is the first address of the block of translated IP destinations. + +\item \verb|src ADDRESS| + +--- the source address to prefer when sending to the destinations +covered by the route prefix. + +\item \verb|realm REALMID| + +--- the realm to which this route is assigned. +\verb|REALMID| may be a number or a string from the file +\verb|/etc/iproute2/rt_realms|. Sec.\ref{RT-REALMS} (p.\pageref{RT-REALMS}) +contains more information on realms. + +\item \verb|mtu MTU| or \verb|mtu lock MTU| + +--- the MTU along the path to the destination. If the modifier \verb|lock| is +not used, the MTU may be updated by the kernel due to Path MTU Discovery. +If the modifier \verb|lock| is used, no path MTU discovery will be tried, +all packets will be sent without the DF bit in IPv4 case +or fragmented to MTU for IPv6. + +\item \verb|window NUMBER| + +--- the maximal window for TCP to advertise to these destinations, +measured in bytes. It limits maximal data bursts that our TCP +peers are allowed to send to us. + +\item \verb|rtt NUMBER| + +--- the initial RTT (``Round Trip Time'') estimate. + + +\item \verb|rttvar NUMBER| + +--- \threeonly the initial RTT variance estimate. + + +\item \verb|ssthresh NUMBER| + +--- \threeonly an estimate for the initial slow start threshold. + + +\item \verb|cwnd NUMBER| + +--- \threeonly the clamp for congestion window. It is ignored if the \verb|lock| + flag is not used. + + +\item \verb|advmss NUMBER| + +--- \threeonly the MSS (``Maximal Segment Size'') to advertise to these + destinations when establishing TCP connections. If it is not given, + Linux uses a default value calculated from the first hop device MTU. + +\begin{NB} + If the path to these destination is asymmetric, this guess may be wrong. +\end{NB} + +\item \verb|reordering NUMBER| + +--- \threeonly Maximal reordering on the path to this destination. + If it is not given, Linux uses the value selected with \verb|sysctl| + variable \verb|net/ipv4/tcp_reordering|. + + + +\item \verb|nexthop NEXTHOP| + +--- the nexthop of a multipath route. \verb|NEXTHOP| is a complex value +with its own syntax similar to the top level argument lists: +\begin{itemize} +\item \verb|via ADDRESS| is the nexthop router. +\item \verb|dev NAME| is the output device. +\item \verb|weight NUMBER| is a weight for this element of a multipath +route reflecting its relative bandwidth or quality. +\end{itemize} + +\item \verb|scope SCOPE_VAL| + +--- the scope of the destinations covered by the route prefix. +\verb|SCOPE_VAL| may be a number or a string from the file +\verb|/etc/iproute2/rt_scopes|. +If this parameter is omitted, +\verb|ip| assumes scope \verb|global| for all gatewayed \verb|unicast| +routes, scope \verb|link| for direct \verb|unicast| and \verb|broadcast| routes +and scope \verb|host| for \verb|local| routes. + +\item \verb|protocol RTPROTO| + +--- the routing protocol identifier of this route. +\verb|RTPROTO| may be a number or a string from the file +\verb|/etc/iproute2/rt_protos|. If the routing protocol ID is +not given, \verb|ip| assumes protocol \verb|boot| (i.e.\ +it assumes the route was added by someone who doesn't +understand what they are doing). Several protocol values have a fixed interpretation. +Namely: +\begin{itemize} +\item \verb|redirect| --- the route was installed due to an ICMP redirect. +\item \verb|kernel| --- the route was installed by the kernel during +autoconfiguration. +\item \verb|boot| --- the route was installed during the bootup sequence. +If a routing daemon starts, it will purge all of them. +\item \verb|static| --- the route was installed by the administrator +to override dynamic routing. Routing daemon will respect them +and, probably, even advertise them to its peers. +\item \verb|ra| --- the route was installed by Router Discovery protocol. +\end{itemize} +The rest of the values are not reserved and the administrator is free +to assign (or not to assign) protocol tags. At least, routing +daemons should take care of setting some unique protocol values, +f.e.\ as they are assigned in \verb|rtnetlink.h| or in \verb|rt_protos| +database. + + +\item \verb|onlink| + +--- pretend that the nexthop is directly attached to this link, +even if it does not match any interface prefix. One application of this +option may be found in~\cite{IP-TUNNELS}. + +\item \verb|equalize| + +--- allow packet by packet randomization on multipath routes. +Without this modifier, the route will be frozen to one selected +nexthop, so that load splitting will only occur on per-flow base. +\verb|equalize| only works if the kernel is patched. + + +\end{itemize} + + +\begin{NB} + Actually there are more commands: \verb|prepend| does the same + thing as classic \verb|route add|, i.e.\ adds a route, even if another + route to the same destination exists. Its opposite case is \verb|append|, + which adds the route to the end of the list. Avoid these + features. +\end{NB} +\begin{NB} + More sad news, IPv6 only understands the \verb|append| command correctly. + All the others are translated into \verb|append| commands. Certainly, + this will change in the future. +\end{NB} + +\paragraph{Examples:} +\begin{itemize} +\item add a plain route to network 10.0.0/24 via gateway 193.233.7.65 +\begin{verbatim} + ip route add 10.0.0/24 via 193.233.7.65 +\end{verbatim} +\item change it to a direct route via the \verb|dummy| device +\begin{verbatim} + ip ro chg 10.0.0/24 dev dummy +\end{verbatim} +\item add a default multipath route splitting the load between \verb|ppp0| +and \verb|ppp1| +\begin{verbatim} + ip route add default scope global nexthop dev ppp0 \ + nexthop dev ppp1 +\end{verbatim} +Note the scope value. It is not necessary but it informs the kernel +that this route is gatewayed rather than direct. Actually, if you +know the addresses of remote endpoints it would be better to use the +\verb|via| parameter. +\item announce that the address 192.203.80.144 is not a real one, but +should be translated to 193.233.7.83 before forwarding +\begin{verbatim} + ip route add nat 192.203.80.144 via 193.233.7.83 +\end{verbatim} +Backward translation is setup with policy rules described +in the following section (sec.\ref{IP-RULE}, p.\pageref{IP-RULE}). +\end{itemize} + +\subsection{{\tt ip route delete} --- delete a route} + +\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Arguments:} \verb|ip route del| has the same arguments as +\verb|ip route add|, but their semantics are a bit different. + +Key values (\verb|to|, \verb|tos|, \verb|preference| and \verb|table|) +select the route to delete. If optional attributes are present, \verb|ip| +verifies that they coincide with the attributes of the route to delete. +If no route with the given key and attributes was found, \verb|ip route del| +fails. +\begin{NB} +Linux-2.0 had the option to delete a route selected only by prefix address, +ignoring its length (i.e.\ netmask). This option no longer exists +because it was ambiguous. However, look at {\tt ip route flush} +(sec.\ref{IP-ROUTE-FLUSH}, p.\pageref{IP-ROUTE-FLUSH}) which +provides similar and even richer functionality. +\end{NB} + +\paragraph{Example:} +\begin{itemize} +\item delete the multipath route created by the command in previous subsection +\begin{verbatim} + ip route del default scope global nexthop dev ppp0 \ + nexthop dev ppp1 +\end{verbatim} +\end{itemize} + + + +\subsection{{\tt ip route show} --- list routes} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + +\paragraph{Description:} the command displays the contents of the routing tables +or the route(s) selected by some criteria. + + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|to SELECTOR| (default) + +--- only select routes from the given range of destinations. \verb|SELECTOR| +consists of an optional modifier (\verb|root|, \verb|match| or \verb|exact|) +and a prefix. \verb|root PREFIX| selects routes with prefixes not shorter +than \verb|PREFIX|. F.e.\ \verb|root 0/0| selects the entire routing table. +\verb|match PREFIX| selects routes with prefixes not longer than +\verb|PREFIX|. F.e.\ \verb|match 10.0/16| selects \verb|10.0/16|, +\verb|10/8| and \verb|0/0|, but it does not select \verb|10.1/16| and +\verb|10.0.0/24|. And \verb|exact PREFIX| (or just \verb|PREFIX|) +selects routes with this exact prefix. If neither of these options +are present, \verb|ip| assumes \verb|root 0/0| i.e.\ it lists the entire table. + + +\item \verb|tos TOS| or \verb|dsfield TOS| + + --- only select routes with the given TOS. + + +\item \verb|table TABLEID| + + --- show the routes from this table(s). The default setting is to show +\verb|table| \verb|main|. \verb|TABLEID| may either be the ID of a real table +or one of the special values: + \begin{itemize} + \item \verb|all| --- list all of the tables. + \item \verb|cache| --- dump the routing cache. + \end{itemize} +\begin{NB} + IPv6 has a single table. However, splitting it into \verb|main|, \verb|local| + and \verb|cache| is emulated by the \verb|ip| utility. +\end{NB} + +\item \verb|cloned| or \verb|cached| + +--- list cloned routes i.e.\ routes which were dynamically forked from +other routes because some route attribute (f.e.\ MTU) was updated. +Actually, it is equivalent to \verb|table cache|. + +\item \verb|from SELECTOR| + +--- the same syntax as for \verb|to|, but it binds the source address range +rather than destinations. Note that the \verb|from| option only works with +cloned routes. + +\item \verb|protocol RTPROTO| + +--- only list routes of this protocol. + + +\item \verb|scope SCOPE_VAL| + +--- only list routes with this scope. + +\item \verb|type TYPE| + +--- only list routes of this type. + +\item \verb|dev NAME| + +--- only list routes going via this device. + +\item \verb|via PREFIX| + +--- only list routes going via the nexthop routers selected by \verb|PREFIX|. + +\item \verb|src PREFIX| + +--- only list routes with preferred source addresses selected +by \verb|PREFIX|. + +\item \verb|realm REALMID| or \verb|realms FROMREALM/TOREALM| + +--- only list routes with these realms. + +\end{itemize} + +\paragraph{Examples:} Let us count routes of protocol \verb|gated/bgp| +on a router: +\begin{verbatim} +kuznet@amber:~ $ ip ro ls proto gated/bgp | wc + 1413 9891 79010 +kuznet@amber:~ $ +\end{verbatim} +To count the size of the routing cache, we have to use the \verb|-o| option +because cached attributes can take more than one line of output: +\begin{verbatim} +kuznet@amber:~ $ ip -o ro ls cloned | wc + 159 2543 18707 +kuznet@amber:~ $ +\end{verbatim} + + +\paragraph{Output format:} The output of this command consists +of per route records separated by line feeds. +However, some records may consist +of more than one line: particularly, this is the case when the route +is cloned or you requested additional statistics. If the +\verb|-o| option was given, then line feeds separating lines inside +records are replaced with the backslash sign. + +The output has the same syntax as arguments given to {\tt ip route add}, +so that it can be understood easily. F.e.\ +\begin{verbatim} +kuznet@amber:~ $ ip ro ls 193.233.7/24 +193.233.7.0/24 dev eth0 proto gated/conn scope link \ + src 193.233.7.65 realms inr.ac +kuznet@amber:~ $ +\end{verbatim} + +If you list cloned entries, the output contains other attributes which +are evaluated during route calculation and updated during route +lifetime. An example of the output is: +\begin{verbatim} +kuznet@amber:~ $ ip ro ls 193.233.7.82 tab cache +193.233.7.82 from 193.233.7.82 dev eth0 src 193.233.7.65 \ + realms inr.ac/inr.ac + cache <src-direct,redirect> mtu 1500 rtt 300 iif eth0 +193.233.7.82 dev eth0 src 193.233.7.65 realms inr.ac + cache mtu 1500 rtt 300 +kuznet@amber:~ $ +\end{verbatim} +\begin{NB} + \label{NB-strange-route} + The route looks a bit strange, doesn't it? Did you notice that + it is a path from 193.233.7.82 back to 193.233.82? Well, you will + see in the section on \verb|ip route get| (p.\pageref{NB-nature-of-strangeness}) + how it appeared. +\end{NB} +The second line, starting with the word \verb|cache|, shows +additional attributes which normal routes do not possess. +Cached flags are summarized in angle brackets: +\begin{itemize} +\item \verb|local| --- packets are delivered locally. +It stands for loopback unicast routes, for broadcast routes +and for multicast routes, if this host is a member of the corresponding +group. + +\item \verb|reject| --- the path is bad. Any attempt to use it results +in an error. See attribute \verb|error| below (p.\pageref{IP-ROUTE-GET-error}). + +\item \verb|mc| --- the destination is multicast. + +\item \verb|brd| --- the destination is broadcast. + +\item \verb|src-direct| --- the source is on a directly connected +interface. + +\item \verb|redirected| --- the route was created by an ICMP Redirect. + +\item \verb|redirect| --- packets going via this route will +trigger an ICMP redirect. + +\item \verb|fastroute| --- the route is eligible to be used for fastroute. + +\item \verb|equalize| --- make packet by packet randomization +along this path. + +\item \verb|dst-nat| --- the destination address requires translation. + +\item \verb|src-nat| --- the source address requires translation. + +\item \verb|masq| --- the source address requires masquerading. +This feature disappeared in linux-2.4. + +\item \verb|notify| --- ({\em not implemented}) change/deletion +of this route will trigger RTNETLINK notification. +\end{itemize} + +Then some optional attributes follow: +\begin{itemize} +\item \verb|error| --- on \verb|reject| routes it is error code +returned to local senders when they try to use this route. +These error codes are translated into ICMP error codes, sent to remote +senders, according to the rules described above in the subsection +devoted to route types (p.\pageref{IP-ROUTE-TYPES}). +\label{IP-ROUTE-GET-error} + +\item \verb|expires| --- this entry will expire after this timeout. + +\item \verb|iif| --- the packets for this path are expected to arrive +on this interface. +\end{itemize} + +\paragraph{Statistics:} With the \verb|-statistics| option, more +information about this route is shown: +\begin{itemize} +\item \verb|users| --- the number of users of this entry. +\item \verb|age| --- shows when this route was last used. +\item \verb|used| --- the number of lookups of this route since its creation. +\end{itemize} + + +\subsection{{\tt ip route flush} --- flush routing tables} +\label{IP-ROUTE-FLUSH} + +\paragraph{Abbreviations:} \verb|flush|, \verb|f|. + +\paragraph{Description:} this command flushes routes selected +by some criteria. + +\paragraph{Arguments:} the arguments have the same syntax and semantics +as the arguments of \verb|ip route show|, but routing tables are not +listed but purged. The only difference is the default action: \verb|show| +dumps all the IP main routing table but \verb|flush| prints the helper page. +The reason for this difference does not require any explanation, does it? + + +\paragraph{Statistics:} With the \verb|-statistics| option, the command +becomes verbose. It prints out the number of deleted routes and the number +of rounds made to flush the routing table. If the option is given +twice, \verb|ip route flush| also dumps all the deleted routes +in the format described in the previous subsection. + +\paragraph{Examples:} The first example flushes all the +gatewayed routes from the main table (f.e.\ after a routing daemon crash). +\begin{verbatim} +netadm@amber:~ # ip -4 ro flush scope global type unicast +\end{verbatim} +This option deserves to be put into a scriptlet \verb|routef|. +\begin{NB} +This option was described in the \verb|route(8)| man page borrowed +from BSD, but was never implemented in Linux. +\end{NB} + +The second example flushes all IPv6 cloned routes: +\begin{verbatim} +netadm@amber:~ # ip -6 -s -s ro flush cache +3ffe:2400::220:afff:fef4:c5d1 via 3ffe:2400::220:afff:fef4:c5d1 \ + dev eth0 metric 0 + cache used 2 age 12sec mtu 1500 rtt 300 +3ffe:2400::280:adff:feb7:8034 via 3ffe:2400::280:adff:feb7:8034 \ + dev eth0 metric 0 + cache used 2 age 15sec mtu 1500 rtt 300 +3ffe:2400::280:c8ff:fe59:5bcc via 3ffe:2400::280:c8ff:fe59:5bcc \ + dev eth0 metric 0 + cache users 1 used 1 age 23sec mtu 1500 rtt 300 +3ffe:2400:0:1:2a0:ccff:fe66:1878 via 3ffe:2400:0:1:2a0:ccff:fe66:1878 \ + dev eth1 metric 0 + cache used 2 age 20sec mtu 1500 rtt 300 +3ffe:2400:0:1:a00:20ff:fe71:fb30 via 3ffe:2400:0:1:a00:20ff:fe71:fb30 \ + dev eth1 metric 0 + cache used 2 age 33sec mtu 1500 rtt 300 +ff02::1 via ff02::1 dev eth1 metric 0 + cache users 1 used 1 age 45sec mtu 1500 rtt 300 + +*** Round 1, deleting 6 entries *** +*** Flush is complete after 1 round *** +netadm@amber:~ # ip -6 -s -s ro flush cache +Nothing to flush. +netadm@amber:~ # +\end{verbatim} + +The third example flushes BGP routing tables after a \verb|gated| +death. +\begin{verbatim} +netadm@amber:~ # ip ro ls proto gated/bgp | wc + 1408 9856 78730 +netadm@amber:~ # ip -s ro f proto gated/bgp + +*** Round 1, deleting 1408 entries *** +*** Flush is complete after 1 round *** +netadm@amber:~ # ip ro f proto gated/bgp +Nothing to flush. +netadm@amber:~ # ip ro ls proto gated/bgp +netadm@amber:~ # +\end{verbatim} + + +\subsection{{\tt ip route get} --- get a single route} +\label{IP-ROUTE-GET} + +\paragraph{Abbreviations:} \verb|get|, \verb|g|. + +\paragraph{Description:} this command gets a single route to a destination +and prints its contents exactly as the kernel sees it. + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|to ADDRESS| (default) + +--- the destination address. + +\item \verb|from ADDRESS| + +--- the source address. + +\item \verb|tos TOS| or \verb|dsfield TOS| + +--- the Type Of Service. + +\item \verb|iif NAME| + +--- the device from which this packet is expected to arrive. + +\item \verb|oif NAME| + +--- force the output device on which this packet will be routed. + +\item \verb|connected| + +--- if no source address (option \verb|from|) was given, relookup +the route with the source set to the preferred address received from the first lookup. +If policy routing is used, it may be a different route. + +\end{itemize} + +Note that this operation is not equivalent to \verb|ip route show|. +\verb|show| shows existing routes. \verb|get| resolves them and +creates new clones if necessary. Essentially, \verb|get| +is equivalent to sending a packet along this path. +If the \verb|iif| argument is not given, the kernel creates a route +to output packets towards the requested destination. +This is equivalent to pinging the destination +with a subsequent {\tt ip route ls cache}, however, no packets are +actually sent. With the \verb|iif| argument, the kernel pretends +that a packet arrived from this interface and searches for +a path to forward the packet. + +\paragraph{Output format:} This command outputs routes in the same +format as \verb|ip route ls|. + +\paragraph{Examples:} +\begin{itemize} +\item Find a route to output packets to 193.233.7.82: +\begin{verbatim} +kuznet@amber:~ $ ip route get 193.233.7.82 +193.233.7.82 dev eth0 src 193.233.7.65 realms inr.ac + cache mtu 1500 rtt 300 +kuznet@amber:~ $ +\end{verbatim} + +\item Find a route to forward packets arriving on \verb|eth0| +from 193.233.7.82 and destined for 193.233.7.82: +\begin{verbatim} +kuznet@amber:~ $ ip r g 193.233.7.82 from 193.233.7.82 iif eth0 +193.233.7.82 from 193.233.7.82 dev eth0 src 193.233.7.65 \ + realms inr.ac/inr.ac + cache <src-direct,redirect> mtu 1500 rtt 300 iif eth0 +kuznet@amber:~ $ +\end{verbatim} +\begin{NB} + \label{NB-nature-of-strangeness} + This is the command that created the funny route from 193.233.7.82 + looped back to 193.233.7.82 (cf.\ NB on~p.\pageref{NB-strange-route}). + Note the \verb|redirect| flag on it. +\end{NB} + +\item Find a multicast route for packets arriving on \verb|eth0| +from host 193.233.7.82 and destined for multicast group 224.2.127.254 +(it is assumed that a multicast routing daemon is running. +In this case, it is \verb|pimd|) +\begin{verbatim} +kuznet@amber:~ $ ip r g 224.2.127.254 from 193.233.7.82 iif eth0 +multicast 224.2.127.254 from 193.233.7.82 dev lo \ + src 193.233.7.65 realms inr.ac/cosmos + cache <mc> iif eth0 Oifs: eth1 pimreg +kuznet@amber:~ $ +\end{verbatim} +This route differs from the ones seen before. It contains a ``normal'' part +and a ``multicast'' part. The normal part is used to deliver (or not to +deliver) the packet to local IP listeners. In this case the router +is not a member +of this group, so that route has no \verb|local| flag and only +forwards packets. The output device for such entries is always loopback. +The multicast part consists of an additional \verb|Oifs:| list showing +the output interfaces. +\end{itemize} + + +It is time for a more complicated example. Let us add an invalid +gatewayed route for a destination which is really directly connected: +\begin{verbatim} +netadm@alisa:~ # ip route add 193.233.7.98 via 193.233.7.254 +netadm@alisa:~ # ip route get 193.233.7.98 +193.233.7.98 via 193.233.7.254 dev eth0 src 193.233.7.90 + cache mtu 1500 rtt 3072 +netadm@alisa:~ # +\end{verbatim} +and probe it with ping: +\begin{verbatim} +netadm@alisa:~ # ping -n 193.233.7.98 +PING 193.233.7.98 (193.233.7.98) from 193.233.7.90 : 56 data bytes +From 193.233.7.254: Redirect Host(New nexthop: 193.233.7.98) +64 bytes from 193.233.7.98: icmp_seq=0 ttl=255 time=3.5 ms +From 193.233.7.254: Redirect Host(New nexthop: 193.233.7.98) +64 bytes from 193.233.7.98: icmp_seq=1 ttl=255 time=2.2 ms +64 bytes from 193.233.7.98: icmp_seq=2 ttl=255 time=0.4 ms +64 bytes from 193.233.7.98: icmp_seq=3 ttl=255 time=0.4 ms +64 bytes from 193.233.7.98: icmp_seq=4 ttl=255 time=0.4 ms +^C +--- 193.233.7.98 ping statistics --- +5 packets transmitted, 5 packets received, 0% packet loss +round-trip min/avg/max = 0.4/1.3/3.5 ms +netadm@alisa:~ # +\end{verbatim} +What happened? Router 193.233.7.254 understood that we have a much +better path to the destination and sent us an ICMP redirect message. +We may retry \verb|ip route get| to see what we have in the routing +tables now: +\begin{verbatim} +netadm@alisa:~ # ip route get 193.233.7.98 +193.233.7.98 dev eth0 src 193.233.7.90 + cache <redirected> mtu 1500 rtt 3072 +netadm@alisa:~ # +\end{verbatim} + + + +\section{{\tt ip rule} --- routing policy database management} +\label{IP-RULE} + +\paragraph{Abbreviations:} \verb|rule|, \verb|ru|. + +\paragraph{Object:} \verb|rule|s in the routing policy database control +the route selection algorithm. + +Classic routing algorithms used in the Internet make routing decisions +based only on the destination address of packets (and in theory, +but not in practice, on the TOS field). The seminal review of classic +routing algorithms and their modifications can be found in~\cite{RFC1812}. + +In some circumstances we want to route packets differently depending not only +on destination addresses, but also on other packet fields: source address, +IP protocol, transport protocol ports or even packet payload. +This task is called ``policy routing''. + +\begin{NB} + ``policy routing'' $\neq$ ``routing policy''. + +\noindent ``policy routing'' $=$ ``cunning routing''. + +\noindent ``routing policy'' $=$ ``routing tactics'' or ``routing plan''. +\end{NB} + +To solve this task, the conventional destination based routing table, ordered +according to the longest match rule, is replaced with a ``routing policy +database'' (or RPDB), which selects routes +by executing some set of rules. The rules may have lots of keys of different +natures and therefore they have no natural ordering, but one imposed +by the administrator. Linux-2.2 RPDB is a linear list of rules +ordered by numeric priority value. +RPDB explicitly allows matching a few packet fields: + +\begin{itemize} +\item packet source address. +\item packet destination address. +\item TOS. +\item incoming interface (which is packet metadata, rather than a packet field). +\end{itemize} + +Matching IP protocols and transport ports is also possible, +indirectly, via \verb|ipchains|, by exploiting their ability +to mark some classes of packets with \verb|fwmark|. Therefore, +\verb|fwmark| is also included in the set of keys checked by rules. + +Each policy routing rule consists of a {\em selector\/} and an {\em action\/} +predicate. The RPDB is scanned in the order of increasing priority. The selector +of each rule is applied to \{source address, destination address, incoming +interface, tos, fwmark\} and, if the selector matches the packet, +the action is performed. The action predicate may return with success. +In this case, it will either give a route or failure indication +and the RPDB lookup is terminated. Otherwise, the RPDB program +continues on the next rule. + +What is the action, semantically? The natural action is to select the +nexthop and the output device. This is what +Cisco IOS~\cite{IOS} does. Let us call it ``match \& set''. +The Linux-2.2 approach is more flexible. The action includes +lookups in destination-based routing tables and selecting +a route from these tables according to the classic longest match algorithm. +The ``match \& set'' approach is the simplest case of the Linux one. It is realized +when a second level routing table contains a single default route. +Recall that Linux-2.2 supports multiple tables +managed with the \verb|ip route| command, described in the previous section. + +At startup time the kernel configures the default RPDB consisting of three +rules: + +\begin{enumerate} +\item Priority: 0, Selector: match anything, Action: lookup routing +table \verb|local| (ID 255). +The \verb|local| table is a special routing table containing +high priority control routes for local and broadcast addresses. + +Rule 0 is special. It cannot be deleted or overridden. + + +\item Priority: 32766, Selector: match anything, Action: lookup routing +table \verb|main| (ID 254). +The \verb|main| table is the normal routing table containing all non-policy +routes. This rule may be deleted and/or overridden with other +ones by the administrator. + +\item Priority: 32767, Selector: match anything, Action: lookup routing +table \verb|default| (ID 253). +The \verb|default| table is empty. It is reserved for some +post-processing if no previous default rules selected the packet. +This rule may also be deleted. + +\end{enumerate} + +Do not confuse routing tables with rules: rules point to routing tables, +several rules may refer to one routing table and some routing tables +may have no rules pointing to them. If the administrator deletes all the rules +referring to a table, the table is not used, but it still exists +and will disappear only after all the routes contained in it are deleted. + + +\paragraph{Rule attributes:} Each RPDB entry has additional +attributes. F.e.\ each rule has a pointer to some routing +table. NAT and masquerading rules have an attribute to select new IP +address to translate/masquerade. Besides that, rules have some +optional attributes, which routes have, namely \verb|realms|. +These values do not override those contained in the routing tables. They +are only used if the route did not select any attributes. + + +\paragraph{Rule types:} The RPDB may contain rules of the following +types: +\begin{itemize} +\item \verb|unicast| --- the rule prescribes to return the route found +in the routing table referenced by the rule. +\item \verb|blackhole| --- the rule prescribes to silently drop the packet. +\item \verb|unreachable| --- the rule prescribes to generate a ``Network +is unreachable'' error. +\item \verb|prohibit| --- the rule prescribes to generate +``Communication is administratively prohibited'' error. +\item \verb|nat| --- the rule prescribes to translate the source address +of the IP packet into some other value. More about NAT is +in Appendix~\ref{ROUTE-NAT}, p.\pageref{ROUTE-NAT}. +\end{itemize} + + +\paragraph{Commands:} \verb|add|, \verb|delete| and \verb|show| +(or \verb|list|). + +\subsection{{\tt ip rule add} --- insert a new rule\\ + {\tt ip rule delete} --- delete a rule} +\label{IP-RULE-ADD} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|delete|, \verb|del|, + \verb|d|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|type TYPE| (default) + +--- the type of this rule. The list of valid types was given in the previous +subsection. + +\item \verb|from PREFIX| + +--- select the source prefix to match. + +\item \verb|to PREFIX| + +--- select the destination prefix to match. + +\item \verb|iif NAME| + +--- select the incoming device to match. If the interface is loopback, +the rule only matches packets originating from this host. This means that you +may create separate routing tables for forwarded and local packets and, +hence, completely segregate them. + +\item \verb|tos TOS| or \verb|dsfield TOS| + +--- select the TOS value to match. + +\item \verb|fwmark MARK| + +--- select the \verb|fwmark| value to match. + +\item \verb|priority PREFERENCE| + +--- the priority of this rule. Each rule should have an explicitly +set {\em unique\/} priority value. +\begin{NB} + Really, for historical reasons \verb|ip rule add| does not require a + priority value and allows them to be non-unique. + If the user does not supplied a priority, it is selected by the kernel. + If the user creates a rule with a priority value that + already exists, the kernel does not reject the request. It adds + the new rule before all old rules of the same priority. + + It is mistake in design, no more. And it will be fixed one day, + so do not rely on this feature. Use explicit priorities. +\end{NB} + + +\item \verb|table TABLEID| + +--- the routing table identifier to lookup if the rule selector matches. + +\item \verb|realms FROM/TO| + +--- Realms to select if the rule matched and the routing table lookup +succeeded. Realm \verb|TO| is only used if the route did not select +any realm. + +\item \verb|nat ADDRESS| + +--- The base of the IP address block to translate (for source addresses). +The \verb|ADDRESS| may be either the start of the block of NAT addresses +(selected by NAT routes) or in linux-2.2 a local host address (or even zero). +In the last case the router does not translate the packets, +but masquerades them to this address; this feature disappered in 2.4. +More about NAT is in Appendix~\ref{ROUTE-NAT}, +p.\pageref{ROUTE-NAT}. + +\end{itemize} + +\paragraph{Warning:} Changes to the RPDB made with these commands +do not become active immediately. It is assumed that after +a script finishes a batch of updates, it flushes the routing cache +with \verb|ip route flush cache|. + +\paragraph{Examples:} +\begin{itemize} +\item Route packets with source addresses from 192.203.80/24 +according to routing table \verb|inr.ruhep|: +\begin{verbatim} +ip ru add from 192.203.80.0/24 table inr.ruhep prio 220 +\end{verbatim} + +\item Translate packet source address 193.233.7.83 into 192.203.80.144 +and route it according to table \#1 (actually, it is \verb|inr.ruhep|): +\begin{verbatim} +ip ru add from 193.233.7.83 nat 192.203.80.144 table 1 prio 320 +\end{verbatim} + +\item Delete the unused default rule: +\begin{verbatim} +ip ru del prio 32767 +\end{verbatim} + +\end{itemize} + + + +\subsection{{\tt ip rule show} --- list rules} +\label{IP-RULE-SHOW} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + + +\paragraph{Arguments:} Good news, this is one command that has no arguments. + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@amber:~ $ ip ru ls +0: from all lookup local +200: from 192.203.80.0/24 to 193.233.7.0/24 lookup main +210: from 192.203.80.0/24 to 192.203.80.0/24 lookup main +220: from 192.203.80.0/24 lookup inr.ruhep realms inr.ruhep/radio-msu +300: from 193.233.7.83 to 193.233.7.0/24 lookup main +310: from 193.233.7.83 to 192.203.80.0/24 lookup main +320: from 193.233.7.83 lookup inr.ruhep map-to 192.203.80.144 +32766: from all lookup main +kuznet@amber:~ $ +\end{verbatim} + +In the first column is the rule priority value followed +by a colon. Then the selectors follow. Each key is prefixed +with the same keyword that was used to create the rule. + +The keyword \verb|lookup| is followed by a routing table identifier, +as it is recorded in the file \verb|/etc/iproute2/rt_tables|. + +If the rule does NAT (f.e.\ rule \#320), it is shown by the keyword +\verb|map-to| followed by the start of the block of addresses to map. + +The sense of this example is pretty simple. The prefixes +192.203.80.0/24 and 193.233.7.0/24 form the internal network, but +they are routed differently when the packets leave it. +Besides that, the host 193.233.7.83 is translated into +another prefix to look like 192.203.80.144 when talking +to the outer world. + + + +\section{{\tt ip maddress} --- multicast addresses management} +\label{IP-MADDR} + +\paragraph{Object:} \verb|maddress| objects are multicast addresses. + +\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|show| (or \verb|list|). + +\subsection{{\tt ip maddress show} --- list multicast addresses} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + +\paragraph{Arguments:} + +\begin{itemize} + +\item \verb|dev NAME| (default) + +--- the device name. + +\end{itemize} + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip maddr ls dummy +2: dummy + link 33:33:00:00:00:01 + link 01:00:5e:00:00:01 + inet 224.0.0.1 users 2 + inet6 ff02::1 +kuznet@alisa:~ $ +\end{verbatim} + +The first line of the output shows the interface index and its name. +Then the multicast address list follows. Each line starts with the +protocol identifier. The word \verb|link| denotes a link layer +multicast addresses. + +If a multicast address has more than one user, the number +of users is shown after the \verb|users| keyword. + +One additional feature not present in the example above +is the \verb|static| flag, which indicates that the address was joined +with \verb|ip maddr add|. See the following subsection. + + + +\subsection{{\tt ip maddress add} --- add a multicast address\\ + {\tt ip maddress delete} --- delete a multicast address} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Description:} these commands attach/detach +a static link layer multicast address to listen on the interface. +Note that it is impossible to join protocol multicast groups +statically. This command only manages link layer addresses. + + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|address LLADDRESS| (default) + +--- the link layer multicast address. + +\item \verb|dev NAME| + +--- the device to join/leave this multicast address. + +\end{itemize} + + +\paragraph{Example:} Let us continue with the example from the previous subsection. + +\begin{verbatim} +netadm@alisa:~ # ip maddr add 33:33:00:00:00:01 dev dummy +netadm@alisa:~ # ip -0 maddr ls dummy +2: dummy + link 33:33:00:00:00:01 users 2 static + link 01:00:5e:00:00:01 +netadm@alisa:~ # ip maddr del 33:33:00:00:00:01 dev dummy +\end{verbatim} + +\begin{NB} + Neither \verb|ip| nor the kernel check for multicast address validity. + Particularly, this means that you can try to load a unicast address + instead of a multicast address. Most drivers will ignore such addresses, + but several (f.e.\ Tulip) will intern it to their on-board filter. + The effects may be strange. Namely, the addresses become additional + local link addresses and, if you loaded the address of another host + to the router, wait for duplicated packets on the wire. + It is not a bug, but rather a hole in the API and intra-kernel interfaces. + This feature is really more useful for traffic monitoring, but using it + with Linux-2.2 you {\em have to\/} be sure that the host is not + a router and, especially, that it is not a transparent proxy or masquerading + agent. +\end{NB} + + + +\section{{\tt ip mroute} --- multicast routing cache management} +\label{IP-MROUTE} + +\paragraph{Abbreviations:} \verb|mroute|, \verb|mr|. + +\paragraph{Object:} \verb|mroute| objects are multicast routing cache +entries created by a user level mrouting daemon +(f.e.\ \verb|pimd| or \verb|mrouted|). + +Due to the limitations of the current interface to the multicast routing +engine, it is impossible to change \verb|mroute| objects administratively, +so we may only display them. This limitation will be removed +in the future. + +\paragraph{Commands:} \verb|show| (or \verb|list|). + + +\subsection{{\tt ip mroute show} --- list mroute cache entries} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|to PREFIX| (default) + +--- the prefix selecting the destination multicast addresses to list. + + +\item \verb|iif NAME| + +--- the interface on which multicast packets are received. + + +\item \verb|from PREFIX| + +--- the prefix selecting the IP source addresses of the multicast route. + + +\end{itemize} + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@amber:~ $ ip mroute ls +(193.232.127.6, 224.0.1.39) Iif: unresolved +(193.232.244.34, 224.0.1.40) Iif: unresolved +(193.233.7.65, 224.66.66.66) Iif: eth0 Oifs: pimreg +kuznet@amber:~ $ +\end{verbatim} + +Each line shows one (S,G) entry in the multicast routing cache, +where S is the source address and G is the multicast group. \verb|Iif| is +the interface on which multicast packets are expected to arrive. +If the word \verb|unresolved| is there instead of the interface name, +it means that the routing daemon still hasn't resolved this entry. +The keyword \verb|oifs| is followed by a list of output interfaces, separated +by spaces. If a multicast routing entry is created with non-trivial +TTL scope, administrative distances are appended to the device names +in the \verb|oifs| list. + +\paragraph{Statistics:} The \verb|-statistics| option also prints the +number of packets and bytes forwarded along this route and +the number of packets that arrived on the wrong interface, if this number is not zero. + +\begin{verbatim} +kuznet@amber:~ $ ip -s mr ls 224.66/16 +(193.233.7.65, 224.66.66.66) Iif: eth0 Oifs: pimreg + 9383 packets, 300256 bytes +kuznet@amber:~ $ +\end{verbatim} + + +\section{{\tt ip tunnel} --- tunnel configuration} +\label{IP-TUNNEL} + +\paragraph{Abbreviations:} \verb|tunnel|, \verb|tunl|. + +\paragraph{Object:} \verb|tunnel| objects are tunnels, encapsulating +packets in IPv4 packets and then sending them over the IP infrastructure. + +\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|change|, \verb|show| +(or \verb|list|). + +\paragraph{See also:} A more informal discussion of tunneling +over IP and the \verb|ip tunnel| command can be found in~\cite{IP-TUNNELS}. + +\subsection{{\tt ip tunnel add} --- add a new tunnel\\ + {\tt ip tunnel change} --- change an existing tunnel\\ + {\tt ip tunnel delete} --- destroy a tunnel} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|; +\verb|delete|, \verb|del|, \verb|d|. + + +\paragraph{Arguments:} + +\begin{itemize} + +\item \verb|name NAME| (default) + +--- select the tunnel device name. + +\item \verb|mode MODE| + +--- set the tunnel mode. Three modes are currently available: + \verb|ipip|, \verb|sit| and \verb|gre|. + +\item \verb|remote ADDRESS| + +--- set the remote endpoint of the tunnel. + +\item \verb|local ADDRESS| + +--- set the fixed local address for tunneled packets. +It must be an address on another interface of this host. + +\item \verb|ttl N| + +--- set a fixed TTL \verb|N| on tunneled packets. + \verb|N| is a number in the range 1--255. 0 is a special value + meaning that packets inherit the TTL value. + The default value is: \verb|inherit|. + +\item \verb|tos T| or \verb|dsfield T| + +--- set a fixed TOS \verb|T| on tunneled packets. + The default value is: \verb|inherit|. + + + +\item \verb|dev NAME| + +--- bind the tunnel to the device \verb|NAME| so that + tunneled packets will only be routed via this device and will + not be able to escape to another device when the route to endpoint changes. + +\item \verb|nopmtudisc| + +--- disable Path MTU Discovery on this tunnel. + It is enabled by default. Note that a fixed ttl is incompatible + with this option: tunnelling with a fixed ttl always makes pmtu discovery. + +\item \verb|key K|, \verb|ikey K|, \verb|okey K| + +--- (only GRE tunnels) use keyed GRE with key \verb|K|. \verb|K| is + either a number or an IP address-like dotted quad. + The \verb|key| parameter sets the key to use in both directions. + The \verb|ikey| and \verb|okey| parameters set different keys for input and output. + + +\item \verb|csum|, \verb|icsum|, \verb|ocsum| + +--- (only GRE tunnels) generate/require checksums for tunneled packets. + The \verb|ocsum| flag calculates checksums for outgoing packets. + The \verb|icsum| flag requires that all input packets have the correct + checksum. The \verb|csum| flag is equivalent to the combination + ``\verb|icsum| \verb|ocsum|''. + +\item \verb|seq|, \verb|iseq|, \verb|oseq| + +--- (only GRE tunnels) serialize packets. + The \verb|oseq| flag enables sequencing of outgoing packets. + The \verb|iseq| flag requires that all input packets are serialized. + The \verb|seq| flag is equivalent to the combination ``\verb|iseq| \verb|oseq|''. + +\begin{NB} + I think this option does not + work. At least, I did not test it, did not debug it and + do not even understand how it is supposed to work or for what + purpose Cisco planned to use it. Do not use it. +\end{NB} + + +\end{itemize} + +\paragraph{Example:} Create a pointopoint IPv6 tunnel with maximal TTL of 32. +\begin{verbatim} +netadm@amber:~ # ip tunl add Cisco mode sit remote 192.31.7.104 \ + local 192.203.80.142 ttl 32 +\end{verbatim} + +\subsection{{\tt ip tunnel show} --- list tunnels} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + + +\paragraph{Arguments:} None. + +\paragraph{Output format:} +\begin{verbatim} +kuznet@amber:~ $ ip tunl ls Cisco +Cisco: ipv6/ip remote 192.31.7.104 local 192.203.80.142 ttl 32 +kuznet@amber:~ $ +\end{verbatim} +The line starts with the tunnel device name followed by a colon. +Then the tunnel mode follows. The parameters of the tunnel are listed +with the same keywords that were used when creating the tunnel. + +\paragraph{Statistics:} + +\begin{verbatim} +kuznet@amber:~ $ ip -s tunl ls Cisco +Cisco: ipv6/ip remote 192.31.7.104 local 192.203.80.142 ttl 32 +RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts + 12566 1707516 0 0 0 0 +TX: Packets Bytes Errors DeadLoop NoRoute NoBufs + 13445 1879677 0 0 0 0 +kuznet@amber:~ $ +\end{verbatim} +Essentially, these numbers are the same as the numbers +printed with {\tt ip -s link show} +(sec.\ref{IP-LINK-SHOW}, p.\pageref{IP-LINK-SHOW}) but the tags are different +to reflect that they are tunnel specific. +\begin{itemize} +\item \verb|CsumErrs| --- the total number of packets dropped +because of checksum failures for a GRE tunnel with checksumming enabled. +\item \verb|OutOfSeq| --- the total number of packets dropped +because they arrived out of sequence for a GRE tunnel with +serialization enabled. +\item \verb|Mcasts| --- the total number of multicast packets +received on a broadcast GRE tunnel. +\item \verb|DeadLoop| --- the total number of packets which were not +transmitted because the tunnel is looped back to itself. +\item \verb|NoRoute| --- the total number of packets which were not +transmitted because there is no IP route to the remote endpoint. +\item \verb|NoBufs| --- the total number of packets which were not +transmitted because the kernel failed to allocate a buffer. +\end{itemize} + + +\section{{\tt ip monitor} and {\tt rtmon} --- state monitoring} +\label{IP-MONITOR} + +The \verb|ip| utility can monitor the state of devices, addresses +and routes continuously. This option has a slightly different format. +Namely, +the \verb|monitor| command is the first in the command line and then +the object list follows: +\begin{verbatim} + ip monitor [ file FILE ] [ all | OBJECT-LIST ] +\end{verbatim} +\verb|OBJECT-LIST| is the list of object types that we want to monitor. +It may contain \verb|link|, \verb|address| and \verb|route|. +If no \verb|file| argument is given, \verb|ip| opens RTNETLINK, +listens on it and dumps state changes in the format described +in previous sections. + +If a file name is given, it does not listen on RTNETLINK, +but opens the file containing RTNETLINK messages saved in binary format +and dumps them. Such a history file can be generated with the +\verb|rtmon| utility. This utility has a command line syntax similar to +\verb|ip monitor|. +Ideally, \verb|rtmon| should be started before +the first network configuration command is issued. F.e.\ if +you insert: +\begin{verbatim} + rtmon file /var/log/rtmon.log +\end{verbatim} +in a startup script, you will be able to view the full history +later. + +Certainly, it is possible to start \verb|rtmon| at any time. +It prepends the history with the state snapshot dumped at the moment +of starting. + + +\section{Route realms and policy propagation, {\tt rtacct}} +\label{RT-REALMS} + +On routers using OSPF ASE or, especially, the BGP protocol, routing +tables may be huge. If we want to classify or to account for the packets +per route, we will have to keep lots of information. Even worse, if we +want to distinguish the packets not only by their destination, but +also by their source, the task gets quadratic complexity and its solution +is physically impossible. + +One approach to propagating the policy from routing protocols +to the forwarding engine has been proposed in~\cite{IOS-BGP-PP}. +Essentially, Cisco Policy Propagation via BGP is based on the fact +that dedicated routers all have the RIB (Routing Information Base) +close to the forwarding engine, so policy routing rules can +check all the route attributes, including ASPATH information +and community strings. + +The Linux architecture, splitting the RIB (maintained by a user level +daemon) and the kernel based FIB (Forwarding Information Base), +does not allow such a simple approach. + +It is to our fortune because there is another solution +which allows even more flexible policy and richer semantics. + +Namely, routes can be clustered together in user space, based on their +attributes. F.e.\ a BGP router knows route ASPATH, its community; +an OSPF router knows the route tag or its area. The administrator, when adding +routes manually, also knows their nature. Providing that the number of such +aggregates (we call them {\em realms\/}) is low, the task of full +classification both by source and destination becomes quite manageable. + +So each route may be assigned to a realm. It is assumed that +this identification is made by a routing daemon, but static routes +can also be handled manually with \verb|ip route| (see sec.\ref{IP-ROUTE}, +p.\pageref{IP-ROUTE}). +\begin{NB} + There is a patch to \verb|gated|, allowing classification of routes + to realms with all the set of policy rules implemented in \verb|gated|: + by prefix, by ASPATH, by origin, by tag etc. +\end{NB} + +To facilitate the construction (f.e.\ in case the routing +daemon is not aware of realms), missing realms may be completed +with routing policy rules, see sec.~\ref{IP-RULE}, p.\pageref{IP-RULE}. + +For each packet the kernel calculates a tuple of realms: source realm +and destination realm, using the following algorithm: + +\begin{enumerate} +\item If the route has a realm, the destination realm of the packet is set to it. +\item If the rule has a source realm, the source realm of the packet is set to it. +If the destination realm was not inherited from the route and the rule has a destination realm, +it is also set. +\item If at least one of the realms is still unknown, the kernel finds +the reversed route to the source of the packet. +\item If the source realm is still unknown, get it from the reversed route. +\item If one of the realms is still unknown, swap the realms of reversed +routes and apply step 2 again. +\end{enumerate} + +After this procedure is completed we know what realm the packet +arrived from and the realm where it is going to propagate to. +If some of the realms are unknown, they are initialized to zero +(or realm \verb|unknown|). + +The main application of realms is the TC \verb|route| classifier~\cite{TC-CREF}, +where they are used to help assign packets to traffic classes, +to account, police and schedule them according to this +classification. + +A much simpler but still very useful application is incoming packet +accounting by realms. The kernel gathers a packet statistics summary +which can be viewed with the \verb|rtacct| utility. +\begin{verbatim} +kuznet@amber:~ $ rtacct russia +Realm BytesTo PktsTo BytesFrom PktsFrom +russia 20576778 169176 47080168 153805 +kuznet@amber:~ $ +\end{verbatim} +This shows that this router received 153805 packets from +the realm \verb|russia| and forwarded 169176 packets to \verb|russia|. +The realm \verb|russia| consists of routes with ASPATHs not leaving +Russia. + +Note that locally originating packets are not accounted here, +\verb|rtacct| shows incoming packets only. Using the \verb|route| +classifier (see~\cite{TC-CREF}) you can get even more detailed +accounting information about outgoing packets, optionally +summarizing traffic not only by source or destination, but +by any pair of source and destination realms. + + +\begin{thebibliography}{99} +\addcontentsline{toc}{section}{References} +\bibitem{RFC-NDISC} T.~Narten, E.~Nordmark, W.~Simpson. +``Neighbor Discovery for IP Version 6 (IPv6)'', RFC-2461. + +\bibitem{RFC-ADDRCONF} S.~Thomson, T.~Narten. +``IPv6 Stateless Address Autoconfiguration'', RFC-2462. + +\bibitem{RFC1812} F.~Baker. +``Requirements for IP Version 4 Routers'', RFC-1812. + +\bibitem{RFC1122} R.~T.~Braden. +``Requirements for Internet hosts --- communication layers'', RFC-1122. + +\bibitem{IOS} ``Cisco IOS Release 12.0 Network Protocols +Command Reference, Part 1'' and +``Cisco IOS Release 12.0 Quality of Service Solutions +Configuration Guide: Configuring Policy-Based Routing'',\\ +http://www.cisco.com/univercd/cc/td/doc/product/software/ios120. + +\bibitem{IP-TUNNELS} A.~N.~Kuznetsov. +``Tunnels over IP in Linux-2.2'', \\ +In: {\tt ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz}. + +\bibitem{TC-CREF} A.~N.~Kuznetsov. ``TC Command Reference'',\\ +In: {\tt ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz}. + +\bibitem{IOS-BGP-PP} ``Cisco IOS Release 12.0 Quality of Service Solutions +Configuration Guide: Configuring QoS Policy Propagation via +Border Gateway Protocol'',\\ +http://www.cisco.com/univercd/cc/td/doc/product/software/ios120. + +\bibitem{RFC-DHCP} R.~Droms. +``Dynamic Host Configuration Protocol.'', RFC-2131 + +\end{thebibliography} + + + + +\appendix +\addcontentsline{toc}{section}{Appendix} + +\section{Source address selection} +\label{ADDR-SEL} + +When a host creates an IP packet, it must select some source +address. Correct source address selection is a critical procedure, +because it gives the receiver the information needed to deliver a +reply. If the source is selected incorrectly, in the best case, +the backward path may appear different to the forward one which +is harmful for performance. In the worst case, when the addresses +are administratively scoped, the reply may be lost entirely. + +Linux-2.2 selects source addresses using the following algorithm: + +\begin{itemize} +\item +The application may select a source address explicitly with \verb|bind(2)| +syscall or supplying it to \verb|sendmsg(2)| via the ancillary data object +\verb|IP_PKTINFO|. In this case the kernel only checks the validity +of the address and never tries to ``improve'' an incorrect user choice, +generating an error instead. +\begin{NB} + Never say ``Never''. The sysctl option \verb|ip_dynaddr| breaks + this axiom. It has been made deliberately with the purpose + of automatically reselecting the address on hosts with dynamic dial-out interfaces. + However, this hack {\em must not\/} be used on multihomed hosts + and especially on routers: it would break them. +\end{NB} + + +\item Otherwise, IP routing tables can contain an explicit source +address hint for this destination. The hint is set with the \verb|src| parameter +to the \verb|ip route| command, sec.\ref{IP-ROUTE}, p.\pageref{IP-ROUTE}. + + +\item Otherwise, the kernel searches through the list of addresses +attached to the interface through which the packets will be routed. +The search strategies are different for IP and IPv6. Namely: + +\begin{itemize} +\item IPv6 searches for the first valid, not deprecated address +with the same scope as the destination. + +\item IP searches for the first valid address with a scope wider +than the scope of the destination but it prefers addresses +which fall to the same subnet as the nexthop of the route +to the destination. Unlike IPv6, the scopes of IPv4 destinations +are not encoded in their addresses but are supplied +in routing tables instead (the \verb|scope| parameter to the \verb|ip route| command, +sec.\ref{IP-ROUTE}, p.\pageref{IP-ROUTE}). + +\end{itemize} + + +\item Otherwise, if the scope of the destination is \verb|link| or \verb|host|, +the algorithm fails and returns a zero source address. + +\item Otherwise, all interfaces are scanned to search for an address +with an appropriate scope. The loopback device \verb|lo| is always the first +in the search list, so that if an address with global scope (not 127.0.0.1!) +is configured on loopback, it is always preferred. + +\end{itemize} + + +\section{Proxy ARP/NDISC} +\label{PROXY-NEIGH} + +Routers may answer ARP/NDISC solicitations on behalf of other hosts. +In Linux-2.2 proxy ARP on an interface may be enabled +by setting the kernel \verb|sysctl| variable +\verb|/proc/sys/net/ipv4/conf/<dev>/proxy_arp| to 1. After this, the router +starts to answer ARP requests on the interface \verb|<dev>|, provided +the route to the requested destination does {\em not\/} go back via the same +device. + +The variable \verb|/proc/sys/net/ipv4/conf/all/proxy_arp| enables proxy +ARP on all the IP devices. + +However, this approach fails in the case of IPv6 because the router +must join the solicited node multicast address to listen for the corresponding +NDISC queries. It means that proxy NDISC is possible only on a per destination +basis. + +Logically, proxy ARP/NDISC is not a kernel task. It can easily be implemented +in user space. However, similar functionality was present in BSD kernels +and in Linux-2.0, so we have to preserve it at least to the extent that +is standardized in BSD. +\begin{NB} + Linux-2.0 ARP had a feature called {\em subnet\/} proxy ARP. + It is replaced with the sysctl flag in Linux-2.2. +\end{NB} + + +The \verb|ip| utility provides a way to manage proxy ARP/NDISC +with the \verb|ip neigh| command, namely: +\begin{verbatim} + ip neigh add proxy ADDRESS [ dev NAME ] +\end{verbatim} +adds a new proxy ARP/NDISC record and +\begin{verbatim} + ip neigh del proxy ADDRESS [ dev NAME ] +\end{verbatim} +deletes it. + +If the name of the device is not given, the router will answer solicitations +for address \verb|ADDRESS| on all devices, otherwise it will only serve +the device \verb|NAME|. Even if the proxy entry is created with +\verb|ip neigh|, the router {\em will not\/} answer a query if the route +to the destination goes back via the interface from which the solicitation +was received. + +It is important to emphasize that proxy entries have {\em no\/} +parameters other than these (IP/IPv6 address and optional device). +Particularly, the entry does not store any link layer address. +It always advertises the station address of the interface +on which it sends advertisements (i.e. it's own station address). + +\section{Route NAT status} +\label{ROUTE-NAT} + +NAT (or ``Network Address Translation'') remaps some parts +of the IP address space into other ones. Linux-2.2 route NAT is supposed +to be used to facilitate policy routing by rewriting addresses +to other routing domains or to help while renumbering sites +to another prefix. + +\paragraph{What it is not:} +It is necessary to emphasize that {\em it is not supposed\/} +to be used to compress address space or to split load. +This is not missing functionality but a design principle. +Route NAT is {\em stateless\/}. It does not hold any state +about translated sessions. This means that it handles any number +of sessions flawlessly. But it also means that it is {\em static\/}. +It cannot detect the moment when the last TCP client stops +using an address. For the same reason, it will not help to split +load between several servers. +\begin{NB} +It is a pretty commonly held belief that it is useful to split load between +several servers with NAT. This is a mistake. All you get from this +is the requirement that the router keep the state of all the TCP connections +going via it. Well, if the router is so powerful, run apache on it. 8) +\end{NB} + +The second feature: it does not touch packet payload, +does not try to ``improve'' broken protocols by looking +through its data and mangling it. It mangles IP addresses, +only IP addresses and nothing but IP addresses. +This also, is not missing any functionality. + +To resume: if you need to compress address space or keep +active FTP clients happy, your choice is not route NAT but masquerading, +port forwarding, NAPT etc. +\begin{NB} +By the way, you may also want to look at +http://www.suse.com/\~mha/HyperNews/get/linux-ip-nat.html +\end{NB} + + +\paragraph{How it works.} +Some part of the address space is reserved for dummy addresses +which will look for all the world like some host addresses +inside your network. No other hosts may use these addresses, +however other routers may also be configured to translate them. +\begin{NB} +A great advantage of route NAT is that it may be used not +only in stub networks but in environments with arbitrarily complicated +structure. It does not firewall, it {\em forwards.} +\end{NB} +These addresses are selected by the \verb|ip route| command +(sec.\ref{IP-ROUTE-ADD}, p.\pageref{IP-ROUTE-ADD}). F.e.\ +\begin{verbatim} + ip route add nat 192.203.80.144 via 193.233.7.83 +\end{verbatim} +states that the single address 192.203.80.144 is a dummy NAT address. +For all the world it looks like a host address inside our network. +For neighbouring hosts and routers it looks like the local address +of the translating router. The router answers ARP for it, advertises +this address as routed via it, {\em et al\/}. When the router +receives a packet destined for 192.203.80.144, it replaces +this address with 193.233.7.83 which is the address of some real +host and forwards the packet. If you need to remap +blocks of addresses, you may use a command like: +\begin{verbatim} + ip route add nat 192.203.80.192/26 via 193.233.7.64 +\end{verbatim} +This command will map a block of 63 addresses 192.203.80.192-255 to +193.233.7.64-127. + +When an internal host (193.233.7.83 in the example above) +sends something to the outer world and these packets are forwarded +by our router, it should translate the source address 193.233.7.83 +into 192.203.80.144. This task is solved by setting a special +policy rule (sec.\ref{IP-RULE-ADD}, p.\pageref{IP-RULE-ADD}): +\begin{verbatim} + ip rule add prio 320 from 193.233.7.83 nat 192.203.80.144 +\end{verbatim} +This rule says that the source address 193.233.7.83 +should be translated into 192.203.80.144 before forwarding. +It is important that the address after the \verb|nat| keyword +is some NAT address, declared by {\tt ip route add nat}. +If it is just a random address the router will not map to it. +\begin{NB} +The exception is when the address is a local address of this +router (or 0.0.0.0) and masquerading is configured in the linux-2.2 +kernel. In this case the router will masquerade the packets as this address. +If 0.0.0.0 is selected, the result is equivalent to one +obtained with firewalling rules. Otherwise, you have the way +to order Linux to masquerade to this fixed address. +NAT mechanism used in linux-2.4 is more flexible than +masquerading, so that this feature has lost meaning and disabled. +\end{NB} + +If the network has non-trivial internal structure, it is +useful and even necessary to add rules disabling translation +when a packet does not leave this network. Let us return to the +example from sec.\ref{IP-RULE-SHOW} (p.\pageref{IP-RULE-SHOW}). +\begin{verbatim} +300: from 193.233.7.83 to 193.233.7.0/24 lookup main +310: from 193.233.7.83 to 192.203.80.0/24 lookup main +320: from 193.233.7.83 lookup inr.ruhep map-to 192.203.80.144 +\end{verbatim} +This block of rules causes normal forwarding when +packets from 193.233.7.83 do not leave networks 193.233.7/24 +and 192.203.80/24. Also, if the \verb|inr.ruhep| table does not +contain a route to the destination (which means that the routing +domain owning addresses from 192.203.80/24 is dead), no translation +will occur. Otherwise, the packets are translated. + +\paragraph{How to only translate selected ports:} +If you only want to translate selected ports (f.e.\ http) +and leave the rest intact, you may use \verb|ipchains| +to \verb|fwmark| a class of packets. +Suppose you did and all the packets from 193.233.7.83 +destined for port 80 are marked with marker 0x1234 in input fwchain. +In this case you may replace rule \#320 with: +\begin{verbatim} +320: from 193.233.7.83 fwmark 1234 lookup main map-to 192.203.80.144 +\end{verbatim} +and translation will only be enabled for outgoing http requests. + +\section{Example: minimal host setup} +\label{EXAMPLE-SETUP} + +The following script gives an example of a fault safe +setup of IP (and IPv6, if it is compiled into the kernel) +in the common case of a node attached to a single broadcast +network. A more advanced script, which may be used both on multihomed +hosts and on routers, is described in the following +section. + +The utilities used in the script may be found in the +directory ftp://ftp.inr.ac.ru/ip-routing/: +\begin{enumerate} +\item \verb|ip| --- package \verb|iproute2|. +\item \verb|arping| --- package \verb|iputils|. +\item \verb|rdisc| --- package \verb|iputils|. +\end{enumerate} +\begin{NB} +It also refers to a DHCP client, \verb|dhcpcd|. I should refrain from +recommending a good DHCP client to use. All that I can +say is that ISC \verb|dhcp-2.0b1pl6| patched with the patch that +can be found in the \verb|dhcp.bootp.rarp| subdirectory of +the same ftp site {\em does\/} work, +at least on Ethernet and Token Ring. +\end{NB} + +\begin{verbatim} +#! /bin/bash +\end{verbatim} +\begin{flushleft} +\# {\bf Usage: \verb|ifone ADDRESS[/PREFIX-LENGTH] [DEVICE]|}\\ +\# {\bf Parameters:}\\ +\# \$1 --- Static IP address, optionally followed by prefix length.\\ +\# \$2 --- Device name. If it is missing, \verb|eth0| is asssumed.\\ +\# F.e. \verb|ifone 193.233.7.90| +\end{flushleft} +\begin{verbatim} +dev=$2 +: ${dev:=eth0} +ipaddr= +\end{verbatim} +\# Parse IP address, splitting prefix length. +\begin{verbatim} +if [ "$1" != "" ]; then + ipaddr=${1%/*} + if [ "$1" != "$ipaddr" ]; then + pfxlen=${1#*/} + fi + : ${pfxlen:=24} +fi +pfx="${ipaddr}/${pfxlen}" +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 0} --- enable loopback.\\ +\#\\ +\# This step is necessary on any networked box before attempt\\ +\# to configure any other device.\\ +\end{flushleft} +\begin{verbatim} +ip link set up dev lo +ip addr add 127.0.0.1/8 dev lo brd + scope host +\end{verbatim} +\begin{flushleft} +\# IPv6 autoconfigure themself on loopback.\\ +\#\\ +\# If user gave loopback as device, we add the address as alias and exit. +\end{flushleft} +\begin{verbatim} +if [ "$dev" = "lo" ]; then + if [ "$ipaddr" != "" -a "$ipaddr" != "127.0.0.1" ]; then + ip address add $ipaddr dev $dev + exit $? + fi + exit 0 +fi +\end{verbatim} + +\noindent\# {\bf Step 1} --- enable device \verb|$dev| + +\begin{verbatim} +if ! ip link set up dev $dev ; then + echo "Cannot enable interface $dev. Aborting." 1>&2 + exit 1 +fi +\end{verbatim} +\begin{flushleft} +\# The interface is \verb|UP|. IPv6 started stateless autoconfiguration itself,\\ +\# and its configuration finishes here. However,\\ +\# IP still needs some static preconfigured address. +\end{flushleft} +\begin{verbatim} +if [ "$ipaddr" = "" ]; then + echo "No address for $dev is configured, trying DHCP..." 1>&2 + dhcpcd + exit $? +fi +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 2} --- IP Duplicate Address Detection~\cite{RFC-DHCP}.\\ +\# Send two probes and wait for result for 3 seconds.\\ +\# If the interface opens slower f.e.\ due to long media detection,\\ +\# you want to increase the timeout.\\ +\end{flushleft} +\begin{verbatim} +if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then + echo "Address $ipaddr is busy, trying DHCP..." 1>&2 + dhcpcd + exit $? +fi +\end{verbatim} +\begin{flushleft} +\# OK, the address is unique, we may add it on the interface.\\ +\#\\ +\# {\bf Step 3} --- Configure the address on the interface. +\end{flushleft} + +\begin{verbatim} +if ! ip address add $pfx brd + dev $dev; then + echo "Failed to add $pfx on $dev, trying DHCP..." 1>&2 + dhcpcd + exit $? +fi +\end{verbatim} + +\noindent\# {\bf Step 4} --- Announce our presence on the link. +\begin{verbatim} +arping -A -c 1 -I $dev $ipaddr +noarp=$? +( sleep 2; + arping -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null & +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 5} (optional) --- Add some control routes.\\ +\#\\ +\# 1. Prohibit link local multicast addresses.\\ +\# 2. Prohibit link local (alias, limited) broadcast.\\ +\# 3. Add default multicast route. +\end{flushleft} +\begin{verbatim} +ip route add unreachable 224.0.0.0/24 +ip route add unreachable 255.255.255.255 +if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then + ip route add 224.0.0.0/4 dev $dev scope global +fi +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 6} --- Add fallback default route with huge metric.\\ +\# If a proxy ARP server is present on the interface, we will be\\ +\# able to talk to all the Internet without further configuration.\\ +\# It is not so cheap though and we still hope that this route\\ +\# will be overridden by more correct one by rdisc.\\ +\# Do not make this step if the device is not ARPable,\\ +\# because dead nexthop detection does not work on them. +\end{flushleft} +\begin{verbatim} +if [ "$noarp" = "0" ]; then + ip ro add default dev $dev metric 30000 scope global +fi +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 7} --- Restart router discovery and exit. +\end{flushleft} +\begin{verbatim} +killall -HUP rdisc || rdisc -fs +exit 0 +\end{verbatim} + + +\section{Example: {\protect\tt ifcfg} --- interface address management} +\label{EXAMPLE-IFCFG} + +This is a simplistic script replacing one option of \verb|ifconfig|, +namely, IP address management. It not only adds +addresses, but also carries out Duplicate Address Detection~\cite{RFC-DHCP}, +sends unsolicited ARP to update the caches of other hosts sharing +the interface, adds some control routes and restarts Router Discovery +when it is necessary. + +I strongly recommend using it {\em instead\/} of \verb|ifconfig| both +on hosts and on routers. + +\begin{verbatim} +#! /bin/bash +\end{verbatim} +\begin{flushleft} +\# {\bf Usage: \verb?ifcfg DEVICE[:ALIAS] [add|del] ADDRESS[/LENGTH] [PEER]?}\\ +\# {\bf Parameters:}\\ +\# ---Device name. It may have alias suffix, separated by colon.\\ +\# ---Command: add, delete or stop.\\ +\# ---IP address, optionally followed by prefix length.\\ +\# ---Optional peer address for pointopoint interfaces.\\ +\# F.e. \verb|ifcfg eth0 193.233.7.90/24| + +\noindent\# This function determines, whether it is router or host.\\ +\# It returns 0, if the host is apparently not router. +\end{flushleft} +\begin{verbatim} +CheckForwarding () { + local sbase fwd + sbase=/proc/sys/net/ipv4/conf + fwd=0 + if [ -d $sbase ]; then + for dir in $sbase/*/forwarding; do + fwd=$[$fwd + `cat $dir`] + done + else + fwd=2 + fi + return $fwd +} +\end{verbatim} +\begin{flushleft} +\# This function restarts Router Discovery.\\ +\end{flushleft} +\begin{verbatim} +RestartRDISC () { + killall -HUP rdisc || rdisc -fs +} +\end{verbatim} +\begin{flushleft} +\# Calculate ABC "natural" mask length\\ +\# Arg: \$1 = dotquad address +\end{flushleft} +\begin{verbatim} +ABCMaskLen () { + local class; + class=${1%%.*} + if [ $class -eq 0 -o $class -ge 224 ]; then return 0 + elif [ $class -ge 192 ]; then return 24 + elif [ $class -ge 128 ]; then return 16 + else return 8 ; fi +} +\end{verbatim} + + +\begin{flushleft} +\# {\bf MAIN()}\\ +\#\\ +\# Strip alias suffix separated by colon. +\end{flushleft} +\begin{verbatim} +label="label $1" +ldev=$1 +dev=${1%:*} +if [ "$dev" = "" -o "$1" = "help" ]; then + echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2 + echo " add - add new address" 1>&2 + echo " del - delete address" 1>&2 + echo " stop - completely disable IP" 1>&2 + exit 1 +fi +shift + +CheckForwarding +fwd=$? +\end{verbatim} +\begin{flushleft} +\# Parse command. If it is ``stop'', flush and exit. +\end{flushleft} +\begin{verbatim} +deleting=0 +case "$1" in +add) shift ;; +stop) + if [ "$ldev" != "$dev" ]; then + echo "Cannot stop alias $ldev" 1>&2 + exit 1; + fi + ip -4 addr flush dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 ;; +del*) + deleting=1; shift ;; +*) +esac +\end{verbatim} +\begin{flushleft} +\# Parse prefix, split prefix length, separated by slash. +\end{flushleft} +\begin{verbatim} +ipaddr= +pfxlen= +if [ "$1" != "" ]; then + ipaddr=${1%/*} + if [ "$1" != "$ipaddr" ]; then + pfxlen=${1#*/} + fi + if [ "$ipaddr" = "" ]; then + echo "$1 is bad IP address." 1>&2 + exit 1 + fi +fi +shift +\end{verbatim} +\begin{flushleft} +\# If peer address is present, prefix length is 32.\\ +\# Otherwise, if prefix length was not given, guess it. +\end{flushleft} +\begin{verbatim} +peer=$1 +if [ "$peer" != "" ]; then + if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then + echo "Peer address with non-trivial netmask." 1>&2 + exit 1 + fi + pfx="$ipaddr peer $peer" +else + if [ "$pfxlen" = "" ]; then + ABCMaskLen $ipaddr + pfxlen=$? + fi + pfx="$ipaddr/$pfxlen" +fi +if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then + label= +fi +\end{verbatim} +\begin{flushleft} +\# If deletion was requested, delete the address and restart RDISC +\end{flushleft} +\begin{verbatim} +if [ $deleting -ne 0 ]; then + ip addr del $pfx dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 +fi +\end{verbatim} +\begin{flushleft} +\# Start interface initialization.\\ +\#\\ +\# {\bf Step 0} --- enable device \verb|$dev| +\end{flushleft} +\begin{verbatim} +if ! ip link set up dev $dev ; then + echo "Error: cannot enable interface $dev." 1>&2 + exit 1 +fi +if [ "$ipaddr" = "" ]; then exit 0; fi +\end{verbatim} +\begin{flushleft} +\# {\bf Step 1} --- IP Duplicate Address Detection~\cite{RFC-DHCP}.\\ +\# Send two probes and wait for result for 3 seconds.\\ +\# If the interface opens slower f.e.\ due to long media detection,\\ +\# you want to increase the timeout.\\ +\end{flushleft} +\begin{verbatim} +if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then + echo "Error: some host already uses address $ipaddr on $dev." 1>&2 + exit 1 +fi +\end{verbatim} +\begin{flushleft} +\# OK, the address is unique. We may add it to the interface.\\ +\#\\ +\# {\bf Step 2} --- Configure the address on the interface. +\end{flushleft} +\begin{verbatim} +if ! ip address add $pfx brd + dev $dev $label; then + echo "Error: failed to add $pfx on $dev." 1>&2 + exit 1 +fi +\end{verbatim} +\noindent\# {\bf Step 3} --- Announce our presence on the link +\begin{verbatim} +arping -q -A -c 1 -I $dev $ipaddr +noarp=$? +( sleep 2 ; + arping -q -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null & +\end{verbatim} +\begin{flushleft} +\# {\bf Step 4} (optional) --- Add some control routes.\\ +\#\\ +\# 1. Prohibit link local multicast addresses.\\ +\# 2. Prohibit link local (alias, limited) broadcast.\\ +\# 3. Add default multicast route. +\end{flushleft} +\begin{verbatim} +ip route add unreachable 224.0.0.0/24 >& /dev/null +ip route add unreachable 255.255.255.255 >& /dev/null +if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then + ip route add 224.0.0.0/4 dev $dev scope global >& /dev/null +fi +\end{verbatim} +\begin{flushleft} +\# {\bf Step 5} --- Add fallback default route with huge metric.\\ +\# If a proxy ARP server is present on the interface, we will be\\ +\# able to talk to all the Internet without further configuration.\\ +\# Do not make this step on router or if the device is not ARPable.\\ +\# because dead nexthop detection does not work on them. +\end{flushleft} +\begin{verbatim} +if [ $fwd -eq 0 ]; then + if [ $noarp -eq 0 ]; then + ip ro append default dev $dev metric 30000 scope global + elif [ "$peer" != "" ]; then + if ping -q -c 2 -w 4 $peer ; then + ip ro append default via $peer dev $dev metric 30001 + fi + fi + RestartRDISC +fi + +exit 0 +\end{verbatim} +\begin{flushleft} +\# End of {\bf MAIN()} +\end{flushleft} + + +\end{document} diff --git a/doc/ip-tunnels.tex b/doc/ip-tunnels.tex new file mode 100644 index 0000000..0a8c930 --- /dev/null +++ b/doc/ip-tunnels.tex @@ -0,0 +1,469 @@ +\documentstyle[12pt,twoside]{article} +\def\TITLE{Tunnels over IP} +\input preamble +\begin{center} +\Large\bf Tunnels over IP in Linux-2.2 +\end{center} + + +\begin{center} +{ \large Alexey~N.~Kuznetsov } \\ +\em Institute for Nuclear Research, Moscow \\ +\verb|kuznet@ms2.inr.ac.ru| \\ +\rm March 17, 1999 +\end{center} + +\vspace{5mm} + +\tableofcontents + + +\section{Instead of introduction: micro-FAQ.} + +\begin{itemize} + +\item +Q: In linux-2.0.36 I used: +\begin{verbatim} + ifconfig tunl1 10.0.0.1 pointopoint 193.233.7.65 +\end{verbatim} +to create tunnel. It does not work in 2.2.0! + +A: You are right, it does not work. The command written above is split to two commands. +\begin{verbatim} + ip tunnel add MY-TUNNEL mode ipip remote 193.233.7.65 +\end{verbatim} +will create tunnel device with name \verb|MY-TUNNEL|. Now you may configure +it with: +\begin{verbatim} + ifconfig MY-TUNNEL 10.0.0.1 +\end{verbatim} +Certainly, if you prefer name \verb|tunl1| to \verb|MY-TUNNEL|, +you still may use it. + +\item +Q: In linux-2.0.36 I used: +\begin{verbatim} + ifconfig tunl0 10.0.0.1 + route add -net 10.0.0.0 gw 193.233.7.65 dev tunl0 +\end{verbatim} +to tunnel net 10.0.0.0 via router 193.233.7.65. It does not +work in 2.2.0! Moreover, \verb|route| prints a funny error sort of +``network unreachable'' and after this I found a strange direct route +to 10.0.0.0 via \verb|tunl0| in routing table. + +A: Yes, in 2.2 the rule that {\em normal} gateway must reside on directly +connected network has not any exceptions. You may tell kernel, that +this particular route is {\em abnormal}: +\begin{verbatim} + ifconfig tunl0 10.0.0.1 netmask 255.255.255.255 + ip route add 10.0.0.0/8 via 193.233.7.65 dev tunl0 onlink +\end{verbatim} +Note keyword \verb|onlink|, it is the magic key that orders kernel +not to check for consistency of gateway address. +Probably, after this explanation you have already guessed another method +to cheat kernel: +\begin{verbatim} + ifconfig tunl0 10.0.0.1 netmask 255.255.255.255 + route add -host 193.233.7.65 dev tunl0 + route add -net 10.0.0.0 netmask 255.0.0.0 gw 193.233.7.65 + route del -host 193.233.7.65 dev tunl0 +\end{verbatim} +Well, if you like such tricks, nobody may prohibit you to use them. +Only do not forget +that between \verb|route add| and \verb|route del| host 193.233.7.65 is +unreachable. + +\item +Q: In 2.0.36 I used to load \verb|tunnel| device module and \verb|ipip| module. +I cannot find any \verb|tunnel| in 2.2! + +A: Linux-2.2 has single module \verb|ipip| for both directions of tunneling +and for all IPIP tunnel devices. + +\item +Q: \verb|traceroute| does not work over tunnel! Well, stop... It works, + only skips some number of hops. + +A: Yes. By default tunnel driver copies \verb|ttl| value from +inner packet to outer one. It means that path traversed by tunneled +packets to another endpoint is not hidden. If you dislike this, or if you +are going to use some routing protocol expecting that packets +with ttl 1 will reach peering host (f.e.\ RIP, OSPF or EBGP) +and you are not afraid of +tunnel loops, you may append option \verb|ttl 64|, when creating tunnel +with \verb|ip tunnel add|. + +\item +Q: ... Well, list of things, which 2.0 was able to do finishes. + +\end{itemize} + +\paragraph{Summary of differences between 2.2 and 2.0.} + +\begin{itemize} + +\item {\bf In 2.0} you could compile tunnel device into kernel + and got set of 4 devices \verb|tunl0| ... \verb|tunl3| or, + alternatively, compile it as module and load new module + for each new tunnel. Also, module \verb|ipip| was necessary + to receive tunneled packets. + + {\bf 2.2} has {\em one\/} module \verb|ipip|. Loading it you get base + tunnel device \verb|tunl0| and another tunnels may be created with command + \verb|ip tunnel add|. These new devices may have arbitrary names. + + +\item {\bf In 2.0} you set remote tunnel endpoint address with + the command \verb|ifconfig| ... \verb|pointopoint A|. + + {\bf In 2.2} this command has the same semantics on all + the interfaces, namely it sets not tunnel endpoint, + but address of peering host, which is directly reachable + via this tunnel, + rather than via Internet. Actual tunnel endpoint address \verb|A| + should be set with \verb|ip tunnel add ... remote A|. + +\item {\bf In 2.0} you create tunnel routes with the command: +\begin{verbatim} + route add -net 10.0.0.0 gw A dev tunl0 +\end{verbatim} + + {\bf 2.2} interprets this command equally for all device + kinds and gateway is required to be directly reachable via this tunnel, + rather than via Internet. You still may use \verb|ip route add ... onlink| + to override this behaviour. + +\end{itemize} + + +\section{Tunnel setup: basics} + +Standard Linux-2.2 kernel supports three flavor of tunnels, +listed in the following table: +\vspace{2mm} + +\begin{tabular}{lll} +\vrule depth 0.8ex width 0pt\relax +Mode & Description & Base device \\ +ipip & IP over IP & tunl0 \\ +sit & IPv6 over IP & sit0 \\ +gre & ANY over GRE over IP & gre0 +\end{tabular} + +\vspace{2mm} + +\noindent All the kinds of tunnels are created with one command: +\begin{verbatim} + ip tunnel add <NAME> mode <MODE> [ local <S> ] [ remote <D> ] +\end{verbatim} + +This command creates new tunnel device with name \verb|<NAME>|. +The \verb|<NAME>| is an arbitrary string. Particularly, +it may be even \verb|eth0|. The rest of parameters set +different tunnel characteristics. + +\begin{itemize} + +\item +\verb|mode <MODE>| sets tunnel mode. Three modes are available now + \verb|ipip|, \verb|sit| and \verb|gre|. + +\item +\verb|remote <D>| sets remote endpoint of the tunnel to IP + address \verb|<D>|. +\item +\verb|local <S>| sets fixed local address for tunneled + packets. It must be an address on another interface of this host. + +\end{itemize} + +\let\thefootnote\oldthefootnote + +Both \verb|remote| and \verb|local| may be omitted. In this case we +say that they are zero or wildcard. Two tunnels of one mode cannot +have the same \verb|remote| and \verb|local|. Particularly it means +that base device or fallback tunnel cannot be replicated.\footnote{ +This restriction is relaxed for keyed GRE tunnels.} + +Tunnels are divided to two classes: {\bf pointopoint} tunnels, which +have some not wildcard \verb|remote| address and deliver all the packets +to this destination, and {\bf NBMA} (i.e. Non-Broadcast Multi-Access) tunnels, +which have no \verb|remote|. Particularly, base devices (f.e.\ \verb|tunl0|) +are NBMA, because they have neither \verb|remote| nor +\verb|local| addresses. + + +After tunnel device is created you should configure it as you did +it with another devices. Certainly, the configuration of tunnels has +some features related to the fact that they work over existing Internet +routing infrastructure and simultaneously create new virtual links, +which changes this infrastructure. The danger that not enough careful +tunnel setup will result in formation of tunnel loops, +collapse of routing or flooding network with exponentially +growing number of tunneled fragments is very real. + + +Protocol setup on pointopoint tunnels does not differ of configuration +of another devices. You should set a protocol address with \verb|ifconfig| +and add routes with \verb|route| utility. + +NBMA tunnels are different. To route something via NBMA tunnel +you have to explain to driver, where it should deliver packets to. +The only way to make it is to create special routes with gateway +address pointing to desired endpoint. F.e.\ +\begin{verbatim} + ip route add 10.0.0.0/24 via <A> dev tunl0 onlink +\end{verbatim} +It is important to use option \verb|onlink|, otherwise +kernel will refuse request to create route via gateway not directly +reachable over device \verb|tunl0|. With IPv6 the situation is much simpler: +when you start device \verb|sit0|, it automatically configures itself +with all IPv4 addresses mapped to IPv6 space, so that all IPv4 +Internet is {\em really reachable} via \verb|sit0|! Excellent, the command +\begin{verbatim} + ip route add 3FFE::/16 via ::193.233.7.65 dev sit0 +\end{verbatim} +will route \verb|3FFE::/16| via \verb|sit0|, sending all the packets +destined to this prefix to 193.233.7.65. + +\section{Tunnel setup: options} + +Command \verb|ip tunnel add| has several additional options. +\begin{itemize} + +\item \verb|ttl N| --- set fixed TTL \verb|N| on tunneled packets. + \verb|N| is number in the range 1--255. 0 is special value, + meaning that packets inherit TTL value. + Default value is: \verb|inherit|. + +\item \verb|tos T| --- set fixed tos \verb|T| on tunneled packets. + Default value is: \verb|inherit|. + +\item \verb|dev DEV| --- bind tunnel to device \verb|DEV|, so that + tunneled packets will be routed only via this device and will + not be able to escape to another device, when route to endpoint changes. + +\item \verb|nopmtudisc| --- disable Path MTU Discovery on this tunnel. + It is enabled by default. Note that fixed ttl is incompatible + with this option: tunnels with fixed ttl always make pmtu discovery. + +\end{itemize} + +\verb|ipip| and \verb|sit| tunnels have no more options. \verb|gre| +tunnels are more complicated: + +\begin{itemize} + +\item \verb|key K| --- use keyed GRE with key \verb|K|. \verb|K| is + either number or IP address-like dotted quad. + +\item \verb|csum| --- checksum tunneled packets. + +\item \verb|seq| --- serialize packets. +\begin{NB} + I think this option does not + work. At least, I did not test it, did not debug it and + even do not understand, how it is supposed to work and for what + purpose Cisco planned to use it. +\end{NB} + +\end{itemize} + + +Actually, these GRE options can be set separately for input and +output directions by prefixing corresponding keywords with letter +\verb|i| or \verb|o|. F.e.\ \verb|icsum| orders to accept only +packets with correct checksum and \verb|ocsum| means, that +our host will calculate and send checksum. + +Command \verb|ip tunnel add| is not the only operation, +which can be made with tunnels. Certainly, you may get short help page +with: +\begin{verbatim} + ip tunnel help +\end{verbatim} + +Besides that, you may view list of installed tunnels with the help of command: +\begin{verbatim} + ip tunnel ls +\end{verbatim} +Also you may look at statistics: +\begin{verbatim} + ip -s tunnel ls Cisco +\end{verbatim} +where \verb|Cisco| is name of tunnel device. Command +\begin{verbatim} + ip tunnel del Cisco +\end{verbatim} +destroys tunnel \verb|Cisco|. And, finally, +\begin{verbatim} + ip tunnel change Cisco mode sit local ME remote HE ttl 32 +\end{verbatim} +changes its parameters. + +\section{Differences 2.2 and 2.0 tunnels revisited.} + +Now we can discuss more subtle differences between tunneling in 2.0 +and 2.2. + +\begin{itemize} + +\item In 2.0 all tunneled packets were received promiscuously +as soon as you loaded module \verb|ipip|. 2.2 tries to select the best +tunnel device and packet looks as received on this. F.e.\ if host +received \verb|ipip| packet from host \verb|D| destined to our +local address \verb|S|, kernel searches for matching tunnels +in order: + +\begin{tabular}{ll} +1 & \verb|remote| is \verb|D| and \verb|local| is \verb|S| \\ +2 & \verb|remote| is \verb|D| and \verb|local| is wildcard \\ +3 & \verb|remote| is wildcard and \verb|local| is \verb|S| \\ +4 & \verb|tunl0| +\end{tabular} + +If tunnel exists, but it is not in \verb|UP| state, the tunnel is ignored. +Note, that if \verb|tunl0| is \verb|UP| it receives all the IPIP packets, +not acknowledged by more specific tunnels. +Be careful, it means that without carefully installed firewall rules +anyone on the Internet may inject to your network any packets with +source addresses indistinguishable from local ones. It is not so bad idea +to design tunnels in the way enforcing maximal route symmetry +and to enable reversed path filter (\verb|rp_filter| sysctl option) on +tunnel devices. + +\item In 2.2 you can monitor and debug tunnels with \verb|tcpdump|. +F.e.\ \verb|tcpdump| \verb|-i Cisco| \verb|-nvv| will dump packets, +which kernel output, via tunnel \verb|Cisco| and the packets received on it +from kernel viewpoint. + +\end{itemize} + + +\section{Linux and Cisco IOS tunnels.} + +Among another tunnels Cisco IOS supports IPIP and GRE. +Essentially, Cisco setup is subset of options, available for Linux. +Let us consider the simplest example: + +\begin{verbatim} +interface Tunnel0 + tunnel mode gre ip + tunnel source 10.10.14.1 + tunnel destination 10.10.13.2 +\end{verbatim} + + +This command set translates to: + +\begin{verbatim} + ip tunnel add Tunnel0 \ + mode gre \ + local 10.10.14.1 \ + remote 10.10.13.2 +\end{verbatim} + +Any questions? No questions. + +\section{Interaction IPIP tunnels and DVMRP.} + +DVMRP exploits IPIP tunnels to route multicasts via Internet. +\verb|mrouted| creates +IPIP tunnels listed in its configuration file automatically. +From kernel and user viewpoints there are no differences between +tunnels, created in this way, and tunnels created by \verb|ip tunnel|. +I.e.\ if \verb|mrouted| created some tunnel, it may be used to +route unicast packets, provided appropriate routes are added. +And vice versa, if administrator has already created a tunnel, +it will be reused by \verb|mrouted|, if it requests DVMRP +tunnel with the same local and remote addresses. + +Do not wonder, if your manually configured tunnel is +destroyed, when mrouted exits. + + +\section{Broadcast GRE ``tunnels''.} + +It is possible to set \verb|remote| for GRE tunnel to a multicast +address. Such tunnel becomes {\bf broadcast} tunnel (though word +tunnel is not quite appropriate in this case, it is rather virtual network). +\begin{verbatim} + ip tunnel add Universe local 193.233.7.65 \ + remote 224.66.66.66 ttl 16 + ip addr add 10.0.0.1/16 dev Universe + ip link set Universe up +\end{verbatim} +This tunnel is true broadcast network and broadcast packets are +sent to multicast group 224.66.66.66. By default such tunnel starts +to resolve both IP and IPv6 addresses via ARP/NDISC, so that +if multicast routing is supported in surrounding network, all GRE nodes +will find one another automatically and will form virtual Ethernet-like +broadcast network. If multicast routing does not work, it is unpleasant +but not fatal flaw. The tunnel becomes NBMA rather than broadcast network. +You may disable dynamic ARPing by: +\begin{verbatim} + echo 0 > /proc/sys/net/ipv4/neigh/Universe/mcast_solicit +\end{verbatim} +and to add required information to ARP tables manually: +\begin{verbatim} + ip neigh add 10.0.0.2 lladdr 128.6.190.2 dev Universe nud permanent +\end{verbatim} +In this case packets sent to 10.0.0.2 will be encapsulated in GRE +and sent to 128.6.190.2. It is possible to facilitate address resolution +using methods typical for another NBMA networks f.e.\ to start user +level \verb|arpd| daemon, which will maintain database of hosts attached +to GRE virtual network or ask for information +dedicated ARP or NHRP server. + + +Actually, such setup is the most natural for tunneling, +it is really flexible, scalable and easily managable, so that +it is strongly recommended to be used with GRE tunnels instead of ugly +hack with NBMA mode and \verb|onlink| modifier. Unfortunately, +by historical reasons broadcast mode is not supported by IPIP tunnels, +but this probably will change in future. + + + +\section{Traffic control issues.} + +Tunnels are devices, hence all the power of Linux traffic control +applies to them. The simplest (and the most useful in practice) +example is limiting tunnel bandwidth. The following command: +\begin{verbatim} + tc qdisc add dev tunl0 root tbf \ + rate 128Kbit burst 4K limit 10K +\end{verbatim} +will limit tunneled traffic to 128Kbit with maximal burst size of 4K +and queuing not more than 10K. + +However, you should remember, that tunnels are {\em virtual} devices +implemented in software and true queue management is impossible for them +just because they have no queues. Instead, it is better to create classes +on real physical interfaces and to map tunneled packets to them. +In general case of dynamic routing you should create such classes +on all outgoing interfaces, or, alternatively, +to use option \verb|dev DEV| to bind tunnel to a fixed physical device. +In the last case packets will be routed only via specified device +and you need to setup corresponding classes only on it. +Though you have to pay for this convenience, +if routing will change, your tunnel will fail. + +Suppose that CBQ class \verb|1:ABC| has been created on device \verb|eth0| +specially for tunnel \verb|Cisco| with endpoints \verb|S| and \verb|D|. +Now you can select IPIP packets with addresses \verb|S| and \verb|D| +with some classifier and map them to class \verb|1:ABC|. F.e.\ +it is easy to make with \verb|rsvp| classifier: +\begin{verbatim} + tc filter add dev eth0 pref 100 proto ip rsvp \ + session D ipproto ipip filter S \ + classid 1:ABC +\end{verbatim} + +If you want to make more detailed classification of sub-flows +transmitted via tunnel, you can build CBQ subtree, +rooted at \verb|1:ABC| and attach to subroot set of rules parsing +IPIP packets more deeply. + +\end{document} diff --git a/doc/nstat.sgml b/doc/nstat.sgml new file mode 100644 index 0000000..be9d8bc --- /dev/null +++ b/doc/nstat.sgml @@ -0,0 +1,110 @@ +<!doctype linuxdoc system> + +<article> + +<title>NSTAT, IFSTAT and RTACCT Utilities +<author>Alexey Kuznetosv, <tt/kuznet@ms2.inr.ac.ru/ +<date>some_negative_number, 20 Sep 2001 +<abstract> +<tt/nstat/, <tt/ifstat/ and <tt/rtacct/ are simple tools helping +to monitor kernel snmp counters and network interface statistics. +</abstract> + +<p> These utilities are very similar, so that I describe +them simultaneously, using name <tt/Xstat/ in the places which apply +to all of them. + +<p>The format of the command is: + +<tscreen><verb> + Xstat [ OPTIONS ] [ PATTERN [ PATTERN ... ] ] +</verb></tscreen> + +<p> +<tt/PATTERN/ is shell style pattern, selecting identifier +of SNMP variables or interfaces to show. Variable is displayed +if one of patterns matches its name. If no patterns are given, +<tt/Xstat/ assumes that user wants to see all the variables. + +<p> <tt/OPTIONS/ is list of single letter options, using common unix +conventions. + +<itemize> +<item><tt/-h/ - show help page +<item><tt/-?/ - the same, of course +<item><tt/-v/, <tt/-V/ - print version of <tt/Xstat/ and exit +<item><tt/-z/ - dump zero counters too. By default they are not shown. +<item><tt/-a/ - dump absolute values of counters. By default <tt/Xstat/ + calculates increments since the previous use. +<item><tt/-s/ - do not update history, so that the next time you will + see counters including values accumulated to the moment + of this measurement too. +<item><tt/-n/ - do not display anything, only update history. +<item><tt/-r/ - reset history. +<item><tt/-d INTERVAL/ - <tt/Xstat/ is run in daemon mode collecting + statistics. <tt/INTERVAL/ is interval between measurements + in seconds. +<item><tt/-t INTERVAL/ - time interval to average rates. Default value + is 60 seconds. +<item><tt/-e/ - display extended information about errors (<tt/ifstat/ only). +</itemize> + +<p> +History is just dump saved in file <tt>/tmp/.Xstat.uUID</tt> +or in file given by environment variables <tt/NSTAT_HISTORY/, +<tt/IFSTAT_HISTORY/ and <tt/RTACCT_HISTORY/. +Each time when you use <tt/Xstat/ values there are updated. +If you use patterns, only the values which you _really_ see +are updated. If you want to skip an unintersting period, +use option <tt/-n/, or just output to <tt>/dev/null</tt>. + +<p> +<tt/Xstat/ understands when history is invalidated by system reboot +or source of information switched between different instances +of daemonic <tt/Xstat/ and kernel SNMP tables and does not +use invalid history. + +<p> Beware, <tt/Xstat/ will not produce sane output, +when many processes use it simultaneously. If several processes +under single user need this utility they should use environment +variables to put their history in safe places +or to use it with options <tt/-a -s/. + +<p> +Well, that's all. The utility is very simple, but nevertheless +very handy. + +<p> <bf/Output of XSTAT/ +<p> The first line of output is <tt/#/ followed by identifier +of source of information, it may be word <tt/kernel/, when <tt/Xstat/ +gets information from kernel or some dotted decimal number followed +by parameters, when it obtains information from running <tt/Xstat/ daemon. + +<p>In the case of <tt/nstat/ the rest of output consists of three columns: +SNMP MIB identifier, +its value (or increment since previous measurement) and average +rate of increase of the counter per second. <tt/ifstat/ outputs +interface name followed by pairs of counter and rate of its change. + +<p> <bf/Daemonic Xstat/ +<p> <tt/Xstat/ may be started as daemon by any user. This makes sense +to avoid wrapped counters and to obtain reasonable long counters +for large time. Also <tt/Xstat/ daemon calculates average rates. +For the first goal sampling interval (option <tt/-d/) may be large enough, +f.e. for gigabit rates byte counters overflow not more frequently than +each 40 seconds and you may select interval of 20 seconds. +From the other hand, when <tt/Xstat/ is used for estimating rates +interval should be less than averaging period (option <tt/-t/), otherwise +estimation loses in quality. + +Client <tt/Xstat/, before trying to get information from the kernel, +contacts daemon started by this user, then it tries system wide +daemon, which is supposed to be started by superuser. And only if +none of them replied it gets information from kernel. + +<p> <bf/Environment/ +<p> <tt/NSTAT_HISTORY/ - name of history file for <tt/nstat/. +<p> <tt/IFSTAT_HISTORY/ - name of history file for <tt/ifstat/. +<p> <tt/RTACCT_HISTORY/ - name of history file for <tt/rtacct/. + +</article> diff --git a/doc/preamble.tex b/doc/preamble.tex new file mode 100644 index 0000000..80ca508 --- /dev/null +++ b/doc/preamble.tex @@ -0,0 +1,26 @@ +\textwidth 6.0in +\textheight 8.5in + +\input SNAPSHOT + +\pagestyle{myheadings} +\markboth{\protect\TITLE}{} +\markright{{\protect\sc iproute2-ss\Draft}} + +% To print it in compact form: both sides on one sheet (psnup -2) +\evensidemargin=\oddsidemargin + +\newenvironment{NB}{\bgroup \vskip 1mm\leftskip 1cm \footnotesize \noindent NB. +}{\par\egroup \vskip 1mm} + +\def\threeonly{[2.3.15+ only] } + +\begin{document} + +\makeatletter +\renewcommand{\@oddhead}{{\protect\sc iproute2-ss\Draft} \hfill \protect\arabic{page}} +\makeatother +\let\oldthefootnote\thefootnote +\def\thefootnote{} +\footnotetext{Copyright \copyright~1999 A.N.Kuznetsov} + diff --git a/doc/rtstat.sgml b/doc/rtstat.sgml new file mode 100644 index 0000000..07391c3 --- /dev/null +++ b/doc/rtstat.sgml @@ -0,0 +1,52 @@ +<!doctype linuxdoc system> + +<article> + +<title>RTACCT Utility +<author>Robert Olsson +<date>some_negative_number, 20 Dec 2001 + +<p> +Here is some code for monitoring the route cache. For systems handling high +network load, servers, routers, firewalls etc the route cache and its garbage +collection is crucial. Linux has a solid implementation. + +<p> +The kernel patch (not required since linux-2.4.7) adds statistics counters +from route cache process into +/proc/net/rt_cache_stat. A companion user mode program presents the statistics +in a vmstat or iostat manner. The ratio between cache hits and misses gives +the flow length. + +<p> +Hopefully it can help understanding performance and DoS and other related +issues. + +<p> An URL where newer versions of this utility can be (probably) found +is ftp://robur.slu.se/pub/Linux/net-development/rt_cache_stat/ + + +<p><bf/Description/ + +<p>The format of the command is: + +<tscreen><verb> + rtstat [ OPTIONS ] +</verb></tscreen> + +<p> <tt/OPTIONS/ are: + +<itemize> + +<item><tt/-h/, <tt/-help/ - show help page and version of the utility. + +<item><tt/-i INTERVAL/ - interval between snapshots, default value is +2 seconds. + +<item><tt/-s NUMBER/ - whether to print header line. 0 inhibits header line, +1 prescribes to print it once and 2 (this is default setting) forces header +line each 20 lines. + +</itemize> + +</article> diff --git a/doc/ss.sgml b/doc/ss.sgml new file mode 100644 index 0000000..0b1b533 --- /dev/null +++ b/doc/ss.sgml @@ -0,0 +1,525 @@ +<!doctype linuxdoc system> + +<article> + +<title>SS Utility: Quick Intro +<author>Alexey Kuznetosv, <tt/kuznet@ms2.inr.ac.ru/ +<date>some_negative_number, 20 Sep 2001 +<abstract> +<tt/ss/ is one another utility to investigate sockets. +Functionally it is NOT better than <tt/netstat/ combined +with some perl/awk scripts and though it is surely faster +it is not enough to make it much better. :-) +So, stop reading this now and do not waste your time. +Well, certainly, it proposes some functionality, which current +netstat is still not able to do, but surely will soon. +</abstract> + +<sect>Why? + +<p> <tt>/proc</tt> interface is inadequate, unfortunately. +When amount of sockets is enough large, <tt/netstat/ or even +plain <tt>cat /proc/net/tcp/</tt> cause nothing but pains and curses. +In linux-2.4 the desease became worse: even if amount +of sockets is small reading <tt>/proc/net/tcp/</tt> is slow enough. + +This utility presents a new approach, which is supposed to scale +well. I am not going to describe technical details here and +will concentrate on description of the command. +The only important thing to say is that it is not so bad idea +to load module <tt/tcp_diag/, which can be found in directory +<tt/Modules/ of <tt/iproute2/. If you do not make this <tt/ss/ +will work, but it falls back to <tt>/proc</tt> and becomes slow +like <tt/netstat/, well, a bit faster yet (see section "Some numbers"). + +<sect>Old news + +<p> +In the simplest form <tt/ss/ is equivalent to netstat +with some small deviations. + +<itemize> +<item><tt/ss -t -a/ dumps all TCP sockets +<item><tt/ss -u -a/ dumps all UDP sockets +<item><tt/ss -w -a/ dumps all RAW sockets +<item><tt/ss -x -a/ dumps all UNIX sockets +</itemize> + +<p> +Option <tt/-o/ shows TCP timers state. +Option <tt/-e/ shows some extended information. +Etc. etc. etc. Seems, all the options of netstat related to sockets +are supported. Though not AX.25 and other bizarres. :-) +If someone wants, he can make support for decnet and ipx. +Some rudimentary support for them is already present in iproute2 libutils, +and I will be glad to see these new members. + +<p> +However, standard functionality is a bit different: + +<p> +The first: without option <tt/-a/ sockets in states +<tt/TIME-WAIT/ and <tt/SYN-RECV/ are skipped too. +It is more reasonable default, I think. + +<p> +The second: format of UNIX sockets is different. It coincides +with tcp/udp. Though standard kernel still does not allow to +see write/read queues and peer address of connected UNIX sockets, +the patch doing this exists. + +<p> +The third: default is to dump only TCP sockets, rather than all of the types. + +<p> +The next: by default it does not resolve numeric host addresses (like <tt/ip/)! +Resolving is enabled with option <tt/-r/. Service names, usually stored +in local files, are resolved by default. Also, if service database +does not contain references to a port, <tt/ss/ queries system +<tt/rpcbind/. RPC services are prefixed with <tt/rpc./ +Resolution of services may be suppressed with option <tt/-n/. + +<p> +It does not accept "long" options (I dislike them, sorry). +So, address family is given with family identifier following +option <tt/-f/ to be algined to iproute2 conventions. +Mostly, it is to allow option parser to parse +addresses correctly, but as side effect it really limits dumping +to sockets supporting only given family. Option <tt/-A/ followed +by list of socket tables to dump is also supported. +Logically, id of socket table is different of _address_ family, which is +another point of incompatibility. So, id is one of +<tt/all/, <tt/tcp/, <tt/udp/, +<tt/raw/, <tt/inet/, <tt/unix/, <tt/packet/, <tt/netlink/. See? +Well, <tt/inet/ is just abbreviation for <tt/tcp|udp|raw/ +and it is not difficult to guess that <tt/packet/ allows +to look at packet sockets. Actually, there are also some other abbreviations, +f.e. <tt/unix_dgram/ selects only datagram UNIX sockets. + +<p> +The next: well, I still do not know. :-) + + + + +<sect>Time to talk about new functionality. + +<p>It is builtin filtering of socket lists. + +<sect1> Filtering by state. + +<p> +<tt/ss/ allows to filter socket states, using keywords +<tt/state/ and <tt/exclude/, followed by some state +identifier. + +<p> +State identifier are standard TCP state names (not listed, +they are useless for you if you already do not know them) +or abbreviations: + +<itemize> +<item><tt/all/ - for all the states +<item><tt/bucket/ - for TCP minisockets (<tt/TIME-WAIT|SYN-RECV/) +<item><tt/big/ - all except for minisockets +<item><tt/connected/ - not closed and not listening +<item><tt/synchronized/ - connected and not <tt/SYN-SENT/ +</itemize> + +<p> + F.e. to dump all tcp sockets except <tt/SYN-RECV/: + +<tscreen><verb> + ss exclude SYN-RECV +</verb></tscreen> + +<p> + If neither <tt/state/ nor <tt/exclude/ directives + are present, + state filter defaults to <tt/all/ with option <tt/-a/ + or to <tt/all/, + excluding listening, syn-recv, time-wait and closed sockets. + +<sect1> Filtering by addresses and ports. + +<p> +Option list may contain address/port filter. +It is boolean expression which consists of boolean operation +<tt/or/, <tt/and/, <tt/not/ and predicates. +Actually, all the flavors of names for boolean operations are eaten: +<tt/&/, <tt/&&/, <tt/|/, <tt/||/, <tt/!/, but do not forget +about special sense given to these symbols by unix shells and escape +them correctly, when used from command line. + +<p> +Predicates may be of the folowing kinds: + +<itemize> +<item>A. Address/port match, where address is checked against mask + and port is either wildcard or exact. It is one of: + +<tscreen><verb> + dst prefix:port + src prefix:port + src unix:STRING + src link:protocol:ifindex + src nl:channel:pid +</verb></tscreen> + + Both prefix and port may be absent or replaced with <tt/*/, + which means wildcard. UNIX socket use more powerful scheme + matching to socket names by shell wildcards. Also, prefixes + unix: and link: may be omitted, if address family is evident + from context (with option <tt/-x/ or with <tt/-f unix/ + or with <tt/unix/ keyword) + +<p> + F.e. + +<tscreen><verb> + dst 10.0.0.1 + dst 10.0.0.1: + dst 10.0.0.1/32: + dst 10.0.0.1:* +</verb></tscreen> + are equivalent and mean socket connected to + any port on host 10.0.0.1 + +<tscreen><verb> + dst 10.0.0.0/24:22 +</verb></tscreen> + sockets connected to port 22 on network + 10.0.0.0...255. + +<p> + Note that port separated of address with colon, which creates + troubles with IPv6 addresses. Generally, we interpret the last + colon as splitting port. To allow to give IPv6 addresses, + trick like used in IPv6 HTTP URLs may be used: + +<tscreen><verb> + dst [::1] +</verb></tscreen> + are sockets connected to ::1 on any port + +<p> + Another way is <tt/dst ::1/128/. / helps to understand that + colon is part of IPv6 address. + +<p> + Now we can add another alias for <tt/dst 10.0.0.1/: + <tt/dst [10.0.0.1]/. :-) + +<p> Address may be a DNS name. In this case all the addresses are looked + up (in all the address families, if it is not limited by option <tt/-f/ + or special address prefix <tt/inet:/, <tt/inet6/) and resulting + expression is <tt/or/ over all of them. + +<item> B. Port expressions: +<tscreen><verb> + dport >= :1024 + dport != :22 + sport < :32000 +</verb></tscreen> + etc. + + All the relations: <tt/</, <tt/>/, <tt/=/, <tt/>=/, <tt/=/, <tt/==/, + <tt/!=/, <tt/eq/, <tt/ge/, <tt/lt/, <tt/ne/... + Use variant which you like more, but not forget to escape special + characters when typing them in command line. :-) + + Note that port number syntactically coincides to the case A! + You may even add an IP address, but it will not participate + incomparison, except for <tt/==/ and <tt/!=/, which are equivalent + to corresponding predicates of type A. F.e. +<p> +<tt/dst 10.0.0.1:22/ + is equivalent to <tt/dport eq 10.0.0.1:22/ + and + <tt/not dst 10.0.0.1:22/ is equivalent to + <tt/dport neq 10.0.0.1:22/ + +<item>C. Keyword <tt/autobound/. It matches to sockets bound automatically + on local system. + +</itemize> + + +<sect> Examples + +<p> +<itemize> +<item>1. List all the tcp sockets in state <tt/FIN-WAIT-1/ for our apache + to network 193.233.7/24 and look at their timers: + +<tscreen><verb> + ss -o state fin-wait-1 \( sport = :http or sport = :https \) \ + dst 193.233.7/24 +</verb></tscreen> + + Oops, forgot to say that missing logical operation is + equivalent to <tt/and/. + +<item> 2. Well, now look at the rest... + +<tscreen><verb> + ss -o excl fin-wait-1 + ss state fin-wait-1 \( sport neq :http and sport neq :https \) \ + or not dst 193.233.7/24 +</verb></tscreen> + + Note that we have to do _two_ calls of ss to do this. + State match is always anded to address/port match. + The reason for this is purely technical: ss does fast skip of + not matching states before parsing addresses and I consider the + ability to skip fastly gobs of time-wait and syn-recv sockets + as more important than logical generality. + +<item> 3. So, let's look at all our sockets using autobound ports: + +<tscreen><verb> + ss -a -A all autobound +</verb></tscreen> + + +<item> 4. And eventually find all the local processes connected + to local X servers: + +<tscreen><verb> + ss -xp dst "/tmp/.X11-unix/*" +</verb></tscreen> + + Pardon, this does not work with current kernel, patching is required. + But we still can look at server side: + +<tscreen><verb> + ss -x src "/tmp/.X11-unix/*" +</verb></tscreen> + +</itemize> + + +<sect> Returning to ground: real manual + +<p> +<sect1> Command arguments + +<p> General format of arguments to <tt/ss/ is: + +<tscreen><verb> + ss [ OPTIONS ] [ STATE-FILTER ] [ ADDRESS-FILTER ] +</verb></tscreen> + +<sect2><tt/OPTIONS/ +<p> <tt/OPTIONS/ is list of single letter options, using common unix +conventions. + +<itemize> +<item><tt/-h/ - show help page +<item><tt/-?/ - the same, of course +<item><tt/-v/, <tt/-V/ - print version of <tt/ss/ and exit +<item><tt/-s/ - print summary statistics. This option does not parse +socket lists obtaining summary from various sources. It is useful +when amount of sockets is so huge that parsing <tt>/proc/net/tcp</tt> +is painful. +<item><tt/-D FILE/ - do not display anything, just dump raw information +about TCP sockets to <tt/FILE/ after applying filters. If <tt/FILE/ is <tt/-/ +<tt/stdout/ is used. +<item><tt/-F FILE/ - read continuation of filter from <tt/FILE/. +Each line of <tt/FILE/ is interpreted like single command line option. +If <tt/FILE/ is <tt/-/ <tt/stdin/ is used. +<item><tt/-r/ - try to resolve numeric address/ports +<item><tt/-n/ - do not try to resolve ports +<item><tt/-o/ - show some optional information, f.e. TCP timers +<item><tt/-i/ - show some infomration specific to TCP (RTO, congestion +window, slow start threshould etc.) +<item><tt/-e/ - show even more optional information +<item><tt/-m/ - show extended information on memory used by the socket. +It is available only with <tt/tcp_diag/ enabled. +<item><tt/-p/ - show list of processes owning the socket +<item><tt/-f FAMILY/ - default address family used for parsing addresses. + Also this option limits listing to sockets supporting + given address family. Currently the following families + are supported: <tt/unix/, <tt/inet/, <tt/inet6/, <tt/link/, + <tt/netlink/. +<item><tt/-4/ - alias for <tt/-f inet/ +<item><tt/-6/ - alias for <tt/-f inet6/ +<item><tt/-0/ - alias for <tt/-f link/ +<item><tt/-A LIST-OF-TABLES/ - list of socket tables to dump, separated + by commas. The following identifiers are understood: + <tt/all/, <tt/inet/, <tt/tcp/, <tt/udp/, <tt/raw/, + <tt/unix/, <tt/packet/, <tt/netlink/, <tt/unix_dgram/, + <tt/unix_stream/, <tt/packet_raw/, <tt/packet_dgram/. +<item><tt/-x/ - alias for <tt/-A unix/ +<item><tt/-t/ - alias for <tt/-A tcp/ +<item><tt/-u/ - alias for <tt/-A udp/ +<item><tt/-w/ - alias for <tt/-A raw/ +<item><tt/-a/ - show sockets of all the states. By default sockets + in states <tt/LISTEN/, <tt/TIME-WAIT/, <tt/SYN_RECV/ + and <tt/CLOSE/ are skipped. +<item><tt/-l/ - show only sockets in state <tt/LISTEN/ +</itemize> + +<sect2><tt/STATE-FILTER/ + +<p><tt/STATE-FILTER/ allows to construct arbitrary set of +states to match. Its syntax is sequence of keywords <tt/state/ +and <tt/exclude/ followed by identifier of state. +Available identifiers are: + +<p> +<itemize> +<item> All standard TCP states: <tt/established/, <tt/syn-sent/, +<tt/syn-recv/, <tt/fin-wait-1/, <tt/fin-wait-2/, <tt/time-wait/, +<tt/closed/, <tt/close-wait/, <tt/last-ack/, <tt/listen/ and <tt/closing/. + +<item><tt/all/ - for all the states +<item><tt/connected/ - all the states except for <tt/listen/ and <tt/closed/ +<item><tt/synchronized/ - all the <tt/connected/ states except for +<tt/syn-sent/ +<item><tt/bucket/ - states, which are maintained as minisockets, i.e. +<tt/time-wait/ and <tt/syn-recv/. +<item><tt/big/ - opposite to <tt/bucket/ +</itemize> + +<sect2><tt/ADDRESS_FILTER/ + +<p><tt/ADDRESS_FILTER/ is boolean expression with operations <tt/and/, <tt/or/ +and <tt/not/, which can be abbreviated in C style f.e. as <tt/&/, +<tt/&&/. + +<p> +Predicates check socket addresses, both local and remote. +There are the following kinds of predicates: + +<itemize> +<item> <tt/dst ADDRESS_PATTERN/ - matches remote address and port +<item> <tt/src ADDRESS_PATTERN/ - matches local address and port +<item> <tt/dport RELOP PORT/ - compares remote port to a number +<item> <tt/sport RELOP PORT/ - compares local port to a number +<item> <tt/autobound/ - checks that socket is bound to an ephemeral + port +</itemize> + +<p><tt/RELOP/ is some of <tt/<=/, <tt/>=/, <tt/==/ etc. +To make this more convinient for use in unix shell, alphabetic +FORTRAN-like notations <tt/le/, <tt/gt/ etc. are accepted as well. + +<p>The format and semantics of <tt/ADDRESS_PATTERN/ depends on address +family. + +<itemize> +<item><tt/inet/ - <tt/ADDRESS_PATTERN/ consists of IP prefix, optionally +followed by colon and port. If prefix or port part is absent or replaced +with <tt/*/, this means wildcard match. +<item><tt/inet6/ - The same as <tt/inet/, only prefix refers to an IPv6 +address. Unlike <tt/inet/ colon becomes ambiguous, so that <tt/ss/ allows +to use scheme, like used in URLs, where address is suppounded with +<tt/[/ ... <tt/]/. +<item><tt/unix/ - <tt/ADDRESS_PATTERN/ is shell-style wildcard. +<item><tt/packet/ - format looks like <tt/inet/, only interface index +stays instead of port and link layer protocol id instead of address. +<item><tt/netlink/ - format looks like <tt/inet/, only socket pid +stays instead of port and netlink channel instead of address. +</itemize> + +<p><tt/PORT/ is syntactically <tt/ADDRESS_PATTERN/ with wildcard +address part. Certainly, it is undefined for UNIX sockets. + +<sect1> Environment variables + +<p> +<tt/ss/ allows to change source of information using various +environment variables: + +<p> +<itemize> +<item> <tt/PROC_SLABINFO/ to override <tt>/proc/slabinfo</tt> +<item> <tt/PROC_NET_TCP/ to override <tt>/proc/net/tcp</tt> +<item> <tt/PROC_NET_UDP/ to override <tt>/proc/net/udp</tt> +<item> etc. +</itemize> + +<p> +Variable <tt/PROC_ROOT/ allows to change root of all the <tt>/proc/</tt> +hierarchy. + +<p> +Variable <tt/TCPDIAG_FILE/ prescribes to open a file instead of +requesting kernel to dump information about TCP sockets. + + +<p> This option is used mainly to investigate bug reports, +when dumps of files usually found in <tt>/proc/</tt> are recevied +by e-mail. + +<sect1> Output format + +<p>Six columns. The first is <tt/Netid/, it denotes socket type and +transport protocol, when it is ambiguous: <tt/tcp/, <tt/udp/, <tt/raw/, +<tt/u_str/ is abbreviation for <tt/unix_stream/, <tt/u_dgr/ for UNIX +datagram sockets, <tt/nl/ for netlink, <tt/p_raw/ and <tt/p_dgr/ for +raw and datagram packet sockets. This column is optional, it will +be hidden, if filter selects an unique netid. + +<p> +The second column is <tt/State/. Socket state is displayed here. +The names are standard TCP names, except for <tt/UNCONN/, which +cannot happen for TCP, but normal for not connected sockets +of another types. Again, this column can be hidden. + +<p> +Then two columns (<tt/Recv-Q/ and <tt/Send-Q/) showing amount of data +queued for receive and transmit. + +<p> +And the last two columns display local address and port of the socket +and its peer address, if the socket is connected. + +<p> +If options <tt/-o/, <tt/-e/ or <tt/-p/ were given, options are +displayed not in fixed positions but separated by spaces pairs: +<tt/option:value/. If value is not a single number, it is presented +as list of values, enclosed to <tt/(/ ... <tt/)/ and separated with +commas. F.e. + +<tscreen><verb> + timer:(keepalive,111min,0) +</verb></tscreen> +is typical format for TCP timer (option <tt/-o/). + +<tscreen><verb> + users:((X,113,3)) +</verb></tscreen> +is typical for list of users (option <tt/-p/). + + +<sect>Some numbers + +<p> +Well, let us use <tt/pidentd/ and a tool <tt/ibench/ to measure +its performance. It is 30 requests per second here. Nothing to test, +it is too slow. OK, let us patch pidentd with patch from directory +Patches. After this it handles about 4300 requests per second +and becomes handy tool to pollute socket tables with lots of timewait +buckets. + +<p> +So, each test starts from pollution tables with 30000 sockets +and then doing full dump of the table piped to wc and measuring +timings with time: + +<p>Results: + +<itemize> +<item> <tt/netstat -at/ - 15.6 seconds +<item> <tt/ss -atr/, but without <tt/tcp_diag/ - 5.4 seconds +<item> <tt/ss -atr/ with <tt/tcp_diag/ - 0.47 seconds +</itemize> + +No comments. Though one comment is necessary, most of time +without <tt/tcp_diag/ is wasted inside kernel with completely +blocked networking. More than 10 seconds, yes. <tt/tcp_diag/ +does the same work for 100 milliseconds of system time. + +</article> diff --git a/etc/iproute2/rt_dsfield b/etc/iproute2/rt_dsfield new file mode 100644 index 0000000..2b36e49 --- /dev/null +++ b/etc/iproute2/rt_dsfield @@ -0,0 +1,15 @@ +#0x10 lowdelay +#0x08 throughput +#0x04 reliability + +# This value overlap with ECT, do not use it! +#0x02 mincost + +# These values seems do not want to die, Cisco likes them by a strange reason. +#0x20 priority +#0x40 immediate +#0x60 flash +#0x80 flash-override +#0xa0 critical +#0xc0 internet +#0xe0 network diff --git a/etc/iproute2/rt_dsfield.rt_config b/etc/iproute2/rt_dsfield.rt_config new file mode 100644 index 0000000..110061a --- /dev/null +++ b/etc/iproute2/rt_dsfield.rt_config @@ -0,0 +1,13 @@ +0x10 lowdelay +0x08 throughput +0x04 reliability +# This value overlap with ECT, do not use it! +0x02 mincost +# These values seems do not want to die, Cisco likes them by a strange reason. +0x20 priority +0x40 immediate +0x60 flash +0x80 flash-override +0xa0 critical +0xc0 internet +0xe0 network diff --git a/etc/iproute2/rt_protos b/etc/iproute2/rt_protos new file mode 100644 index 0000000..2569edf --- /dev/null +++ b/etc/iproute2/rt_protos @@ -0,0 +1,26 @@ +# +# Reserved protocols. +# +#0 unspec +#1 redirect +#2 kernel +#3 boot +#4 static +#8 gated +#9 ra +#10 mrt +#11 zebra +#12 bird + +# +# Used by me for gated +# +#254 gated/aggr +#253 gated/bgp +#252 gated/ospf +#251 gated/ospfase +#250 gated/rip +#249 gated/static +#248 gated/conn +#247 gated/inet +#246 gated/default diff --git a/etc/iproute2/rt_protos.rt_config b/etc/iproute2/rt_protos.rt_config new file mode 100644 index 0000000..8c985d7 --- /dev/null +++ b/etc/iproute2/rt_protos.rt_config @@ -0,0 +1,25 @@ +# +# Reserved protocols. +# +0 unspec +1 redirect +2 kernel +3 boot +4 static +8 gated +9 ra +10 mrt +11 zebra +12 bird +# +# Used by me for gated +# +254 gated/aggr +253 gated/bgp +252 gated/ospf +251 gated/ospfase +250 gated/rip +249 gated/static +248 gated/conn +247 gated/inet +246 gated/default diff --git a/etc/iproute2/rt_realms b/etc/iproute2/rt_realms new file mode 100644 index 0000000..332179d --- /dev/null +++ b/etc/iproute2/rt_realms @@ -0,0 +1,13 @@ +# +# reserved values +# +#0 cosmos +# +# local +# +#1 inr.ac +#2 inr.ruhep +#3 freenet +#4 radio-msu +#5 russia +#6 internet diff --git a/etc/iproute2/rt_realms.rt_config b/etc/iproute2/rt_realms.rt_config new file mode 100644 index 0000000..eedd76d --- /dev/null +++ b/etc/iproute2/rt_realms.rt_config @@ -0,0 +1,13 @@ +# +# reserved values +# +0 cosmos +# +# local +# +#1 inr.ac +#2 inr.ruhep +#3 freenet +#4 radio-msu +#5 russia +#6 internet diff --git a/etc/iproute2/rt_scopes b/etc/iproute2/rt_scopes new file mode 100644 index 0000000..36fbc01 --- /dev/null +++ b/etc/iproute2/rt_scopes @@ -0,0 +1,12 @@ +# +# reserved values +# +#0 global +#255 nowhere +#254 host +#253 link + +# +# pseudo-reserved +# +#200 site diff --git a/etc/iproute2/rt_scopes.rt_config b/etc/iproute2/rt_scopes.rt_config new file mode 100644 index 0000000..8514bc1 --- /dev/null +++ b/etc/iproute2/rt_scopes.rt_config @@ -0,0 +1,11 @@ +# +# reserved values +# +0 global +255 nowhere +254 host +253 link +# +# pseudo-reserved +# +200 site diff --git a/etc/iproute2/rt_tables b/etc/iproute2/rt_tables new file mode 100644 index 0000000..558716b --- /dev/null +++ b/etc/iproute2/rt_tables @@ -0,0 +1,11 @@ +# +# reserved values +# +#255 local +#254 main +#253 default +#0 unspec +# +# local +# +#1 inr.ruhep diff --git a/etc/iproute2/rt_tables.rt_config b/etc/iproute2/rt_tables.rt_config new file mode 100644 index 0000000..541abfd --- /dev/null +++ b/etc/iproute2/rt_tables.rt_config @@ -0,0 +1,11 @@ +# +# reserved values +# +255 local +254 main +253 default +0 unspec +# +# local +# +#1 inr.ruhep diff --git a/examples/SYN-DoS.rate.limit b/examples/SYN-DoS.rate.limit new file mode 100644 index 0000000..8766b67 --- /dev/null +++ b/examples/SYN-DoS.rate.limit @@ -0,0 +1,49 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# this script shows how one can rate limit incoming SYNs +# Useful for TCP-SYN attack protection. You can use +# IPchains to have more powerful additions to the SYN (eg +# in addition the subnet) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +# +# tag all incoming SYN packets through $INDEV as mark value 1 +############################################################ +$IPCHAINS -A input -i $INDEV -y -m 1 +############################################################ +# +# install the ingress qdisc on the ingress interface +############################################################ +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ + +# +# +# SYN packets are 40 bytes (320 bits) so three SYNs equals +# 960 bits (approximately 1kbit); so we rate limit below +# the incoming SYNs to 3/sec (not very sueful really; but +#serves to show the point - JHS +############################################################ +$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw \ +police rate 1kbit burst 40 mtu 9k drop flowid :1 +############################################################ + + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/cbqinit.eth1 b/examples/cbqinit.eth1 new file mode 100755 index 0000000..226ec1c --- /dev/null +++ b/examples/cbqinit.eth1 @@ -0,0 +1,76 @@ +#! /bin/sh + +TC=/home/root/tc +IP=/home/root/ip +DEVICE=eth1 +BANDWIDTH="bandwidth 10Mbit" + +# Attach CBQ on $DEVICE. It will have handle 1:. +# $BANDWIDTH is real $DEVICE bandwidth (10Mbit). +# avpkt is average packet size. +# mpu is minimal packet size. + +$TC qdisc add dev $DEVICE root handle 1: cbq \ +$BANDWIDTH avpkt 1000 mpu 64 + +# Create root class with classid 1:1. This step is not necessary. +# bandwidth is the same as on CBQ itself. +# rate == all the bandwidth +# allot is MTU + MAC header +# maxburst measure allowed class burstiness (please,read S.Floyd and VJ papers) +# est 1sec 8sec means, that kernel will evaluate average rate +# on this class with period 1sec and time constant 8sec. +# This rate is viewed with "tc -s class ls dev $DEVICE" + +$TC class add dev $DEVICE parent 1:0 classid :1 est 1sec 8sec cbq \ +$BANDWIDTH rate 10Mbit allot 1514 maxburst 50 avpkt 1000 + +# Bulk. +# New parameters are: +# weight, which is set to be proportional to +# "rate". It is not necessary, weight=1 will work as well. +# defmap and split say that best effort ttraffic, not classfied +# by another means will fall to this class. + +$TC class add dev $DEVICE parent 1:1 classid :2 est 1sec 8sec cbq \ +$BANDWIDTH rate 4Mbit allot 1514 weight 500Kbit \ +prio 6 maxburst 50 avpkt 1000 split 1:0 defmap ff3d + +# OPTIONAL. +# Attach "sfq" qdisc to this class, quantum is MTU, perturb +# gives period of hash function perturbation in seconds. +# +$TC qdisc add dev $DEVICE parent 1:2 sfq quantum 1514b perturb 15 + +# Interactive-burst class + +$TC class add dev $DEVICE parent 1:1 classid :3 est 2sec 16sec cbq \ +$BANDWIDTH rate 1Mbit allot 1514 weight 100Kbit \ +prio 2 maxburst 100 avpkt 1000 split 1:0 defmap c0 + +$TC qdisc add dev $DEVICE parent 1:3 sfq quantum 1514b perturb 15 + +# Background. + +$TC class add dev $DEVICE parent 1:1 classid :4 est 1sec 8sec cbq \ + $BANDWIDTH rate 100Kbit allot 1514 weight 10Mbit \ + prio 7 maxburst 10 avpkt 1000 split 1:0 defmap 2 + +$TC qdisc add dev $DEVICE parent 1:4 sfq quantum 1514b perturb 15 + +# Realtime class for RSVP + +$TC class add dev $DEVICE parent 1:1 classid 1:7FFE cbq \ +rate 5Mbit $BANDWIDTH allot 1514b avpkt 1000 \ +maxburst 20 + +# Reclassified realtime traffic +# +# New element: split is not 1:0, but 1:7FFE. It means, +# that only real-time packets, which violated policing filters +# or exceeded reshaping buffers will fall to it. + +$TC class add dev $DEVICE parent 1:7FFE classid 1:7FFF est 4sec 32sec cbq \ +rate 1Mbit $BANDWIDTH allot 1514b avpkt 1000 weight 10Kbit \ +prio 6 maxburst 10 split 1:7FFE defmap ffff + diff --git a/examples/dhcp-client-script b/examples/dhcp-client-script new file mode 100755 index 0000000..7207b57 --- /dev/null +++ b/examples/dhcp-client-script @@ -0,0 +1,446 @@ +#!/bin/bash +# +# dhclient-script for Linux. +# +# 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. +# +# Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> +# +# Probably, I did not understand, what this funny feature as "alias" +# means exactly. For now I suppose, that it is a static address, which +# we should install and preserve. +# + +exec >> /tmp/DHS.log 2>&1 + +echo dhc-script $* reason=$reason +set | grep "^\(old_\|new_\|check_\)" + +LOG () { + echo LOG $* ; +} + +# convert 8bit mask to length +# arg: $1 = mask +# +Mask8ToLen() { + local l=0; + + while [ $l -le 7 ]; do + if [ $[ ( 1 << $l ) + $1 ] -eq 256 ]; then + return $[ 8 - $l ] + fi + l=$[ $l + 1 ] + done + return 0; +} + +# convert inet dotted quad mask to length +# arg: $1 = dotquad mask +# +MaskToLen() { + local masklen=0 + local mask8=$1 + + case $1 in + 0.0.0.0) + return 0; + ;; + 255.*.0.0) + masklen=8 + mask8=${mask8#255.} + mask8=${mask8%.0.0} + ;; + 255.255.*.0) + masklen=16 + mask8=${mask8#255.255.} + mask8=${mask8%.0} + ;; + 255.255.255.*) + masklen=24 + mask8=${mask8#255.255.255.} + ;; + *) + return 255 + ;; + esac + Mask8ToLen $mask8 + return $[ $? + $masklen ] +} + +# calculate ABC "natural" mask +# arg: $1 = dotquad address +# +ABCMask () { + local class; + + class=${1%%.*} + + if [ "$1" = "255.255.255.255" ]; then + echo $1 + elif [ "$1" = "0.0.0.0" ]; then + echo $1 + elif [ $class -ge 224 ]; then + echo 240.0.0.0 + elif [ $class -ge 192 ]; then + echo 255.255.255.0 + elif [ $class -ge 128 ]; then + echo 255.255.0.0 + else + echo 255.0.0.0 + fi +} + +# calculate ABC "natural" mask length +# arg: $1 = dotquad address +# +ABCMaskLen () { + local class; + + class=${1%%.*} + + if [ "$1" = "255.255.255.255" ]; then + return 32 + elif [ "$1" = "0.0.0.0" ]; then + return 0 + elif [ $class -ge 224 ]; then + return 4; + elif [ $class -ge 192 ]; then + return 24; + elif [ $class -ge 128 ]; then + return 16; + else + return 8; + fi +} + +# Delete IP address +# args: $1 = interface +# $2 = address +# $3 = mask +# $4 = broadcast +# $5 = label +# +DelINETAddr () { + local masklen=32 + local addrid=$1 + + LOG DelINETAddr $* + + if [ "$5" ]; then + addrid=$addrid:$5 + fi + LOG ifconfig $addrid down + ifconfig $addrid down +} + +# Add IP address +# args: $1 = interface +# $2 = address +# $3 = mask +# $4 = broadcast +# $5 = label +# +AddINETAddr () { + local mask_arg + local brd_arg + local addrid=$1 + + LOG AddINETAddr $* + + if [ "$5" ]; then + addrid=$addrid:$5 + fi + if [ "$3" ]; then + mask_arg="netmask $3" + fi + if [ "$4" ]; then + brd_arg="broadcast $4" + fi + + LOG ifconfig $addrid $2 $mask_arg $brd_arg up + ifconfig $addrid $2 $mask_arg $brd_arg up +} + +# Add default routes +# args: $1 = routers list +# +AddDefaultRoutes() { + local router + + if [ "$1" ]; then + LOG AddDefaultRoutes $* + for router in $1; do + LOG route add default gw $router + route add default gw $router + done ; + fi +} + +# Delete default routes +# args: $1 = routers list +# +DelDefaultRoutes() { + local router + + if [ "$1" ]; then + LOG DelDefaultRoutes $* + + for router in $1; do + LOG route del default gw $router + route del default gw $router + done + fi +} + +# ping a host +# args: $1 = dotquad address of the host +# +PingNode() { + LOG PingNode $* + if ping -q -c 1 -w 2 $1 ; then + return 0; + fi + return 1; +} + +# Check (and add route, if alive) default routers +# args: $1 = routers list +# returns: 0 if at least one router is alive. +# +CheckRouterList() { + local router + local succeed=1 + + LOG CheckRouterList $* + + for router in $1; do + if PingNode $router ; then + succeed=0 + route add default gw $router + fi + done + return $succeed +} + +# Delete/create static routes. +# args: $1 = operation (del/add) +# $2 = routes list in format "dst1 nexthop1 dst2 ..." +# +# BEWARE: this feature of DHCP is obsolete, because does not +# support subnetting. +# +X-StaticRouteList() { + local op=$1 + local lst="$2" + local masklen + + LOG X-StaticRouteList $* + + if [ "$lst" ]; then + set $lst + while [ $# -gt 1 ]; do + route $op -net $1 netmask `ABCMask "$1"` gw $2 + shift; shift; + done + fi +} + +# Create static routes. +# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..." +# +AddStaticRouteList() { + LOG AddStaticRouteList $* + X-StaticRouteList add "$1" +} + +# Delete static routes. +# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..." +# +DelStaticRouteList() { + LOG DelStaticRouteList $* + X-StaticRouteList del "$1" +} + +# Broadcast unsolicited ARP to update neighbours' caches. +# args: $1 = interface +# $2 = address +# +UnsolicitedARP() { + if [ -f /sbin/arping ]; then + /sbin/arping -A -c 1 -I "$1" "$2" & + (sleep 2 ; /sbin/arping -U -c 1 -I "$1" "$2" ) & + fi +} + +# Duplicate address detection. +# args: $1 = interface +# $2 = test address +# returns: 0, if DAD succeeded. +DAD() { + if [ -f /sbin/arping ]; then + /sbin/arping -c 2 -w 3 -D -I "$1" "$2" + return $? + fi + return 0 +} + + +# Setup resolver. +# args: NO +# domain and nameserver list are passed in global variables. +# +# NOTE: we try to be careful and not to break user supplied resolv.conf. +# The script mangles it, only if it has dhcp magic signature. +# +UpdateDNS() { + local nameserver + local idstring="#### Generated by DHCPCD" + + LOG UpdateDNS $* + + if [ "$new_domain_name" = "" -a "$new_domain_name_servers" = "" ]; then + return 0; + fi + + echo $idstring > /etc/resolv.conf.dhcp + if [ "$new_domain_name" ]; then + echo search $new_domain_name >> /etc/resolv.conf.dhcp + fi + echo options ndots:1 >> /etc/resolv.conf.dhcp + + if [ "$new_domain_name_servers" ]; then + for nameserver in $new_domain_name_servers; do + echo nameserver $nameserver >> /etc/resolv.conf.dhcp + done + else + echo nameserver 127.0.0.1 >> /etc/resolv.conf.dhcp + fi + + if [ -f /etc/resolv.conf ]; then + if [ "`head -1 /etc/resolv.conf`" != "$idstring" ]; then + return 0 + fi + if [ "$old_domain_name" = "$new_domain_name" -a + "$new_domain_name_servers" = "$old_domain_name_servers" ]; then + return 0 + fi + fi + mv /etc/resolv.conf.dhcp /etc/resolv.conf +} + +case $reason in +NBI) + exit 1 + ;; + +MEDIUM) + exit 0 + ;; + +PREINIT) + ifconfig $interface:dhcp down + ifconfig $interface:dhcp1 down + if [ -d /proc/sys/net/ipv4/conf/$interface ]; then + ifconfig $interface:dhcp 10.10.10.10 netmask 255.255.255.255 + ifconfig $interface:dhcp down + if [ -d /proc/sys/net/ipv4/conf/$interface ]; then + LOG The interface $interface already configured. + fi + fi + ifconfig $interface:dhcp up + exit 0 + ;; + +ARPSEND) + exit 0 + ;; + +ARPCHECK) + if DAD "$interface" "$check_ip_address" ; then + exit 0 + fi + exit 1 + ;; + +BOUND|RENEW|REBIND|REBOOT) + if [ "$old_ip_address" -a "$alias_ip_address" -a \ + "$alias_ip_address" != "$old_ip_address" ]; then + DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + if [ "$old_ip_address" -a "$old_ip_address" != "$new_ip_address" ]; then + DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp + DelDefaultRoutes "$old_routers" + DelStaticRouteList "$old_static_routes" + fi + if [ "$old_ip_address" = "" -o "$old_ip_address" != "$new_ip_address" -o \ + "$reason" = "BOUND" -o "$reason" = "REBOOT" ]; then + AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp + AddStaticRouteList "$new_static_routes" + AddDefaultRoutes "$new_routers" + UnsolicitedARP "$interface" "$new_ip_address" + fi + if [ "$new_ip_address" != "$alias_ip_address" -a "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + UpdateDNS + exit 0 + ;; + +EXPIRE|FAIL) + if [ "$alias_ip_address" ]; then + DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + if [ "$old_ip_address" ]; then + DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp + DelDefaultRoutes "$old_routers" + DelStaticRouteList "$old_static_routes" + fi + if [ "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + exit 0 + ;; + +TIMEOUT) + if [ "$alias_ip_address" ]; then + DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi +# Seems, <null address> means, that no more old leases found. +# Or does it mean bug in dhcpcd? 8) Fail for now. + if [ "$new_ip_address" = "<null address>" ]; then + if [ "$old_ip_address" ]; then + DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp + fi + if [ "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + exit 1 + fi + if DAD "$interface" "$new_ip_address" ; then + AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp + UnsolicitedARP "$interface" "$new_ip_address" + if [ "$alias_ip_address" -a "$alias_ip_address" != "$new_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + UnsolicitedARP "$interface" "$alias_ip_address" + fi + if CheckRouterList "$new_routers" ; then + AddStaticRouteList "$new_static_routes" + UpdateDNS + exit 0 + fi + fi + DelINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp + DelDefaultRoutes "$old_routers" + DelStaticRouteList "$old_static_routes" + if [ "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + exit 1 + ;; +esac + +exit 0 diff --git a/examples/diffserv/Edge1 b/examples/diffserv/Edge1 new file mode 100644 index 0000000..4ddffdd --- /dev/null +++ b/examples/diffserv/Edge1 @@ -0,0 +1,68 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script just tags on the ingress interfac using Ipchains +# the result is used for fast classification and re-marking +# on the egress interface +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +# +# tag all incoming packets from host 10.2.0.24 to value 1 +# tag all incoming packets from host 10.2.0.3 to value 2 +# tag the rest of incoming packets from subnet 10.2.0.0/24 to value 3 +#These values are used in the egress +# +############################################################ +$IPCHAINS -A input -s 10.2.0.4/24 -m 3 +$IPCHAINS -A input -i $INDEV -s 10.2.0.24 -m 1 +$IPCHAINS -A input -i $INDEV -s 10.2.0.3 -m 2 + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 set_tc_index +# +# values of the DSCP to change depending on the class +# +#becomes EF +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0xb8 +#becomes AF11 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x28 +#becomes AF21 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x48 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 1 fw classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 2 fw classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 3 fw classid 1:3 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent 1:0 + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 diff --git a/examples/diffserv/Edge2 b/examples/diffserv/Edge2 new file mode 100644 index 0000000..2f78da2 --- /dev/null +++ b/examples/diffserv/Edge2 @@ -0,0 +1,87 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script tags the fwmark on the ingress interface using IPchains +# the result is used first for policing on the Ingress interface then +# for fast classification and re-marking +# on the egress interface +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +# +# tag all incoming packets from host 10.2.0.24 to value 1 +# tag all incoming packets from host 10.2.0.3 to value 2 +# tag the rest of incoming packets from subnet 10.2.0.0/24 to value 3 +#These values are used in the egress +############################################################ +$IPCHAINS -A input -s 10.2.0.0/24 -m 3 +$IPCHAINS -A input -i $INDEV -s 10.2.0.24 -m 1 +$IPCHAINS -A input -i $INDEV -s 10.2.0.3 -m 2 +############################################################ +# +# install the ingress qdisc on the ingress interface +############################################################ +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ + +# +# attach a fw classifier to the ingress which polices anything marked +# by ipchains to tag value 3 (The rest of the subnet packets -- not +# tag 1 or 2) to not go beyond 1.5Mbps +# Allow up to at least 60 packets to burst (assuming maximum packet +# size of # 1.5 KB) in the long run and upto about 6 packets in the +# shot run + +############################################################ +$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 3 fw \ +police rate 1500kbit burst 90k mtu 9k drop flowid :1 +############################################################ + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0xb8 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x28 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x48 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 1 fw classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 2 fw classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 3 fw classid 1:3 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $DEV ingress diff --git a/examples/diffserv/Edge31-ca-u32 b/examples/diffserv/Edge31-ca-u32 new file mode 100644 index 0000000..25e6c0b --- /dev/null +++ b/examples/diffserv/Edge31-ca-u32 @@ -0,0 +1,170 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities using u32 classifier +# This script tags tcindex based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color aware mode marker with PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.1) +# +# The colors are defined using the Diffserv Fields +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/usr/src/iproute2-current +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +INDEV=eth0 +EGDEV="dev eth1" +CIR1=1500kbit +CIR2=1000kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k + +############################################################ +# +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ +# +# Create u32 filters +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1: u32 \ +divisor 1 +############################################################ + +# The meters: Note that we have shared meters in this case as identified +# by the index parameter +meter1=" police index 1 rate $CIR1 burst $CBS1 " +meter2=" police index 2 rate $CIR2 burst $CBS1 " +meter3=" police index 3 rate $CIR2 burst $CBS2 " +meter4=" police index 4 rate $CIR1 burst $CBS2 " +meter5=" police index 5 rate $CIR1 burst $CBS2 " + +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE + +# *********************** AF41 *************************** +#AF41 (DSCP 0x22) is passed on with a tcindex value 1 +#if it doesnt exceed its CIR/CBS +#policer 1 is used. +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \ +match ip tos 0x88 0xfc \ +$meter1 \ +continue flowid :1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +# tcindex value of 2 +# policer 2 is used +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip tos 0x88 0xfc \ +$meter2 \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x88 0xfc \ +$meter3 \ +drop flowid :3 +# + +# *********************** AF42 *************************** +#AF42 (DSCP 0x24) from is passed on with a tcindex value 2 +#if it doesnt exceed its CIR/CBS +#policer 2 is used. Note that this is shared with the AF41 +# +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip tos 0x90 0xfc \ +$meter2 \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x90 0xfc \ +$meter3 \ +drop flowid :3 +# +# *********************** AF43 *************************** +# +#AF43 (DSCP 0x26) from is passed on with a tcindex value 3 +#if it doesnt exceed its CIR/CBS +#policer 3 is used. Note that this is shared with the AF41 and AF42 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x98 0xfc \ +$meter3 \ +drop flowid :3 +# +# *********************** BE *************************** +# +# Anything else (not from the AF4*) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# Note that the BE class is also used by the AF4* in the worst +# case +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 7 u32 \ +match ip src 0/0\ +$meter4 \ +drop flowid :4 + +######################## Egress side ######################## + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge31-cb-chains b/examples/diffserv/Edge31-cb-chains new file mode 100644 index 0000000..d7faae9 --- /dev/null +++ b/examples/diffserv/Edge31-cb-chains @@ -0,0 +1,132 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script fwmark tags(IPchains) based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color blind mode marker with no PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.1) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1500kbit +CIR2=1000kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k + +meter1="police rate $CIR1 burst $CBS1 " +meter2="police rate $CIR1 burst $CBS2 " +meter3="police rate $CIR2 burst $CBS1 " +meter4="police rate $CIR2 burst $CBS2 " +meter5="police rate $CIR2 burst $CBS2 " +# +# tag the rest of incoming packets from subnet 10.2.0.0/24 to fw value 1 +# tag all incoming packets from any other subnet to fw tag 2 +############################################################ +$IPCHAINS -A input -i $INDEV -s 0/0 -m 2 +$IPCHAINS -A input -i $INDEV -s 10.2.0.0/24 -m 1 +# +############################################################ +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +# +############################################################ + +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +############################################################ +# +# anything with fw tag of 1 is passed on with a tcindex value 1 +#if it doesnt exceed its allocated rate (CIR/CBS) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1 fw \ +$meter1 \ +continue flowid 4:1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +#tcindex value of 2 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 handle 1 fw \ +$meter2 \ +continue flowid 4:2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 1 fw \ +$meter3 \ +drop flowid 4:3 +# +# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 2 fw \ +$meter5 \ +drop flowid 4:4 + + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:4 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping (using tcindex; could easily have +# replaced it with the fw classifier instead) +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge32-ca-u32 b/examples/diffserv/Edge32-ca-u32 new file mode 100644 index 0000000..edf21e4 --- /dev/null +++ b/examples/diffserv/Edge32-ca-u32 @@ -0,0 +1,198 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities using u32 classifier +# This script tags tcindex based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color aware mode marker with PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.2) +# +# The colors are defined using the Diffserv Fields +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1000kbit +CIR2=500kbit +# the PIR is what is in excess of the CIR +PIR1=1000kbit +PIR2=500kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k +#the EBS is about 20 max sized packets +EBS1=30k +EBS2=30k + +# The meters: Note that we have shared meters in this case as identified +# by the index parameter +meter1=" police index 1 rate $CIR1 burst $CBS1 " +meter1a=" police index 2 rate $PIR1 burst $EBS1 " +meter2=" police index 3 rate $CIR2 burst $CBS1 " +meter2a=" police index 4 rate $PIR2 burst $EBS1 " +meter3=" police index 5 rate $CIR2 burst $CBS2 " +meter3a=" police index 6 rate $PIR2 burst $EBS2 " +meter4=" police index 7 rate $CIR1 burst $CBS2 " + +############################################################ +# +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ +# +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +# *********************** AF41 *************************** +#AF41 (DSCP 0x22) from is passed on with a tcindex value 1 +#if it doesnt exceed its CIR/CBS + PIR/EBS +#policer 1 is used. +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 1 u32 \ +match ip tos 0x88 0xfc \ +$meter1 \ +continue flowid :1 +$TC filter add dev $INDEV parent ffff: protocol ip prio 2 u32 \ +match ip tos 0x88 0xfc \ +$meter1a \ +continue flowid :1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +# tcindex value of 2 +# policer 2 is used +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 3 u32 \ +match ip tos 0x88 0xfc \ +$meter2 \ +continue flowid :2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \ +match ip tos 0x88 0xfc \ +$meter2a \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip tos 0x88 0xfc \ +$meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x88 0xfc \ +$meter3a \ +drop flowid :3 +# +# *********************** AF42 *************************** +#AF42 (DSCP 0x24) from is passed on with a tcindex value 2 +#if it doesnt exceed its CIR/CBS + PIR/EBS +#policer 2 is used. Note that this is shared with the AF41 +# +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 8 u32 \ +match ip tos 0x90 0xfc \ +$meter2 \ +continue flowid :2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 9 u32 \ +match ip tos 0x90 0xfc \ +$meter2a \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 10 u32 \ +match ip tos 0x90 0xfc \ +$meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 11 u32 \ +match ip tos 0x90 0xfc \ +$meter3a \ +drop flowid :3 + +# +# *********************** AF43 *************************** +# +#AF43 (DSCP 0x26) from is passed on with a tcindex value 3 +#if it doesnt exceed its CIR/CBS + PIR/EBS +#policer 3 is used. Note that this is shared with the AF41 and AF42 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 13 u32 \ +match ip tos 0x98 0xfc \ +$meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 14 u32 \ +match ip tos 0x98 0xfc \ +$meter3a \ +drop flowid :3 +# +## *********************** BE *************************** +## +## Anything else (not from the AF4*) gets discarded if it +## exceeds 1Mbps and by default goes to BE if it doesnt +## Note that the BE class is also used by the AF4* in the worst +## case +## +$TC filter add dev $INDEV parent ffff: protocol ip prio 16 u32 \ +match ip src 0/0\ +$meter4 \ +drop flowid :4 + +######################## Egress side ######################## + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge32-cb-chains b/examples/diffserv/Edge32-cb-chains new file mode 100644 index 0000000..804fad1 --- /dev/null +++ b/examples/diffserv/Edge32-cb-chains @@ -0,0 +1,144 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script fwmark tags(IPchains) based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color blind mode marker with no PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.1) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1500kbit +CIR2=500kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k + +meter1="police rate $CIR1 burst $CBS1 " +meter1a="police rate $CIR2 burst $CBS1 " +meter2="police rate $CIR1 burst $CBS2 " +meter2a="police rate $CIR2 burst $CBS2 " +meter3="police rate $CIR2 burst $CBS1 " +meter3a="police rate $CIR2 burst $CBS1 " +meter4="police rate $CIR2 burst $CBS2 " +meter5="police rate $CIR1 burst $CBS2 " +# +# tag the rest of incoming packets from subnet 10.2.0.0/24 to fw value 1 +# tag all incoming packets from any other subnet to fw tag 2 +############################################################ +$IPCHAINS -A input -i $INDEV -s 0/0 -m 2 +$IPCHAINS -A input -i $INDEV -s 10.2.0.0/24 -m 1 +# +############################################################ +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +# +############################################################ + +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +############################################################ +# +# anything with fw tag of 1 is passed on with a tcindex value 1 +#if it doesnt exceed its allocated rate (CIR/CBS) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 1 handle 1 fw \ +$meter1 \ +continue flowid 4:1 +$TC filter add dev $INDEV parent ffff: protocol ip prio 2 handle 1 fw \ +$meter1a \ +continue flowid 4:1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +#tcindex value of 2 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 3 handle 1 fw \ +$meter2 \ +continue flowid 4:2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1 fw \ +$meter2a \ +continue flowid 4:2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 handle 1 fw \ +$meter3 \ +continue flowid 4:3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 1 fw \ +$meter3a \ +drop flowid 4:3 +# +# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 7 handle 2 fw \ +$meter5 \ +drop flowid 4:4 + + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:4 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping (using tcindex; could easily have +# replaced it with the fw classifier instead) +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge32-cb-u32 b/examples/diffserv/Edge32-cb-u32 new file mode 100644 index 0000000..cc2ebb4 --- /dev/null +++ b/examples/diffserv/Edge32-cb-u32 @@ -0,0 +1,145 @@ +#! /bin/sh +# +# sample script on using the ingress capabilities using u32 classifier +# This script tags tcindex based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color blind mode marker with PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.2) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1000kbit +CIR2=1000kbit +# The PIR is the excess (in addition to the CIR i.e if always +# going to the PIR --> average rate is CIR+PIR) +PIR1=1000kbit +PIR2=500kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k +#the EBS is about 10 max sized packets +EBS1=15k +EBS2=15k +# The meters +meter1=" police rate $CIR1 burst $CBS1 " +meter1a=" police rate $PIR1 burst $EBS1 " +meter2=" police rate $CIR2 burst $CBS1 " +meter2a="police rate $PIR2 burst $CBS1 " +meter3=" police rate $CIR2 burst $CBS2 " +meter3a=" police rate $PIR2 burst $EBS2 " +meter4=" police rate $CIR1 burst $CBS2 " +meter5=" police rate $CIR1 burst $CBS2 " + + +# install the ingress qdisc on the ingress interface +############################################################ +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ +# +############################################################ + +# All packets are marked with a tcindex value which is used on the egress +# NOTE: tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +#anything from subnet 10.2.0.2/24 is passed on with a tcindex value 1 +#if it doesnt exceed its CIR/CBS + PIR/EBS +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 1 u32 \ +match ip src 10.2.0.0/24 $meter1 \ +continue flowid :1 +$TC filter add dev $INDEV parent ffff: protocol ip prio 2 u32 \ +match ip src 10.2.0.0/24 $meter1a \ +continue flowid :1 + +# +# if it exceeds the above but not the extra rate/burst below, it gets a +#tcindex value of 2 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 3 u32 \ +match ip src 10.2.0.0/24 $meter2 \ +continue flowid :2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \ +match ip src 10.2.0.0/24 $meter2a \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip src 10.2.0.0/24 $meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip src 10.2.0.0/24 $meter3a \ +drop flowid :3 +# +# +# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 7 u32 \ +match ip src 0/0 $meter5 \ +drop flowid :4 + + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/README b/examples/diffserv/README new file mode 100644 index 0000000..ec91d63 --- /dev/null +++ b/examples/diffserv/README @@ -0,0 +1,98 @@ + +Note all these are mere examples which can be customized to your needs + +AFCBQ +----- +AF PHB built using CBQ, DSMARK,GRED (default in GRIO mode) ,RED for BE +and the tcindex classifier with some algorithmic mapping + +EFCBQ +----- +EF PHB built using CBQ (for rate control and prioritization), +DSMARK( to remark DSCPs), tcindex classifier and RED for the BE +traffic. + +EFPRIO +------ +EF PHB using the PRIO scheduler, Token Bucket to rate control EF, +tcindex classifier, DSMARK to remark, and RED for the BE traffic + +EDGE scripts +============== + +CB-3(1|2)-(u32/chains) +====================== + + +The major differences are that the classifier is u32 on -u32 extension +and IPchains on the chains extension. CB stands for color Blind +and 31 is for the mode where only a CIR and CBS are defined whereas +32 stands for a mode where a CIR/CBS + PIR/EBS are defined. + +Color Blind (CB) +==========-----= +We look at one special subnet that we are interested in for simplicty +reasons to demonstrate the capability. We send the packets from that +subnet to AF4*, BE or end up dropping depending on the metering results. + + +The algorithm overview is as follows: + +*classify: + +**case: subnet X +---------------- + if !exceed meter1 tag as AF41 + else + if !exceed meter2 tag as AF42 + else + if !exceed meter 3 tag as AF43 + else + drop + +default case: Any other subnet +------------------------------- + if !exceed meter 5 tag as AF43 + else + drop + + +One Egress side change the DSCPs of the packets to reflect AF4* and BE +based on the tags from the ingress. + +------------------------------------------------------------- + +Color Aware +=========== + +Define some meters with + policing and give them IDs eg + +meter1=police index 1 rate $CIR1 burst $CBS1 +meter2=police index 2 rate $CIR2 burst $CBS2 etc + +General overview: +classify based on the DSCPs and use the policer ids to decide tagging + + +*classify on ingress: + +switch (dscp) { + case AF41: /* tos&0xfc == 0x88 */ + if (!exceed meter1) break; + case AF42: /* tos&0xfc == 0x90 */ + if (!exceed meter2) { + tag as AF42; + break; + } + case AF43: /* tos&0xfc == 0x98 */ + if (!exceed meter3) { + tag as AF43; + break; + } else + drop; + default: + if (!exceed meter4) tag as BE; + else drop; +} + +On the Egress side mark the proper AF tags diff --git a/examples/diffserv/afcbq b/examples/diffserv/afcbq new file mode 100644 index 0000000..10d6d93 --- /dev/null +++ b/examples/diffserv/afcbq @@ -0,0 +1,105 @@ +#!/usr/bin/perl +# +# +# AF using CBQ for a single interface eth0 +# 4 AF classes using GRED and one BE using RED +# Things you might want to change: +# - the device bandwidth (set at 10Mbits) +# - the bandwidth allocated for each AF class and the BE class +# - the drop probability associated with each AF virtual queue +# +# AF DSCP values used (based on AF draft 04) +# ----------------------------------------- +# AF DSCP values +# AF1 1. 0x0a 2. 0x0c 3. 0x0e +# AF2 1. 0x12 2. 0x14 3. 0x16 +# AF3 1. 0x1a 2. 0x1c 3. 0x1e +# AF4 1. 0x22 2. 0x24 3. 0x26 + +# +# +# A simple DSCP-class relationship formula used to generate +# values in the for loop of this script; $drop stands for the +# DP +# $dscp = ($class*8+$drop*2) +# +# if you use GRIO buffer sharing, then GRED priority is set as follows: +# $gprio=$drop+1; +# + +$TC = "/usr/src/iproute2-current/tc/tc"; +$DEV = "dev lo"; +$DEV = "dev eth1"; +$DEV = "dev eth0"; +# the BE-class number +$beclass = "5"; + +#GRIO buffer sharing on or off? +$GRIO = ""; +$GRIO = "grio"; +# The bandwidth of your device +$linerate="10Mbit"; +# The BE and AF rates +%rate_table=(); +$berate="1500Kbit"; +$rate_table{"AF1rate"}="1500Kbit"; +$rate_table{"AF2rate"}="1500Kbit"; +$rate_table{"AF3rate"}="1500Kbit"; +$rate_table{"AF4rate"}="1500Kbit"; +# +# +# +print "\n# --- General setup ---\n"; +print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex mask 0xfc " . + "shift 2 pass_on\n"; + #"shift 2\n"; +print "$TC qdisc add $DEV parent 1:0 handle 2:0 cbq bandwidth $linerate ". + "cell 8 avpkt 1000 mpu 64\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 tcindex ". + "mask 0xf0 shift 4 pass_on\n"; +for $class (1..4) { + print "\n# --- AF Class $class specific setup---\n"; + $AFrate=sprintf("AF%drate",$class); + print "$TC class add $DEV parent 2:0 classid 2:$class cbq ". + "bandwidth $linerate rate $rate_table{$AFrate} avpkt 1000 prio ". + (6-$class)." bounded allot 1514 weight 1 maxburst 21\n"; + print "$TC filter add $DEV parent 2:0 protocol ip prio 1 handle $class ". + "tcindex classid 2:$class\n"; + print "$TC qdisc add $DEV parent 2:$class gred setup DPs 3 default 2 ". + "$GRIO\n"; +# +# per DP setup +# + for $drop (1..3) { + print "\n# --- AF Class $class DP $drop---\n"; + $dscp = $class*8+$drop*2; + $tcindex = sprintf("1%x%x",$class,$drop); + print "$TC filter add $DEV parent 1:0 protocol ip prio 1 ". + "handle $dscp tcindex classid 1:$tcindex\n"; + $prob = $drop*0.02; + if ($GRIO) { + $gprio = $drop+1; + print "$TC qdisc change $DEV parent 2:$class gred limit 60KB min 15KB ". + "max 45KB burst 20 avpkt 1000 bandwidth $linerate DP $drop ". + "probability $prob ". + "prio $gprio\n"; + } else { + print "$TC qdisc change $DEV parent 2:$class gred limit 60KB min 15KB ". + "max 45KB burst 20 avpkt 1000 bandwidth $linerate DP $drop ". + "probability $prob \n"; + } + } +} +# +# +print "\n#------BE Queue setup------\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 2 ". + "handle 0 tcindex mask 0 classid 1:1\n"; +print "$TC class add $DEV parent 2:0 classid 2:$beclass cbq ". + "bandwidth $linerate rate $berate avpkt 1000 prio 6 " . + "bounded allot 1514 weight 1 maxburst 21 \n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 handle 0 tcindex ". + "classid 2:5\n"; +print "$TC qdisc add $DEV parent 2:5 red limit 60KB min 15KB max 45KB ". + "burst 20 avpkt 1000 bandwidth $linerate probability 0.4\n"; diff --git a/examples/diffserv/ef-prio b/examples/diffserv/ef-prio new file mode 100644 index 0000000..48611bd --- /dev/null +++ b/examples/diffserv/ef-prio @@ -0,0 +1,25 @@ +#!/usr/bin/perl +$TC = "/root/DS-6-beta/iproute2-990530-dsing/tc/tc"; +$DEV = "dev eth1"; +$efrate="1.5Mbit"; +$MTU="1.5kB"; +print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex ". + "mask 0xfc shift 2\n"; +print "$TC qdisc add $DEV parent 1:0 handle 2:0 prio\n"; +# +# EF class: Maximum about one MTU sized packet allowed on the queue +# +print "$TC qdisc add $DEV parent 2:1 tbf rate $efrate burst $MTU limit 1.6kB\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 ". + "handle 0x2e tcindex classid 2:1 pass_on\n"; +# +# BE class +# +print "#BE class(2:2) \n"; +print "$TC qdisc add $DEV parent 2:2 red limit 60KB ". + "min 15KB max 45KB burst 20 avpkt 1000 bandwidth 10Mbit ". + "probability 0.4\n"; +# +print "$TC filter add $DEV parent 2:0 protocol ip prio 2 ". + "handle 0 tcindex mask 0 classid 2:2 pass_on\n"; diff --git a/examples/diffserv/efcbq b/examples/diffserv/efcbq new file mode 100644 index 0000000..bcc437b --- /dev/null +++ b/examples/diffserv/efcbq @@ -0,0 +1,31 @@ +#!/usr/bin/perl +# +$TC = "/root/DS-6-beta/iproute2-990530-dsing/tc/tc"; +$DEV = "dev eth1"; +print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex ". + "mask 0xfc shift 2\n"; +print "$TC qdisc add $DEV parent 1:0 handle 2:0 cbq bandwidth ". + "10Mbit cell 8 avpkt 1000 mpu 64\n"; +# +# EF class +# +print "$TC class add $DEV parent 2:0 classid 2:1 cbq bandwidth ". + "10Mbit rate 1500Kbit avpkt 1000 prio 1 bounded isolated ". + "allot 1514 weight 1 maxburst 10 \n"; +# packet fifo for EF? +print "$TC qdisc add $DEV parent 2:1 pfifo limit 5\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 ". + "handle 0x2e tcindex classid 2:1 pass_on\n"; +# +# BE class +# +print "#BE class(2:2) \n"; +print "$TC class add $DEV parent 2:0 classid 2:2 cbq bandwidth ". + "10Mbit rate 5Mbit avpkt 1000 prio 7 allot 1514 weight 1 ". + "maxburst 21 borrow split 2:0 defmap 0xffff \n"; +print "$TC qdisc add $DEV parent 2:2 red limit 60KB ". + "min 15KB max 45KB burst 20 avpkt 1000 bandwidth 10Mbit ". + "probability 0.4\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 2 ". + "handle 0 tcindex mask 0 classid 2:2 pass_on\n"; diff --git a/examples/diffserv/regression-testing b/examples/diffserv/regression-testing new file mode 100644 index 0000000..0ec705c --- /dev/null +++ b/examples/diffserv/regression-testing @@ -0,0 +1,125 @@ + +These were the tests done to validate the Diffserv scripts. +This document will be updated continously. If you do more +thorough validation testing please post the details to the +diffserv mailing list. +Nevertheless, these tests should serve for basic validation. + +AFCBQ, EFCBQ, EFPRIO +---------------------- + +generate all possible DSCPs and observe that they +get sent to the proper classes. In the case of AF also +to the correct Virtual Queues. + +Edge1 +----- +generate TOS values 0x0,0x10,0xbb each with IP addresses +10.2.0.24 (mark 1), 10.2.0.3 (mark2) and 10.2.0.30 (mark 3) +and observe that they get marked as expected. + +Edge2 +----- + +-Repeat the tests in Edge1 +-ftp with data direction from 10.2.0.2 + *observe that the metering/policing works correctly (and the marking + as well). In this case the mark used will be 3 + +Edge31-cb-chains +---------------- + +-ftp with data direction from 10.2.0.2 + + *observe that the metering/policing works correctly (and the marking + as well). In this case the mark used will be 1. + + Metering: The data throughput should not exceed 2*CIR1 + 2*CIR2 + which is roughly: 5mbps + + Marking: the should be a variation of marked packets: + AF41(TOS=0x88) AF42(0x90) AF43(0x98) and BE (0x0) + +More tests required to see the interaction of several sources (other +than subnet 10.2.0.0/24). + +Edge31-ca-u32 +-------------- + +Generate data using modified tcpblast from 10.2.0.2 (behind eth2) to the +discard port of 10.1.0.2 (behind eth1) + +1) generate with src tos = 0x88 + Metering: Allocated throughput should not exceed 2*CIR1 + 2*CIR2 + approximately 5mbps + Marking: Should vary between 0x88,0x90,0x98 and 0x0 + +2) generate with src tos = 0x90 + Metering: Allocated throughput should not exceed CIR1 + 2*CIR2 + approximately 3.5mbps + Marking: Should vary between 0x90,0x98 and 0x0 + +3) generate with src tos = 0x98 + Metering: Allocated throughput should not exceed CIR1 + CIR2 + approximately 2.5mbps + Marking: Should vary between 0x98 and 0x0 + +4) generate with src tos any other than the above + Metering: Allocated throughput should not exceed CIR1 + approximately 1.5mbps + Marking: Should be consistent at 0x0 + +TODO: Testing on how each color shares when all 4 types of packets +are going through the edge device + +Edge32-cb-u32, Edge32-cb-chains +------------------------------- + +-ftp with data direction from 10.2.0.2 + + *observe that the metering/policing works correctly (and the marking + as well). + + Metering: + The data throughput should not exceed 2*CIR1 + 2*CIR2 + + 2*PIR2 + PIR1 for u32 which is roughly: 6mbps + The data throughput should not exceed 2*CIR1 + 5*CIR2 + for chains which is roughly: 6mbps + + Marking: the should be a variation of marked packets: + AF41(TOS=0x88) AF42(0x90) AF43(0x98) and BE (0x0) + +TODO: +-More tests required to see the interaction of several sources (other +than subnet 10.2.0.0/24). +-More tests needed to capture stats on how many times the CIR was exceeded +but the data was not remarked etc. + +Edge32-ca-u32 +-------------- + +Generate data using modified tcpblast from 10.2.0.2 (behind eth2) to the +discard port of 10.1.0.2 (behind eth1) + +1) generate with src tos = 0x88 + Metering: Allocated throughput should not exceed 2*CIR1 + 2*CIR2 + +PIR1 -- approximately 4mbps + Marking: Should vary between 0x88,0x90,0x98 and 0x0 + +2) generate with src tos = 0x90 + Metering: Allocated throughput should not exceed CIR1 + 2*CIR2 + + 2* PIR2 approximately 3mbps + Marking: Should vary between 0x90,0x98 and 0x0 + +3) generate with src tos = 0x98 + Metering: Allocated throughput should not exceed PIR1+ CIR1 + CIR2 + approximately 2.5mbps + Marking: Should vary between 0x98 and 0x0 + +4) generate with src tos any other than the above + Metering: Allocated throughput should not exceed CIR1 + approximately 1mbps + Marking: Should be consistent at 0x0 + +TODO: Testing on how each color shares when all 4 types of packets +are going through the edge device diff --git a/include/SNAPSHOT.h b/include/SNAPSHOT.h new file mode 100644 index 0000000..8375d76 --- /dev/null +++ b/include/SNAPSHOT.h @@ -0,0 +1 @@ +static char SNAPSHOT[] = "050314"; diff --git a/include/ip6tables.h b/include/ip6tables.h new file mode 100644 index 0000000..8360617 --- /dev/null +++ b/include/ip6tables.h @@ -0,0 +1,141 @@ +#ifndef _IP6TABLES_USER_H +#define _IP6TABLES_USER_H + +#include "iptables_common.h" +#include "libiptc/libip6tc.h" + +struct ip6tables_rule_match +{ + struct ip6tables_rule_match *next; + + struct ip6tables_match *match; +}; + +/* Include file for additions: new matches and targets. */ +struct ip6tables_match +{ + struct ip6tables_match *next; + + ip6t_chainlabel name; + + const char *version; + + /* Size of match data. */ + size_t size; + + /* Size of match data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the match. */ + void (*init)(struct ip6t_entry_match *m, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ip6t_entry *entry, + unsigned int *nfcache, + struct ip6t_entry_match **match); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the match iff non-NULL: put space at end */ + void (*print)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_match *match, int numeric); + + /* Saves the union ipt_matchinfo in parsable form to stdout. */ + void (*save)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_match *match); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ip6t_entry_match *m; + unsigned int mflags; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +struct ip6tables_target +{ + struct ip6tables_target *next; + + ip6t_chainlabel name; + + const char *version; + + /* Size of target data. */ + size_t size; + + /* Size of target data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the target. */ + void (*init)(struct ip6t_entry_target *t, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ip6t_entry *entry, + struct ip6t_entry_target **target); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the target iff non-NULL: put space at end */ + void (*print)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_target *target, int numeric); + + /* Saves the targinfo in parsable form to stdout. */ + void (*save)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_target *target); + + /* Pointer to list of extra command-line options */ + struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ip6t_entry_target *t; + unsigned int tflags; + unsigned int used; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +extern int line; + +/* Your shared library should call one of these. */ +extern void register_match6(struct ip6tables_match *me); +extern void register_target6(struct ip6tables_target *me); + +extern int do_command6(int argc, char *argv[], char **table, + ip6tc_handle_t *handle); +/* Keeping track of external matches and targets: linked lists. */ +extern struct ip6tables_match *ip6tables_matches; +extern struct ip6tables_target *ip6tables_targets; + +enum ip6t_tryload { + DONT_LOAD, + TRY_LOAD, + LOAD_MUST_SUCCEED +}; + +extern struct ip6tables_target *find_target(const char *name, enum ip6t_tryload); +extern struct ip6tables_match *find_match(const char *name, enum ip6t_tryload, struct ip6tables_rule_match **match); + +extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), int verbose, int builtinstoo, ip6tc_handle_t *handle); +extern int flush_entries(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle); +extern int delete_chain(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle); +extern int ip6tables_insmod(const char *modname, const char *modprobe); + +#endif /*_IP6TABLES_USER_H*/ diff --git a/include/iptables.h b/include/iptables.h new file mode 100644 index 0000000..5aca69a --- /dev/null +++ b/include/iptables.h @@ -0,0 +1,155 @@ +#ifndef _IPTABLES_USER_H +#define _IPTABLES_USER_H + +#include "iptables_common.h" +#include "libiptc/libiptc.h" + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif + +struct iptables_rule_match +{ + struct iptables_rule_match *next; + + struct iptables_match *match; +}; + +/* Include file for additions: new matches and targets. */ +struct iptables_match +{ + struct iptables_match *next; + + ipt_chainlabel name; + + const char *version; + + /* Size of match data. */ + size_t size; + + /* Size of match data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the match. */ + void (*init)(struct ipt_entry_match *m, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + unsigned int *nfcache, + struct ipt_entry_match **match); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the match iff non-NULL: put space at end */ + void (*print)(const struct ipt_ip *ip, + const struct ipt_entry_match *match, int numeric); + + /* Saves the match info in parsable form to stdout. */ + void (*save)(const struct ipt_ip *ip, + const struct ipt_entry_match *match); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ipt_entry_match *m; + unsigned int mflags; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +struct iptables_target +{ + struct iptables_target *next; + + ipt_chainlabel name; + + const char *version; + + /* Size of target data. */ + size_t size; + + /* Size of target data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the target. */ + void (*init)(struct ipt_entry_target *t, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + struct ipt_entry_target **target); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the target iff non-NULL: put space at end */ + void (*print)(const struct ipt_ip *ip, + const struct ipt_entry_target *target, int numeric); + + /* Saves the targinfo in parsable form to stdout. */ + void (*save)(const struct ipt_ip *ip, + const struct ipt_entry_target *target); + + /* Pointer to list of extra command-line options */ + struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ipt_entry_target *t; + unsigned int tflags; + unsigned int used; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +extern int line; + +/* Your shared library should call one of these. */ +extern void register_match(struct iptables_match *me); +extern void register_target(struct iptables_target *me); + +extern struct in_addr *dotted_to_addr(const char *dotted); +extern char *addr_to_dotted(const struct in_addr *addrp); +extern char *addr_to_anyname(const struct in_addr *addr); +extern char *mask_to_dotted(const struct in_addr *mask); + +extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp, + struct in_addr *maskp, unsigned int *naddrs); +extern u_int16_t parse_protocol(const char *s); + +extern int do_command(int argc, char *argv[], char **table, + iptc_handle_t *handle); +/* Keeping track of external matches and targets: linked lists. */ +extern struct iptables_match *iptables_matches; +extern struct iptables_target *iptables_targets; + +enum ipt_tryload { + DONT_LOAD, + TRY_LOAD, + LOAD_MUST_SUCCEED +}; + +extern struct iptables_target *find_target(const char *name, enum ipt_tryload); +extern struct iptables_match *find_match(const char *name, enum ipt_tryload, struct iptables_rule_match **match); + +extern int delete_chain(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle); +extern int flush_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle); +extern int for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), + int verbose, int builtinstoo, iptc_handle_t *handle); +#endif /*_IPTABLES_USER_H*/ diff --git a/include/iptables_common.h b/include/iptables_common.h new file mode 100644 index 0000000..e3b99aa --- /dev/null +++ b/include/iptables_common.h @@ -0,0 +1,37 @@ +#ifndef _IPTABLES_COMMON_H +#define _IPTABLES_COMMON_H +/* Shared definitions between ipv4 and ipv6. */ + +enum exittype { + OTHER_PROBLEM = 1, + PARAMETER_PROBLEM, + VERSION_PROBLEM +}; +extern void exit_printhelp(void) __attribute__((noreturn)); +extern void exit_tryhelp(int) __attribute__((noreturn)); +int check_inverse(const char option[], int *invert, int *optind, int argc); +extern int string_to_number(const char *, + unsigned int, + unsigned int, + unsigned int *); +extern int string_to_number_l(const char *, + unsigned long int, + unsigned long int, + unsigned long *); +extern int string_to_number_ll(const char *, + unsigned long long int, + unsigned long long int, + unsigned long long *); +extern int iptables_insmod(const char *modname, const char *modprobe); +void exit_error(enum exittype, char *, ...)__attribute__((noreturn, + format(printf,2,3))); +extern const char *program_name, *program_version; + +#ifdef NO_SHARED_LIBS +# ifdef _INIT +# define _init _INIT +# endif + extern void init_extensions(void); +#endif + +#endif /*_IPTABLES_COMMON_H*/ diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h new file mode 100644 index 0000000..18861fe --- /dev/null +++ b/include/libiptc/ipt_kernel_headers.h @@ -0,0 +1,27 @@ +/* This is the userspace/kernel interface for Generic IP Chains, + required for libc6. */ +#ifndef _FWCHAINS_KERNEL_HEADERS_H +#define _FWCHAINS_KERNEL_HEADERS_H + +#include <limits.h> + +#if defined(__GLIBC__) && __GLIBC__ == 2 +#include <netinet/ip.h> +#include <netinet/in.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <net/if.h> +#include <sys/types.h> +#else /* libc5 */ +#include <sys/socket.h> +#include <linux/ip.h> +#include <linux/in.h> +#include <linux/if.h> +#include <linux/icmp.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/types.h> +#include <linux/in6.h> +#endif +#endif diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h new file mode 100644 index 0000000..7a247c4 --- /dev/null +++ b/include/libiptc/libip6tc.h @@ -0,0 +1,154 @@ +#ifndef _LIBIP6TC_H +#define _LIBIP6TC_H +/* Library which manipulates firewall rules. Version 0.2. */ + +#include <libiptc/ipt_kernel_headers.h> +#include <linux/netfilter_ipv6/ip6_tables.h> + +#ifndef IP6T_MIN_ALIGN +#define IP6T_MIN_ALIGN (__alignof__(struct ip6t_entry)) +#endif +#define IP6T_ALIGN(s) (((s) + (IP6T_MIN_ALIGN-1)) & ~(IP6T_MIN_ALIGN-1)) + +typedef char ip6t_chainlabel[32]; + +#define IP6TC_LABEL_ACCEPT "ACCEPT" +#define IP6TC_LABEL_DROP "DROP" +#define IP6TC_LABEL_QUEUE "QUEUE" +#define IP6TC_LABEL_RETURN "RETURN" + +/* Transparent handle type. */ +typedef struct ip6tc_handle *ip6tc_handle_t; + +/* Does this chain exist? */ +int ip6tc_is_chain(const char *chain, const ip6tc_handle_t handle); + +/* Take a snapshot of the rules. Returns NULL on error. */ +ip6tc_handle_t ip6tc_init(const char *tablename); + +/* Cleanup after ip6tc_init(). */ +void ip6tc_free(ip6tc_handle_t *h); + +/* Iterator functions to run through the chains. Returns NULL at end. */ +const char *ip6tc_first_chain(ip6tc_handle_t *handle); +const char *ip6tc_next_chain(ip6tc_handle_t *handle); + +/* Get first rule in the given chain: NULL for empty chain. */ +const struct ip6t_entry *ip6tc_first_rule(const char *chain, + ip6tc_handle_t *handle); + +/* Returns NULL when rules run out. */ +const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev, + ip6tc_handle_t *handle); + +/* Returns a pointer to the target name of this position. */ +const char *ip6tc_get_target(const struct ip6t_entry *e, + ip6tc_handle_t *handle); + +/* Is this a built-in chain? */ +int ip6tc_builtin(const char *chain, const ip6tc_handle_t handle); + +/* Get the policy of a given built-in chain */ +const char *ip6tc_get_policy(const char *chain, + struct ip6t_counters *counters, + ip6tc_handle_t *handle); + +/* These functions return TRUE for OK or 0 and set errno. If errno == + 0, it means there was a version error (ie. upgrade libiptc). */ +/* Rule numbers start at 1 for the first rule. */ + +/* Insert the entry `fw' in chain `chain' into position `rulenum'. */ +int ip6tc_insert_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *e, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* Atomically replace rule `rulenum' in `chain' with `fw'. */ +int ip6tc_replace_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *e, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* Append entry `fw' to chain `chain'. Equivalent to insert with + rulenum = length of chain. */ +int ip6tc_append_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *e, + ip6tc_handle_t *handle); + +/* Delete the first rule in `chain' which matches `fw'. */ +int ip6tc_delete_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *origfw, + unsigned char *matchmask, + ip6tc_handle_t *handle); + +/* Delete the rule in position `rulenum' in `chain'. */ +int ip6tc_delete_num_entry(const ip6t_chainlabel chain, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* Check the packet `fw' on chain `chain'. Returns the verdict, or + NULL and sets errno. */ +const char *ip6tc_check_packet(const ip6t_chainlabel chain, + struct ip6t_entry *, + ip6tc_handle_t *handle); + +/* Flushes the entries in the given chain (ie. empties chain). */ +int ip6tc_flush_entries(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Zeroes the counters in a chain. */ +int ip6tc_zero_entries(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Creates a new chain. */ +int ip6tc_create_chain(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Deletes a chain. */ +int ip6tc_delete_chain(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Renames a chain. */ +int ip6tc_rename_chain(const ip6t_chainlabel oldname, + const ip6t_chainlabel newname, + ip6tc_handle_t *handle); + +/* Sets the policy on a built-in chain. */ +int ip6tc_set_policy(const ip6t_chainlabel chain, + const ip6t_chainlabel policy, + struct ip6t_counters *counters, + ip6tc_handle_t *handle); + +/* Get the number of references to this chain */ +int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* read packet and byte counters for a specific rule */ +struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* zero packet and byte counters for a specific rule */ +int ip6tc_zero_counter(const ip6t_chainlabel chain, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* set packet and byte counters for a specific rule */ +int ip6tc_set_counter(const ip6t_chainlabel chain, + unsigned int rulenum, + struct ip6t_counters *counters, + ip6tc_handle_t *handle); + +/* Makes the actual changes. */ +int ip6tc_commit(ip6tc_handle_t *handle); + +/* Get raw socket. */ +int ip6tc_get_raw_socket(); + +/* Translates errno numbers into more human-readable form than strerror. */ +const char *ip6tc_strerror(int err); + +/* Return prefix length, or -1 if not contiguous */ +int ipv6_prefix_length(const struct in6_addr *a); + +#endif /* _LIBIP6TC_H */ diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h new file mode 100644 index 0000000..7628bda --- /dev/null +++ b/include/libiptc/libiptc.h @@ -0,0 +1,166 @@ +#ifndef _LIBIPTC_H +#define _LIBIPTC_H +/* Library which manipulates filtering rules. */ + +#include <libiptc/ipt_kernel_headers.h> +#include <linux/netfilter_ipv4/ip_tables.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef IPT_MIN_ALIGN +/* ipt_entry has pointers and u_int64_t's in it, so if you align to + it, you'll also align to any crazy matches and targets someone + might write */ +#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry)) +#endif + +#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1)) + +typedef char ipt_chainlabel[32]; + +#define IPTC_LABEL_ACCEPT "ACCEPT" +#define IPTC_LABEL_DROP "DROP" +#define IPTC_LABEL_QUEUE "QUEUE" +#define IPTC_LABEL_RETURN "RETURN" + +/* Transparent handle type. */ +typedef struct iptc_handle *iptc_handle_t; + +/* Does this chain exist? */ +int iptc_is_chain(const char *chain, const iptc_handle_t handle); + +/* Take a snapshot of the rules. Returns NULL on error. */ +iptc_handle_t iptc_init(const char *tablename); + +/* Cleanup after iptc_init(). */ +void iptc_free(iptc_handle_t *h); + +/* Iterator functions to run through the chains. Returns NULL at end. */ +const char *iptc_first_chain(iptc_handle_t *handle); +const char *iptc_next_chain(iptc_handle_t *handle); + +/* Get first rule in the given chain: NULL for empty chain. */ +const struct ipt_entry *iptc_first_rule(const char *chain, + iptc_handle_t *handle); + +/* Returns NULL when rules run out. */ +const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, + iptc_handle_t *handle); + +/* Returns a pointer to the target name of this entry. */ +const char *iptc_get_target(const struct ipt_entry *e, + iptc_handle_t *handle); + +/* Is this a built-in chain? */ +int iptc_builtin(const char *chain, const iptc_handle_t handle); + +/* Get the policy of a given built-in chain */ +const char *iptc_get_policy(const char *chain, + struct ipt_counters *counter, + iptc_handle_t *handle); + +/* These functions return TRUE for OK or 0 and set errno. If errno == + 0, it means there was a version error (ie. upgrade libiptc). */ +/* Rule numbers start at 1 for the first rule. */ + +/* Insert the entry `e' in chain `chain' into position `rulenum'. */ +int iptc_insert_entry(const ipt_chainlabel chain, + const struct ipt_entry *e, + unsigned int rulenum, + iptc_handle_t *handle); + +/* Atomically replace rule `rulenum' in `chain' with `e'. */ +int iptc_replace_entry(const ipt_chainlabel chain, + const struct ipt_entry *e, + unsigned int rulenum, + iptc_handle_t *handle); + +/* Append entry `e' to chain `chain'. Equivalent to insert with + rulenum = length of chain. */ +int iptc_append_entry(const ipt_chainlabel chain, + const struct ipt_entry *e, + iptc_handle_t *handle); + +/* Delete the first rule in `chain' which matches `e', subject to + matchmask (array of length == origfw) */ +int iptc_delete_entry(const ipt_chainlabel chain, + const struct ipt_entry *origfw, + unsigned char *matchmask, + iptc_handle_t *handle); + +/* Delete the rule in position `rulenum' in `chain'. */ +int iptc_delete_num_entry(const ipt_chainlabel chain, + unsigned int rulenum, + iptc_handle_t *handle); + +/* Check the packet `e' on chain `chain'. Returns the verdict, or + NULL and sets errno. */ +const char *iptc_check_packet(const ipt_chainlabel chain, + struct ipt_entry *entry, + iptc_handle_t *handle); + +/* Flushes the entries in the given chain (ie. empties chain). */ +int iptc_flush_entries(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Zeroes the counters in a chain. */ +int iptc_zero_entries(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Creates a new chain. */ +int iptc_create_chain(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Deletes a chain. */ +int iptc_delete_chain(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Renames a chain. */ +int iptc_rename_chain(const ipt_chainlabel oldname, + const ipt_chainlabel newname, + iptc_handle_t *handle); + +/* Sets the policy on a built-in chain. */ +int iptc_set_policy(const ipt_chainlabel chain, + const ipt_chainlabel policy, + struct ipt_counters *counters, + iptc_handle_t *handle); + +/* Get the number of references to this chain */ +int iptc_get_references(unsigned int *ref, + const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* read packet and byte counters for a specific rule */ +struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain, + unsigned int rulenum, + iptc_handle_t *handle); + +/* zero packet and byte counters for a specific rule */ +int iptc_zero_counter(const ipt_chainlabel chain, + unsigned int rulenum, + iptc_handle_t *handle); + +/* set packet and byte counters for a specific rule */ +int iptc_set_counter(const ipt_chainlabel chain, + unsigned int rulenum, + struct ipt_counters *counters, + iptc_handle_t *handle); + +/* Makes the actual changes. */ +int iptc_commit(iptc_handle_t *handle); + +/* Get raw socket. */ +int iptc_get_raw_socket(void); + +/* Translates errno numbers into more human-readable form than strerror. */ +const char *iptc_strerror(int err); + +#ifdef __cplusplus +} +#endif + + +#endif /* _LIBIPTC_H */ diff --git a/include/libnetlink.h b/include/libnetlink.h new file mode 100644 index 0000000..63cc3c8 --- /dev/null +++ b/include/libnetlink.h @@ -0,0 +1,57 @@ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +struct rtnl_handle +{ + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; +}; + +extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); +extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol); +extern void rtnl_close(struct rtnl_handle *rth); +extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); +extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); + +typedef int (*rtnl_filter_t)(const struct sockaddr_nl *, + struct nlmsghdr *n, void *); +extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2); +extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + rtnl_filter_t junk, + void *jarg); +extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int); + + +extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); +extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen); +extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len); +extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); +extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen); + +extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len); + +#define parse_rtattr_nested(tb, max, rta) \ + (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) + +extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler, + void *jarg); +extern int rtnl_from_file(FILE *, rtnl_filter_t handler, + void *jarg); + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +#endif /* __LIBNETLINK_H__ */ + diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h new file mode 100644 index 0000000..13f4e74 --- /dev/null +++ b/include/linux/gen_stats.h @@ -0,0 +1,67 @@ +#ifndef __LINUX_GEN_STATS_H +#define __LINUX_GEN_STATS_H + +#include <linux/types.h> + +enum { + TCA_STATS_UNSPEC, + TCA_STATS_BASIC, + TCA_STATS_RATE_EST, + TCA_STATS_QUEUE, + TCA_STATS_APP, + __TCA_STATS_MAX, +}; +#define TCA_STATS_MAX (__TCA_STATS_MAX - 1) + +/** + * struct gnet_stats_basic - byte/packet throughput statistics + * @bytes: number of seen bytes + * @packets: number of seen packets + */ +struct gnet_stats_basic +{ + __u64 bytes; + __u32 packets; +}; + +/** + * struct gnet_stats_rate_est - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est +{ + __u32 bps; + __u32 pps; +}; + +/** + * struct gnet_stats_queue - queuing statistics + * @qlen: queue length + * @backlog: backlog size of queue + * @drops: number of dropped packets + * @requeues: number of requeues + * @overlimits: number of enqueues over the limit + */ +struct gnet_stats_queue +{ + __u32 qlen; + __u32 backlog; + __u32 drops; + __u32 requeues; + __u32 overlimits; +}; + +/** + * struct gnet_estimator - rate estimator configuration + * @interval: sampling period + * @ewma_log: the log of measurement window weight + */ +struct gnet_estimator +{ + signed char interval; + unsigned char ewma_log; +}; + + +#endif /* __LINUX_GEN_STATS_H */ diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h new file mode 100644 index 0000000..7346ead --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -0,0 +1,347 @@ +/* + * 25-Jul-1998 Major changes to allow for ip chain table + * + * 3-Jan-2000 Named tables to allow packet selection for different uses. + */ + +/* + * Format of an IP firewall descriptor + * + * src, dst, src_mask, dst_mask are always stored in network byte order. + * flags are stored in host byte order (of course). + * Port numbers are stored in HOST byte order. + */ + +#ifndef _IPTABLES_H +#define _IPTABLES_H + +#include <linux/compiler.h> +#include <linux/netfilter_ipv4.h> + +#define IPT_FUNCTION_MAXNAMELEN 30 +#define IPT_TABLE_MAXNAMELEN 32 + +/* Yes, Virginia, you have to zero the padding. */ +struct ipt_ip { + /* Source and destination IP addr */ + struct in_addr src, dst; + /* Mask for src and dest IP addr */ + struct in_addr smsk, dmsk; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + + /* Protocol, 0 = ANY */ + u_int16_t proto; + + /* Flags word */ + u_int8_t flags; + /* Inverse flags */ + u_int8_t invflags; +}; + +struct ipt_entry_match +{ + union { + struct { + u_int16_t match_size; + + /* Used by userspace */ + char name[IPT_FUNCTION_MAXNAMELEN-1]; + + u_int8_t revision; + } user; + struct { + u_int16_t match_size; + + /* Used inside the kernel */ + struct ipt_match *match; + } kernel; + + /* Total length */ + u_int16_t match_size; + } u; + + unsigned char data[0]; +}; + +struct ipt_entry_target +{ + union { + struct { + u_int16_t target_size; + + /* Used by userspace */ + char name[IPT_FUNCTION_MAXNAMELEN-1]; + + u_int8_t revision; + } user; + struct { + u_int16_t target_size; + + /* Used inside the kernel */ + struct ipt_target *target; + } kernel; + + /* Total length */ + u_int16_t target_size; + } u; + + unsigned char data[0]; +}; + +struct ipt_standard_target +{ + struct ipt_entry_target target; + int verdict; +}; + +struct ipt_counters +{ + u_int64_t pcnt, bcnt; /* Packet and byte counters */ +}; + +/* Values for "flag" field in struct ipt_ip (general ip structure). */ +#define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ +#define IPT_F_MASK 0x01 /* All possible flag bits mask. */ + +/* Values for "inv" field in struct ipt_ip. */ +#define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +#define IPT_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */ +#define IPT_INV_TOS 0x04 /* Invert the sense of TOS. */ +#define IPT_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */ +#define IPT_INV_DSTIP 0x10 /* Invert the sense of DST OP. */ +#define IPT_INV_FRAG 0x20 /* Invert the sense of FRAG. */ +#define IPT_INV_PROTO 0x40 /* Invert the sense of PROTO. */ +#define IPT_INV_MASK 0x7F /* All possible flag bits mask. */ + +/* This structure defines each of the firewall rules. Consists of 3 + parts which are 1) general IP header stuff 2) match specific + stuff 3) the target to perform if the rule matches */ +struct ipt_entry +{ + struct ipt_ip ip; + + /* Mark with fields that we care about. */ + unsigned int nfcache; + + /* Size of ipt_entry + matches */ + u_int16_t target_offset; + /* Size of ipt_entry + matches + target */ + u_int16_t next_offset; + + /* Back pointer */ + unsigned int comefrom; + + /* Packet and byte counters. */ + struct ipt_counters counters; + + /* The matches (if any), then the target. */ + unsigned char elems[0]; +}; + +/* + * New IP firewall options for [gs]etsockopt at the RAW IP level. + * Unlike BSD Linux inherits IP options so you don't have to use a raw + * socket for this. Instead we check rights in the calls. */ +#define IPT_BASE_CTL 64 /* base for firewall socket options */ + +#define IPT_SO_SET_REPLACE (IPT_BASE_CTL) +#define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1) +#define IPT_SO_SET_MAX IPT_SO_SET_ADD_COUNTERS + +#define IPT_SO_GET_INFO (IPT_BASE_CTL) +#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1) +#define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2) +#define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3) +#define IPT_SO_GET_MAX IPT_SO_GET_REVISION_TARGET + +/* CONTINUE verdict for targets */ +#define IPT_CONTINUE 0xFFFFFFFF + +/* For standard target */ +#define IPT_RETURN (-NF_MAX_VERDICT - 1) + +/* TCP matching stuff */ +struct ipt_tcp +{ + u_int16_t spts[2]; /* Source port range. */ + u_int16_t dpts[2]; /* Destination port range. */ + u_int8_t option; /* TCP Option iff non-zero*/ + u_int8_t flg_mask; /* TCP flags mask byte */ + u_int8_t flg_cmp; /* TCP flags compare byte */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "inv" field in struct ipt_tcp. */ +#define IPT_TCP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define IPT_TCP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define IPT_TCP_INV_FLAGS 0x04 /* Invert the sense of TCP flags. */ +#define IPT_TCP_INV_OPTION 0x08 /* Invert the sense of option test. */ +#define IPT_TCP_INV_MASK 0x0F /* All possible flags. */ + +/* UDP matching stuff */ +struct ipt_udp +{ + u_int16_t spts[2]; /* Source port range. */ + u_int16_t dpts[2]; /* Destination port range. */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "invflags" field in struct ipt_udp. */ +#define IPT_UDP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define IPT_UDP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define IPT_UDP_INV_MASK 0x03 /* All possible flags. */ + +/* ICMP matching stuff */ +struct ipt_icmp +{ + u_int8_t type; /* type to match */ + u_int8_t code[2]; /* range of code */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "inv" field for struct ipt_icmp. */ +#define IPT_ICMP_INV 0x01 /* Invert the sense of type/code test */ + +/* The argument to IPT_SO_GET_INFO */ +struct ipt_getinfo +{ + /* Which table: caller fills this in. */ + char name[IPT_TABLE_MAXNAMELEN]; + + /* Kernel fills these in. */ + /* Which hook entry points are valid: bitmask */ + unsigned int valid_hooks; + + /* Hook entry points: one per netfilter hook. */ + unsigned int hook_entry[NF_IP_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_IP_NUMHOOKS]; + + /* Number of entries */ + unsigned int num_entries; + + /* Size of entries. */ + unsigned int size; +}; + +/* The argument to IPT_SO_SET_REPLACE. */ +struct ipt_replace +{ + /* Which table. */ + char name[IPT_TABLE_MAXNAMELEN]; + + /* Which hook entry points are valid: bitmask. You can't + change this. */ + unsigned int valid_hooks; + + /* Number of entries */ + unsigned int num_entries; + + /* Total size of new entries */ + unsigned int size; + + /* Hook entry points. */ + unsigned int hook_entry[NF_IP_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_IP_NUMHOOKS]; + + /* Information about old entries: */ + /* Number of counters (must be equal to current number of entries). */ + unsigned int num_counters; + + /* The old entries' counters. */ + struct ipt_counters *counters; + + /* The entries (hang off end: not really an array). */ + struct ipt_entry entries[0]; +}; + +/* The argument to IPT_SO_ADD_COUNTERS. */ +struct ipt_counters_info +{ + /* Which table. */ + char name[IPT_TABLE_MAXNAMELEN]; + + unsigned int num_counters; + + /* The counters (actually `number' of these). */ + struct ipt_counters counters[0]; +}; + +/* The argument to IPT_SO_GET_ENTRIES. */ +struct ipt_get_entries +{ + /* Which table: user fills this in. */ + char name[IPT_TABLE_MAXNAMELEN]; + + /* User fills this in: total entry size. */ + unsigned int size; + + /* The entries. */ + struct ipt_entry entrytable[0]; +}; + +/* The argument to IPT_SO_GET_REVISION_*. Returns highest revision + * kernel supports, if >= revision. */ +struct ipt_get_revision +{ + char name[IPT_FUNCTION_MAXNAMELEN-1]; + + u_int8_t revision; +}; + +/* Standard return verdict, or do jump. */ +#define IPT_STANDARD_TARGET "" +/* Error verdict. */ +#define IPT_ERROR_TARGET "ERROR" + +/* Helper functions */ +static __inline__ struct ipt_entry_target * +ipt_get_target(struct ipt_entry *e) +{ + return (void *)e + e->target_offset; +} + +/* fn returns 0 to continue iteration */ +#define IPT_MATCH_ITERATE(e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ipt_entry_match *__match; \ + \ + for (__i = sizeof(struct ipt_entry); \ + __i < (e)->target_offset; \ + __i += __match->u.match_size) { \ + __match = (void *)(e) + __i; \ + \ + __ret = fn(__match , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* fn returns 0 to continue iteration */ +#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ipt_entry *__entry; \ + \ + for (__i = 0; __i < (size); __i += __entry->next_offset) { \ + __entry = (void *)(entries) + __i; \ + \ + __ret = fn(__entry , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* + * Main firewall chains definitions and global var's definitions. + */ +#endif /* _IPTABLES_H */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h new file mode 100644 index 0000000..13828e5 --- /dev/null +++ b/include/linux/netlink.h @@ -0,0 +1,101 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include <linux/socket.h> /* for sa_family_t */ +#include <linux/types.h> + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_SKIP 1 /* Reserved for ENskip */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Firewalling hook */ +#define NETLINK_TCPDIAG 4 /* TCP socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ARPD 8 +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */ + +#define MAX_LINKS 32 + +struct sockaddr_nl +{ + sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* process pid */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr +{ + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process PID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 1 /* It is request message. */ +#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 8 /* Echo this request */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +struct nlmsgerr +{ + int error; + struct nlmsghdr msg; +}; + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + + +#endif /* __LINUX_NETLINK_H */ diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h new file mode 100644 index 0000000..741d15b --- /dev/null +++ b/include/linux/pkt_cls.h @@ -0,0 +1,425 @@ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include <linux/pkt_sched.h> + +/* I think i could have done better macros ; for now this is stolen from + * some arch/mips code - jhs +*/ +#define _TC_MAKE32(x) ((x)) + +#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) +#define _TC_MAKEMASK(v,n) (_TC_MAKE32((_TC_MAKE32(1)<<(v))-1) << _TC_MAKE32(n)) +#define _TC_MAKEVALUE(v,n) (_TC_MAKE32(v) << _TC_MAKE32(n)) +#define _TC_GETVALUE(v,n,m) ((_TC_MAKE32(v) & _TC_MAKE32(m)) >> _TC_MAKE32(n)) + +/* verdict bit breakdown + * +bit 0: when set -> this packet has been munged already + +bit 1: when set -> It is ok to munge this packet + +bit 2,3,4,5: Reclassify counter - sort of reverse TTL - if exceeded +assume loop + +bit 6,7: Where this packet was last seen +0: Above the transmit example at the socket level +1: on the Ingress +2: on the Egress + +bit 8: when set --> Request not to classify on ingress. + +bits 9,10,11: redirect counter - redirect TTL. Loop avoidance + + * + * */ + +#define TC_MUNGED _TC_MAKEMASK1(0) +#define SET_TC_MUNGED(v) ( TC_MUNGED | (v & ~TC_MUNGED)) +#define CLR_TC_MUNGED(v) ( v & ~TC_MUNGED) + +#define TC_OK2MUNGE _TC_MAKEMASK1(1) +#define SET_TC_OK2MUNGE(v) ( TC_OK2MUNGE | (v & ~TC_OK2MUNGE)) +#define CLR_TC_OK2MUNGE(v) ( v & ~TC_OK2MUNGE) + +#define S_TC_VERD _TC_MAKE32(2) +#define M_TC_VERD _TC_MAKEMASK(4,S_TC_VERD) +#define G_TC_VERD(x) _TC_GETVALUE(x,S_TC_VERD,M_TC_VERD) +#define V_TC_VERD(x) _TC_MAKEVALUE(x,S_TC_VERD) +#define SET_TC_VERD(v,n) ((V_TC_VERD(n)) | (v & ~M_TC_VERD)) + +#define S_TC_FROM _TC_MAKE32(6) +#define M_TC_FROM _TC_MAKEMASK(2,S_TC_FROM) +#define G_TC_FROM(x) _TC_GETVALUE(x,S_TC_FROM,M_TC_FROM) +#define V_TC_FROM(x) _TC_MAKEVALUE(x,S_TC_FROM) +#define SET_TC_FROM(v,n) ((V_TC_FROM(n)) | (v & ~M_TC_FROM)) +#define AT_STACK 0x0 +#define AT_INGRESS 0x1 +#define AT_EGRESS 0x2 + +#define TC_NCLS _TC_MAKEMASK1(8) +#define SET_TC_NCLS(v) ( TC_NCLS | (v & ~TC_NCLS)) +#define CLR_TC_NCLS(v) ( v & ~TC_NCLS) + +#define S_TC_RTTL _TC_MAKE32(9) +#define M_TC_RTTL _TC_MAKEMASK(3,S_TC_RTTL) +#define G_TC_RTTL(x) _TC_GETVALUE(x,S_TC_RTTL,M_TC_RTTL) +#define V_TC_RTTL(x) _TC_MAKEVALUE(x,S_TC_RTTL) +#define SET_TC_RTTL(v,n) ((V_TC_RTTL(n)) | (v & ~M_TC_RTTL)) + +#define S_TC_AT _TC_MAKE32(12) +#define M_TC_AT _TC_MAKEMASK(2,S_TC_AT) +#define G_TC_AT(x) _TC_GETVALUE(x,S_TC_AT,M_TC_AT) +#define V_TC_AT(x) _TC_MAKEVALUE(x,S_TC_AT) +#define SET_TC_AT(v,n) ((V_TC_AT(n)) | (v & ~M_TC_AT)) + +/* Action attributes */ +enum +{ + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + __TCA_ACT_MAX +}; + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 +#define MAX_REC_LOOP 4 +#define MAX_RED_LOOP 4 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_JUMP 0x10000000 + +/* Action type identifiers*/ +enum +{ + TCA_ID_UNSPEC=0, + TCA_ID_POLICE=1, + /* other actions go here */ + __TCA_ID_MAX=255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police +{ + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t +{ + __u64 install; + __u64 lastuse; + __u64 expires; +}; + +struct tc_cnt +{ + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum +{ + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + __TCA_POLICE_MAX +#define TCA_POLICE_RESULT TCA_POLICE_RESULT +}; + +#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) + +/* U32 filters */ + +#define TC_U32_HTID(h) ((h)&0xFFF00000) +#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20) +#define TC_U32_HASH(h) (((h)>>12)&0xFF) +#define TC_U32_NODE(h) ((h)&0xFFF) +#define TC_U32_KEY(h) ((h)&0xFFFFF) +#define TC_U32_UNSPEC 0 +#define TC_U32_ROOT (0xFFF00000) + +enum +{ + TCA_U32_UNSPEC, + TCA_U32_CLASSID, + TCA_U32_HASH, + TCA_U32_LINK, + TCA_U32_DIVISOR, + TCA_U32_SEL, + TCA_U32_POLICE, + TCA_U32_ACT, + TCA_U32_INDEV, + TCA_U32_PCNT, + TCA_U32_MARK, + __TCA_U32_MAX +}; + +#define TCA_U32_MAX (__TCA_U32_MAX - 1) + +struct tc_u32_key +{ + __u32 mask; + __u32 val; + int off; + int offmask; +}; + +struct tc_u32_sel +{ + unsigned char flags; + unsigned char offshift; + unsigned char nkeys; + + __u16 offmask; + __u16 off; + short offoff; + + short hoff; + __u32 hmask; + struct tc_u32_key keys[0]; +}; + +struct tc_u32_mark +{ + __u32 val; + __u32 mask; + __u32 success; +}; + +struct tc_u32_pcnt +{ + __u64 rcnt; + __u64 rhit; + __u64 kcnts[0]; +}; + +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum +{ + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + TCA_RSVP_ACT, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi +{ + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo +{ + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; +}; + +/* ROUTE filter */ + +enum +{ + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + TCA_ROUTE4_ACT, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum +{ + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */ + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum +{ + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +/* Basic filter */ + +enum +{ + TCA_BASIC_UNSPEC, + TCA_BASIC_CLASSID, + TCA_BASIC_EMATCHES, + TCA_BASIC_ACT, + TCA_BASIC_POLICE, + __TCA_BASIC_MAX +}; + +#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) + +/* Extended Matches */ + +struct tcf_ematch_tree_hdr +{ + __u16 nmatches; + __u16 progid; +}; + +enum +{ + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr +{ + __u16 matchid; + __u16 kind; + __u16 flags; + __u16 pad; /* currently unused */ +}; + +/* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-----------------------+-+-+---+ + * | Unused |S|I| R | + * +-----------------------+-+-+---+ + * + * R(2) ::= relation to next ematch + * where: 0 0 END (last ematch) + * 0 1 AND + * 1 0 OR + * 1 1 Unused (invalid) + * I(1) ::= invert result + * S(1) ::= simple payload + */ +#define TCF_EM_REL_END 0 +#define TCF_EM_REL_AND (1<<0) +#define TCF_EM_REL_OR (1<<1) +#define TCF_EM_INVERT (1<<2) +#define TCF_EM_SIMPLE (1<<3) + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK) + +enum +{ + TCF_LAYER_LINK, + TCF_LAYER_NETWORK, + TCF_LAYER_TRANSPORT, + __TCF_LAYER_MAX +}; +#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) + +/* Ematch type assignments + * 1..32767 Reserved for ematches inside kernel tree + * 32768..65535 Free to use, not reliable + */ +enum +{ + TCF_EM_CONTAINER, + TCF_EM_CMP, + TCF_EM_NBYTE, + TCF_EM_U32, + TCF_EM_META, + __TCF_EM_MAX +}; + +enum +{ + TCF_EM_PROG_TC +}; + +enum +{ + TCF_EM_OPND_EQ, + TCF_EM_OPND_GT, + TCF_EM_OPND_LT +}; + +#endif diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h new file mode 100644 index 0000000..73d84c0 --- /dev/null +++ b/include/linux/pkt_sched.h @@ -0,0 +1,454 @@ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats +{ + __u64 bytes; /* NUmber of enqueues bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +}; + +struct tc_estimator +{ + signed char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) + +struct tc_ratespec +{ + unsigned char cell_log; + unsigned char __reserved; + unsigned short feature; + short addend; + unsigned short mpu; + __u32 rate; +}; + +/* FIFO section */ + +struct tc_fifo_qopt +{ + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 + +struct tc_prio_qopt +{ + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +/* TBF section */ + +struct tc_tbf_qopt +{ + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum +{ + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, + __TCA_TBF_MAX, +}; + +#define TCA_TBF_MAX (__TCA_TBF_MAX - 1) + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt +{ + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +/* + * NOTE: limit, divisor and flows are hardwired to code at the moment. + * + * limit=flows=128, divisor=1024; + * + * The only reason for this is efficiency, it is possible + * to change these parameters in compile time. + */ + +/* RED section */ + +enum +{ + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, + __TCA_RED_MAX, +}; + +#define TCA_RED_MAX (__TCA_RED_MAX - 1) + +struct tc_red_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; +#define TC_RED_ECN 1 +}; + +struct tc_red_xstats +{ + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum +{ + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, + __TCA_GRED_MAX, +}; + +#define TCA_GRED_MAX (__TCA_GRED_MAX - 1) + +#define TCA_SET_OFF TCA_GRED_PARMS +struct tc_gred_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) +*/ + __u32 qth_min; /* Min average length threshold (bytes) +*/ + __u32 qth_max; /* Max average length threshold (bytes) +*/ + __u32 DP; /* upto 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; +/* gred setup */ +struct tc_gred_sopt +{ + __u32 DPs; + __u32 def_DP; + __u8 grio; +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 8 +#define TC_HTB_MAXDEPTH 8 +#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */ + +struct tc_htb_opt +{ + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; + __u32 level; /* out only */ + __u32 prio; +}; +struct tc_htb_glob +{ + __u32 version; /* to match HTB/TC */ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 debug; /* debug flags */ + + /* stats */ + __u32 direct_pkts; /* count of non shapped packets */ +}; +enum +{ + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, + __TCA_HTB_MAX, +}; + +#define TCA_HTB_MAX (__TCA_HTB_MAX - 1) + +struct tc_htb_xstats +{ + __u32 lends; + __u32 borrows; + __u32 giants; /* too big packets (rate will not be accurate) */ + __u32 tokens; + __u32 ctokens; +}; + +/* HFSC section */ + +struct tc_hfsc_qopt +{ + __u16 defcls; /* default class */ +}; + +struct tc_service_curve +{ + __u32 m1; /* slope of the first segment in bps */ + __u32 d; /* x-projection of the first segment in us */ + __u32 m2; /* slope of the second segment in bps */ +}; + +struct tc_hfsc_stats +{ + __u64 work; /* total work done */ + __u64 rtwork; /* work done by real-time criteria */ + __u32 period; /* current period */ + __u32 level; /* class level in hierarchy */ +}; + +enum +{ + TCA_HFSC_UNSPEC, + TCA_HFSC_RSC, + TCA_HFSC_FSC, + TCA_HFSC_USC, + __TCA_HFSC_MAX, +}; + +#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) + + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt +{ + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt +{ + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl +{ + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u32 penalty; +}; + +struct tc_cbq_police +{ + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt +{ + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats +{ + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum +{ + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, + __TCA_CBQ_MAX, +}; + +#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE, + __TCA_DSMARK_MAX, +}; + +#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ + __TCA_ATM_MAX, +}; + +#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) + +/* Network emulator */ + +enum +{ + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, + __TCA_NETEM_MAX, +}; + +#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1) + +struct tc_netem_qopt +{ + __u32 latency; /* added delay (us) */ + __u32 limit; /* fifo limit (packets) */ + __u32 loss; /* random packet loss (0=none ~0=100%) */ + __u32 gap; /* re-ordering gap (0 for delay all) */ + __u32 duplicate; /* random packet dup (0=none ~0=100%) */ + __u32 jitter; /* random jitter in latency (us) */ +}; + +struct tc_netem_corr +{ + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +#define NETEM_DIST_SCALE 8192 + +#endif diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h new file mode 100644 index 0000000..1facfe9 --- /dev/null +++ b/include/linux/rtnetlink.h @@ -0,0 +1,749 @@ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include <linux/netlink.h> + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + RTM_GETPREFIX = 54, +#define RTM_GETPREFIX RTM_GETPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_MAX, +#define RTM_MAX RTM_MAX +}; + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg +{ + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum +{ + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t +{ + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ + +/* Reserved table identifiers */ + +enum rt_class_t +{ + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + __RT_TABLE_MAX +}; +#define RT_TABLE_MAX (__RT_TABLE_MAX - 1) + + + +/* Routing message attributes */ + +enum rtattr_type_t +{ + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop +{ + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo +{ + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum +{ + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN 0x00000001 +#define RTAX_FEATURE_SACK 0x00000002 +#define RTAX_FEATURE_TIMESTAMP 0x00000004 + +struct rta_session +{ + __u8 proto; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + + +/********************************************************* + * Interface address. + ****/ + +struct ifaddrmsg +{ + unsigned char ifa_family; + unsigned char ifa_prefixlen; /* The prefix length */ + unsigned char ifa_flags; /* Flags */ + unsigned char ifa_scope; /* See above */ + int ifa_index; /* Link index */ +}; + +enum +{ + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + __IFA_MAX +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ + +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 + +struct ifa_cacheinfo +{ + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + + +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +/* + Important comment: + IFA_ADDRESS is prefix address, rather than local interface address. + It makes no difference for normally configured broadcast interfaces, + but for point-to-point IFA_ADDRESS is DESTINATION address, + local address is supplied in IFA_LOCAL attribute. + */ + +/************************************************************** + * Neighbour discovery. + ****/ + +struct ndmsg +{ + unsigned char ndm_family; + unsigned char ndm_pad1; + unsigned short ndm_pad2; + int ndm_ifindex; /* Link index */ + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum +{ + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +#define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_PROXY 0x08 /* == ATF_PUBL */ +#define NTF_ROUTER 0x80 + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + + +struct nda_cacheinfo +{ + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg +{ + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg +{ + unsigned char prefix_family; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo +{ + __u32 preferred_time; + __u32 valid_time; +}; + +/* The struct should be in sync with struct net_device_stats */ +struct rtnl_link_stats +{ + __u32 rx_packets; /* total packets received */ + __u32 tx_packets; /* total packets transmitted */ + __u32 rx_bytes; /* total bytes received */ + __u32 tx_bytes; /* total bytes transmitted */ + __u32 rx_errors; /* bad packets received */ + __u32 tx_errors; /* packet transmit problems */ + __u32 rx_dropped; /* no space in linux buffers */ + __u32 tx_dropped; /* no space available in linux */ + __u32 multicast; /* multicast packets received */ + __u32 collisions; + + /* detailed rx_errors: */ + __u32 rx_length_errors; + __u32 rx_over_errors; /* receiver ring buff overflow */ + __u32 rx_crc_errors; /* recved pkt with crc error */ + __u32 rx_frame_errors; /* recv'd frame alignment error */ + __u32 rx_fifo_errors; /* recv'r fifo overrun */ + __u32 rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + __u32 tx_aborted_errors; + __u32 tx_carrier_errors; + __u32 tx_fifo_errors; + __u32 tx_heartbeat_errors; + __u32 tx_window_errors; + + /* for cslip etc */ + __u32 rx_compressed; + __u32 tx_compressed; +}; + +/* The struct should be in sync with struct ifmap */ +struct rtnl_link_ifmap +{ + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +enum +{ + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + __IFLA_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 1) + +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neither of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/* Subtype attributes for IFLA_PROTINFO */ +enum +{ + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ + IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +struct ifla_cacheinfo +{ + __u32 max_reasm_len; + __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ + __u32 reachable_time; + __u32 retrans_time; +}; + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg +{ + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; + __u32 tcm_info; +}; + +enum +{ + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_ACT_STATS, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + + +/* RTnetlink multicast groups */ + +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 + +/* TC action piece */ +struct tcamsg +{ + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +#define TCA_ACT_TAB 1 /* attr type must be >=1 */ +#define TCAA_MAX 1 + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/include/linux/tc_act/tc_gact.h b/include/linux/tc_act/tc_gact.h new file mode 100644 index 0000000..23a03eb --- /dev/null +++ b/include/linux/tc_act/tc_gact.h @@ -0,0 +1,34 @@ +#ifndef __LINUX_TC_GACT_H +#define __LINUX_TC_GACT_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_GACT 5 +struct tc_gact +{ + tc_gen; + +}; + +struct tc_gact_p +{ +#define PGACT_NONE 0 +#define PGACT_NETRAND 1 +#define PGACT_DETERM 2 +#define MAX_RAND (PGACT_DETERM + 1 ) + __u16 ptype; + __u16 pval; + int paction; +}; + +enum +{ + TCA_GACT_UNSPEC, + TCA_GACT_TM, + TCA_GACT_PARMS, + TCA_GACT_PROB, + __TCA_GACT_MAX +}; +#define TCA_GACT_MAX (__TCA_GACT_MAX - 1) + +#endif diff --git a/include/linux/tc_act/tc_ipt.h b/include/linux/tc_act/tc_ipt.h new file mode 100644 index 0000000..4b6f7b6 --- /dev/null +++ b/include/linux/tc_act/tc_ipt.h @@ -0,0 +1,21 @@ +#ifndef __LINUX_TC_IPT_H +#define __LINUX_TC_IPT_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_IPT 6 + +enum +{ + TCA_IPT_UNSPEC, + TCA_IPT_TABLE, + TCA_IPT_HOOK, + TCA_IPT_INDEX, + TCA_IPT_CNT, + TCA_IPT_TM, + TCA_IPT_TARG, + __TCA_IPT_MAX +}; +#define TCA_IPT_MAX (__TCA_IPT_MAX - 1) + +#endif diff --git a/include/linux/tc_act/tc_mirred.h b/include/linux/tc_act/tc_mirred.h new file mode 100644 index 0000000..71d6340 --- /dev/null +++ b/include/linux/tc_act/tc_mirred.h @@ -0,0 +1,28 @@ +#ifndef __LINUX_TC_MIR_H +#define __LINUX_TC_MIR_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_MIRRED 8 +#define TCA_EGRESS_REDIR 1 /* packet redirect to EGRESS*/ +#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */ +#define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/ +#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */ + +struct tc_mirred +{ + tc_gen; + int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ + __u32 ifindex; /* ifindex of egress port */ +}; + +enum +{ + TCA_MIRRED_UNSPEC, + TCA_MIRRED_TM, + TCA_MIRRED_PARMS, + __TCA_MIRRED_MAX +}; +#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1) + +#endif diff --git a/include/linux/tc_act/tc_pedit.h b/include/linux/tc_act/tc_pedit.h new file mode 100644 index 0000000..83e56e3 --- /dev/null +++ b/include/linux/tc_act/tc_pedit.h @@ -0,0 +1,36 @@ +#ifndef __LINUX_TC_PED_H +#define __LINUX_TC_PED_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_PEDIT 7 + +enum +{ + TCA_PEDIT_UNSPEC, + TCA_PEDIT_TM, + TCA_PEDIT_PARMS, + __TCA_PEDIT_MAX +}; +#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1) + +struct tc_pedit_key +{ + __u32 mask; /* AND */ + __u32 val; /*XOR */ + __u32 off; /*offset */ + __u32 at; + __u32 offmask; + __u32 shift; +}; + +struct tc_pedit_sel +{ + tc_gen; + unsigned char nkeys; + unsigned char flags; + struct tc_pedit_key keys[0]; +}; +#define tc_pedit tc_pedit_sel + +#endif diff --git a/include/linux/tcp.h b/include/linux/tcp.h new file mode 100644 index 0000000..9703d6b --- /dev/null +++ b/include/linux/tcp.h @@ -0,0 +1,194 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the TCP protocol. + * + * Version: @(#)tcp.h 1.0.2 04/28/93 + * + * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_TCP_H +#define _LINUX_TCP_H + +#include <linux/types.h> +#include <asm/byteorder.h> + +struct tcphdr { + __u16 source; + __u16 dest; + __u32 seq; + __u32 ack_seq; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 res1:4, + doff:4, + fin:1, + syn:1, + rst:1, + psh:1, + ack:1, + urg:1, + ece:1, + cwr:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u16 doff:4, + res1:4, + cwr:1, + ece:1, + urg:1, + ack:1, + psh:1, + rst:1, + syn:1, + fin:1; +#else +#error "Adjust your <asm/byteorder.h> defines" +#endif + __u16 window; + __u16 check; + __u16 urg_ptr; +}; + + +enum { + TCP_ESTABLISHED = 1, + TCP_SYN_SENT, + TCP_SYN_RECV, + TCP_FIN_WAIT1, + TCP_FIN_WAIT2, + TCP_TIME_WAIT, + TCP_CLOSE, + TCP_CLOSE_WAIT, + TCP_LAST_ACK, + TCP_LISTEN, + TCP_CLOSING, /* now a valid state */ + + TCP_MAX_STATES /* Leave at the end! */ +}; + +#define TCP_STATE_MASK 0xF +#define TCP_ACTION_FIN (1 << 7) + +enum { + TCPF_ESTABLISHED = (1 << 1), + TCPF_SYN_SENT = (1 << 2), + TCPF_SYN_RECV = (1 << 3), + TCPF_FIN_WAIT1 = (1 << 4), + TCPF_FIN_WAIT2 = (1 << 5), + TCPF_TIME_WAIT = (1 << 6), + TCPF_CLOSE = (1 << 7), + TCPF_CLOSE_WAIT = (1 << 8), + TCPF_LAST_ACK = (1 << 9), + TCPF_LISTEN = (1 << 10), + TCPF_CLOSING = (1 << 11) +}; + +/* + * The union cast uses a gcc extension to avoid aliasing problems + * (union is compatible to any of its members) + * This means this part of the code is -fstrict-aliasing safe now. + */ +union tcp_word_hdr { + struct tcphdr hdr; + __u32 words[5]; +}; + +#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) + +enum { + TCP_FLAG_CWR = __constant_htonl(0x00800000), + TCP_FLAG_ECE = __constant_htonl(0x00400000), + TCP_FLAG_URG = __constant_htonl(0x00200000), + TCP_FLAG_ACK = __constant_htonl(0x00100000), + TCP_FLAG_PSH = __constant_htonl(0x00080000), + TCP_FLAG_RST = __constant_htonl(0x00040000), + TCP_FLAG_SYN = __constant_htonl(0x00020000), + TCP_FLAG_FIN = __constant_htonl(0x00010000), + TCP_RESERVED_BITS = __constant_htonl(0x0F000000), + TCP_DATA_OFFSET = __constant_htonl(0xF0000000) +}; + +/* TCP socket options */ +#define TCP_NODELAY 1 /* Turn off Nagle's algorithm. */ +#define TCP_MAXSEG 2 /* Limit MSS */ +#define TCP_CORK 3 /* Never send partially complete segments */ +#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#define TCP_KEEPINTVL 5 /* Interval between keepalives */ +#define TCP_KEEPCNT 6 /* Number of keepalives before death */ +#define TCP_SYNCNT 7 /* Number of SYN retransmits */ +#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ +#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ +#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ +#define TCP_INFO 11 /* Information about this connection. */ +#define TCP_QUICKACK 12 /* Block/reenable quick acks */ + +#define TCPI_OPT_TIMESTAMPS 1 +#define TCPI_OPT_SACK 2 +#define TCPI_OPT_WSCALE 4 +#define TCPI_OPT_ECN 8 + +enum tcp_ca_state +{ + TCP_CA_Open = 0, +#define TCPF_CA_Open (1<<TCP_CA_Open) + TCP_CA_Disorder = 1, +#define TCPF_CA_Disorder (1<<TCP_CA_Disorder) + TCP_CA_CWR = 2, +#define TCPF_CA_CWR (1<<TCP_CA_CWR) + TCP_CA_Recovery = 3, +#define TCPF_CA_Recovery (1<<TCP_CA_Recovery) + TCP_CA_Loss = 4 +#define TCPF_CA_Loss (1<<TCP_CA_Loss) +}; + +struct tcp_info +{ + __u8 tcpi_state; + __u8 tcpi_ca_state; + __u8 tcpi_retransmits; + __u8 tcpi_probes; + __u8 tcpi_backoff; + __u8 tcpi_options; + __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; + + __u32 tcpi_rto; + __u32 tcpi_ato; + __u32 tcpi_snd_mss; + __u32 tcpi_rcv_mss; + + __u32 tcpi_unacked; + __u32 tcpi_sacked; + __u32 tcpi_lost; + __u32 tcpi_retrans; + __u32 tcpi_fackets; + + /* Times. */ + __u32 tcpi_last_data_sent; + __u32 tcpi_last_ack_sent; /* Not remembered, sorry. */ + __u32 tcpi_last_data_recv; + __u32 tcpi_last_ack_recv; + + /* Metrics. */ + __u32 tcpi_pmtu; + __u32 tcpi_rcv_ssthresh; + __u32 tcpi_rtt; + __u32 tcpi_rttvar; + __u32 tcpi_snd_ssthresh; + __u32 tcpi_snd_cwnd; + __u32 tcpi_advmss; + __u32 tcpi_reordering; + + __u32 tcpi_rcv_rtt; + __u32 tcpi_rcv_space; + + __u32 tcpi_total_retrans; +}; + + +#endif /* _LINUX_TCP_H */ diff --git a/include/linux/tcp_diag.h b/include/linux/tcp_diag.h new file mode 100644 index 0000000..ceee962 --- /dev/null +++ b/include/linux/tcp_diag.h @@ -0,0 +1,127 @@ +#ifndef _TCP_DIAG_H_ +#define _TCP_DIAG_H_ 1 + +/* Just some random number */ +#define TCPDIAG_GETSOCK 18 + +/* Socket identity */ +struct tcpdiag_sockid +{ + __u16 tcpdiag_sport; + __u16 tcpdiag_dport; + __u32 tcpdiag_src[4]; + __u32 tcpdiag_dst[4]; + __u32 tcpdiag_if; + __u32 tcpdiag_cookie[2]; +#define TCPDIAG_NOCOOKIE (~0U) +}; + +/* Request structure */ + +struct tcpdiagreq +{ + __u8 tcpdiag_family; /* Family of addresses. */ + __u8 tcpdiag_src_len; + __u8 tcpdiag_dst_len; + __u8 tcpdiag_ext; /* Query extended information */ + + struct tcpdiag_sockid id; + + __u32 tcpdiag_states; /* States to dump */ + __u32 tcpdiag_dbs; /* Tables to dump (NI) */ +}; + +enum +{ + TCPDIAG_REQ_NONE, + TCPDIAG_REQ_BYTECODE, +}; + +#define TCPDIAG_REQ_MAX TCPDIAG_REQ_BYTECODE + +/* Bytecode is sequence of 4 byte commands followed by variable arguments. + * All the commands identified by "code" are conditional jumps forward: + * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be + * length of the command and its arguments. + */ + +struct tcpdiag_bc_op +{ + unsigned char code; + unsigned char yes; + unsigned short no; +}; + +enum +{ + TCPDIAG_BC_NOP, + TCPDIAG_BC_JMP, + TCPDIAG_BC_S_GE, + TCPDIAG_BC_S_LE, + TCPDIAG_BC_D_GE, + TCPDIAG_BC_D_LE, + TCPDIAG_BC_AUTO, + TCPDIAG_BC_S_COND, + TCPDIAG_BC_D_COND, +}; + +struct tcpdiag_hostcond +{ + __u8 family; + __u8 prefix_len; + int port; + __u32 addr[0]; +}; + +/* Base info structure. It contains socket identity (addrs/ports/cookie) + * and, alas, the information shown by netstat. */ +struct tcpdiagmsg +{ + __u8 tcpdiag_family; + __u8 tcpdiag_state; + __u8 tcpdiag_timer; + __u8 tcpdiag_retrans; + + struct tcpdiag_sockid id; + + __u32 tcpdiag_expires; + __u32 tcpdiag_rqueue; + __u32 tcpdiag_wqueue; + __u32 tcpdiag_uid; + __u32 tcpdiag_inode; +}; + +/* Extensions */ + +enum +{ + TCPDIAG_NONE, + TCPDIAG_MEMINFO, + TCPDIAG_INFO, + TCPDIAG_VEGASINFO, +}; + +#define TCPDIAG_MAX TCPDIAG_VEGASINFO + + +/* TCPDIAG_MEM */ + +struct tcpdiag_meminfo +{ + __u32 tcpdiag_rmem; + __u32 tcpdiag_wmem; + __u32 tcpdiag_fmem; + __u32 tcpdiag_tmem; +}; + +/* TCPDIAG_VEGASINFO */ + +struct tcpvegas_info { + __u32 tcpv_enabled; + __u32 tcpv_rttcnt; + __u32 tcpv_rtt; + __u32 tcpv_minrtt; +}; + + +#endif /* _TCP_DIAG_H_ */ diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h new file mode 100644 index 0000000..f0df02a --- /dev/null +++ b/include/linux/xfrm.h @@ -0,0 +1,258 @@ +#ifndef _LINUX_XFRM_H +#define _LINUX_XFRM_H + +#include <linux/types.h> + +/* All of the structures in this file may not change size as they are + * passed into the kernel from userspace via netlink sockets. + */ + +/* Structure to encapsulate addresses. I do not want to use + * "standard" structure. My apologies. + */ +typedef union +{ + __u32 a4; + __u32 a6[4]; +} xfrm_address_t; + +/* Ident of a specific xfrm_state. It is used on input to lookup + * the state by (spi,daddr,ah/esp) or to store information about + * spi, protocol and tunnel address on output. + */ +struct xfrm_id +{ + xfrm_address_t daddr; + __u32 spi; + __u8 proto; +}; + +/* Selector, used as selector both on policy rules (SPD) and SAs. */ + +struct xfrm_selector +{ + xfrm_address_t daddr; + xfrm_address_t saddr; + __u16 dport; + __u16 dport_mask; + __u16 sport; + __u16 sport_mask; + __u16 family; + __u8 prefixlen_d; + __u8 prefixlen_s; + __u8 proto; + int ifindex; + uid_t user; +}; + +#define XFRM_INF (~(__u64)0) + +struct xfrm_lifetime_cfg +{ + __u64 soft_byte_limit; + __u64 hard_byte_limit; + __u64 soft_packet_limit; + __u64 hard_packet_limit; + __u64 soft_add_expires_seconds; + __u64 hard_add_expires_seconds; + __u64 soft_use_expires_seconds; + __u64 hard_use_expires_seconds; +}; + +struct xfrm_lifetime_cur +{ + __u64 bytes; + __u64 packets; + __u64 add_time; + __u64 use_time; +}; + +struct xfrm_replay_state +{ + __u32 oseq; + __u32 seq; + __u32 bitmap; +}; + +struct xfrm_algo { + char alg_name[64]; + int alg_key_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_stats { + __u32 replay_window; + __u32 replay; + __u32 integrity_failed; +}; + +enum +{ + XFRM_POLICY_IN = 0, + XFRM_POLICY_OUT = 1, + XFRM_POLICY_FWD = 2, + XFRM_POLICY_MAX = 3 +}; + +enum +{ + XFRM_SHARE_ANY, /* No limitations */ + XFRM_SHARE_SESSION, /* For this session only */ + XFRM_SHARE_USER, /* For this user only */ + XFRM_SHARE_UNIQUE /* Use once */ +}; + +/* Netlink configuration messages. */ +enum { + XFRM_MSG_BASE = 0x10, + + XFRM_MSG_NEWSA = 0x10, +#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA + XFRM_MSG_DELSA, +#define XFRM_MSG_DELSA XFRM_MSG_DELSA + XFRM_MSG_GETSA, +#define XFRM_MSG_GETSA XFRM_MSG_GETSA + + XFRM_MSG_NEWPOLICY, +#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY + XFRM_MSG_DELPOLICY, +#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY + XFRM_MSG_GETPOLICY, +#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY + + XFRM_MSG_ALLOCSPI, +#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI + XFRM_MSG_ACQUIRE, +#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE + XFRM_MSG_EXPIRE, +#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE + + XFRM_MSG_UPDPOLICY, +#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY + XFRM_MSG_UPDSA, +#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA + + XFRM_MSG_POLEXPIRE, +#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE + + XFRM_MSG_FLUSHSA, +#define XFRM_MSG_FLUSHSA XFRM_MSG_FLUSHSA + XFRM_MSG_FLUSHPOLICY, +#define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY + + XFRM_MSG_MAX +}; + +struct xfrm_user_tmpl { + struct xfrm_id id; + __u16 family; + xfrm_address_t saddr; + __u32 reqid; + __u8 mode; + __u8 share; + __u8 optional; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; +}; + +struct xfrm_encap_tmpl { + __u16 encap_type; + __u16 encap_sport; + __u16 encap_dport; + xfrm_address_t encap_oa; +}; + +/* Netlink message attributes. */ +enum xfrm_attr_type_t { + XFRMA_UNSPEC, + XFRMA_ALG_AUTH, /* struct xfrm_algo */ + XFRMA_ALG_CRYPT, /* struct xfrm_algo */ + XFRMA_ALG_COMP, /* struct xfrm_algo */ + XFRMA_ENCAP, /* struct xfrm_algo + struct xfrm_encap_tmpl */ + XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */ + __XFRMA_MAX + +#define XFRMA_MAX (__XFRMA_MAX - 1) +}; + +struct xfrm_usersa_info { + struct xfrm_selector sel; + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + struct xfrm_stats stats; + __u32 seq; + __u32 reqid; + __u16 family; + __u8 mode; /* 0=transport,1=tunnel */ + __u8 replay_window; + __u8 flags; +#define XFRM_STATE_NOECN 1 +#define XFRM_STATE_DECAP_DSCP 2 +}; + +struct xfrm_usersa_id { + xfrm_address_t daddr; + __u32 spi; + __u16 family; + __u8 proto; +}; + +struct xfrm_userspi_info { + struct xfrm_usersa_info info; + __u32 min; + __u32 max; +}; + +struct xfrm_userpolicy_info { + struct xfrm_selector sel; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + __u32 priority; + __u32 index; + __u8 dir; + __u8 action; +#define XFRM_POLICY_ALLOW 0 +#define XFRM_POLICY_BLOCK 1 + __u8 flags; +#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ + __u8 share; +}; + +struct xfrm_userpolicy_id { + struct xfrm_selector sel; + __u32 index; + __u8 dir; +}; + +struct xfrm_user_acquire { + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_selector sel; + struct xfrm_userpolicy_info policy; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; + __u32 seq; +}; + +struct xfrm_user_expire { + struct xfrm_usersa_info state; + __u8 hard; +}; + +struct xfrm_user_polexpire { + struct xfrm_userpolicy_info pol; + __u8 hard; +}; + +struct xfrm_usersa_flush { + __u8 proto; +}; + +#define XFRMGRP_ACQUIRE 1 +#define XFRMGRP_EXPIRE 2 + +#endif /* _LINUX_XFRM_H */ diff --git a/include/ll_map.h b/include/ll_map.h new file mode 100644 index 0000000..3bff5e9 --- /dev/null +++ b/include/ll_map.h @@ -0,0 +1,13 @@ +#ifndef __LL_MAP_H__ +#define __LL_MAP_H__ 1 + +extern int ll_remember_index(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int ll_init_map(struct rtnl_handle *rth); +extern int ll_name_to_index(const char *name); +extern const char *ll_index_to_name(int idx); +extern const char *ll_idx_n2a(int idx, char *buf); +extern int ll_index_to_type(int idx); +extern unsigned ll_index_to_flags(int idx); + +#endif /* __LL_MAP_H__ */ diff --git a/include/rt_names.h b/include/rt_names.h new file mode 100644 index 0000000..249231e --- /dev/null +++ b/include/rt_names.h @@ -0,0 +1,30 @@ +#ifndef RT_NAMES_H_ +#define RT_NAMES_H_ 1 + +#include <asm/types.h> + +char* rtnl_rtprot_n2a(int id, char *buf, int len); +char* rtnl_rtscope_n2a(int id, char *buf, int len); +char* rtnl_rttable_n2a(int id, char *buf, int len); +char* rtnl_rtrealm_n2a(int id, char *buf, int len); +char* rtnl_dsfield_n2a(int id, char *buf, int len); +int rtnl_rtprot_a2n(__u32 *id, char *arg); +int rtnl_rtscope_a2n(__u32 *id, char *arg); +int rtnl_rttable_a2n(__u32 *id, char *arg); +int rtnl_rtrealm_a2n(__u32 *id, char *arg); +int rtnl_dsfield_a2n(__u32 *id, char *arg); + +const char *inet_proto_n2a(int proto, char *buf, int len); +int inet_proto_a2n(char *buf); + + +const char * ll_type_n2a(int type, char *buf, int len); + +const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen); +int ll_addr_a2n(unsigned char *lladdr, int len, char *arg); + +const char * ll_proto_n2a(unsigned short id, char *buf, int len); +int ll_proto_a2n(unsigned short *id, char *buf); + + +#endif diff --git a/include/rtm_map.h b/include/rtm_map.h new file mode 100644 index 0000000..70bda7d --- /dev/null +++ b/include/rtm_map.h @@ -0,0 +1,10 @@ +#ifndef __RTM_MAP_H__ +#define __RTM_MAP_H__ 1 + +char *rtnl_rtntype_n2a(int id, char *buf, int len); +int rtnl_rtntype_a2n(int *id, char *arg); + +int get_rt_realms(__u32 *realms, char *arg); + + +#endif /* __RTM_MAP_H__ */ diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..906e394 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,126 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ 1 + +#include <asm/types.h> +#include <resolv.h> + +#include "libnetlink.h" +#include "ll_map.h" +#include "rtm_map.h" + +extern int preferred_family; +extern int show_stats; +extern int show_details; +extern int show_raw; +extern int resolve_hosts; +extern int oneline; +extern char * _SL_; + +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#ifndef IPPROTO_COMP +#define IPPROTO_COMP 108 +#endif +#ifndef IPSEC_PROTO_ANY +#define IPSEC_PROTO_ANY 255 +#endif + +#define SPRINT_BSIZE 64 +#define SPRINT_BUF(x) char x[SPRINT_BSIZE] + +extern void incomplete_command(void) __attribute__((noreturn)); + +#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) +#define NEXT_ARG_OK() (argc - 1 > 0) +#define PREV_ARG() do { argv--; argc++; } while(0) + +typedef struct +{ + __u8 family; + __u8 bytelen; + __s16 bitlen; + __u32 data[4]; +} inet_prefix; + +#define DN_MAXADDL 20 +#ifndef AF_DECnet +#define AF_DECnet 12 +#endif + +struct dn_naddr +{ + unsigned short a_len; + unsigned char a_addr[DN_MAXADDL]; +}; + +#define IPX_NODE_LEN 6 + +struct ipx_addr { + u_int32_t ipx_net; + u_int8_t ipx_node[IPX_NODE_LEN]; +}; + +extern __u32 get_addr32(const char *name); +extern int get_addr_1(inet_prefix *dst, const char *arg, int family); +extern int get_prefix_1(inet_prefix *dst, char *arg, int family); +extern int get_addr(inet_prefix *dst, const char *arg, int family); +extern int get_prefix(inet_prefix *dst, char *arg, int family); + +extern int get_integer(int *val, const char *arg, int base); +extern int get_unsigned(unsigned *val, const char *arg, int base); +#define get_byte get_u8 +#define get_ushort get_u16 +#define get_short get_s16 +extern int get_u64(__u64 *val, const char *arg, int base); +extern int get_u32(__u32 *val, const char *arg, int base); +extern int get_u16(__u16 *val, const char *arg, int base); +extern int get_s16(__s16 *val, const char *arg, int base); +extern int get_u8(__u8 *val, const char *arg, int base); +extern int get_s8(__s8 *val, const char *arg, int base); + +extern __u8* hexstring_n2a(const __u8 *str, int len, __u8 *buf, int blen); +extern __u8* hexstring_a2n(const __u8 *str, __u8 *buf, int blen); + +extern const char *format_host(int af, int len, const void *addr, + char *buf, int buflen); +extern const char *rt_addr_n2a(int af, int len, const void *addr, + char *buf, int buflen); + +void missarg(const char *) __attribute__((noreturn)); +void invarg(const char *, const char *) __attribute__((noreturn)); +void duparg(const char *, const char *) __attribute__((noreturn)); +void duparg2(const char *, const char *) __attribute__((noreturn)); +int matches(const char *arg, const char *pattern); +extern int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits); + +const char *dnet_ntop(int af, const void *addr, char *str, size_t len); +int dnet_pton(int af, const char *src, void *addr); + +const char *ipx_ntop(int af, const void *addr, char *str, size_t len); +int ipx_pton(int af, const char *src, void *addr); + +extern int __iproute2_hz_internal; +extern int __get_hz(void); + +static __inline__ int get_hz(void) +{ + if (__iproute2_hz_internal == 0) + __iproute2_hz_internal = __get_hz(); + return __iproute2_hz_internal; +} + +extern int __iproute2_user_hz_internal; +extern int __get_user_hz(void); + +static __inline__ int get_user_hz(void) +{ + if (__iproute2_user_hz_internal == 0) + __iproute2_user_hz_internal = __get_user_hz(); + return __iproute2_user_hz_internal; +} + +#endif /* __UTILS_H__ */ diff --git a/ip/Makefile b/ip/Makefile new file mode 100644 index 0000000..bcc419b --- /dev/null +++ b/ip/Makefile @@ -0,0 +1,24 @@ +IPOBJ=ip.o ipaddress.o iproute.o iprule.o \ + rtm_map.o iptunnel.o ipneigh.o iplink.o \ + ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o \ + ipxfrm.o xfrm_state.o xfrm_policy.o + +RTMONOBJ=rtmon.o + +ALLOBJ=$(IPOBJ) $(RTMONOBJ) +SCRIPTS=ifcfg rtpr routel routef +TARGETS=ip rtmon + +all: $(TARGETS) $(SCRIPTS) + +ip: $(IPOBJ) $(LIBNETLINK) $(LIBUTIL) + +rtmon: $(RTMONOBJ) $(LIBNETLINK) + +install: all + install -m 0755 -s $(TARGETS) $(DESTDIR)$(SBINDIR) + install -m 0755 $(SCRIPTS) $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(ALLOBJ) $(TARGETS) + diff --git a/ip/ifcfg b/ip/ifcfg new file mode 100755 index 0000000..ed6960f --- /dev/null +++ b/ip/ifcfg @@ -0,0 +1,145 @@ +#! /bin/bash + +CheckForwarding () { + local sbase fwd + sbase=/proc/sys/net/ipv4/conf + fwd=0 + if [ -d $sbase ]; then + for dir in $sbase/*/forwarding; do + fwd=$[$fwd + `cat $dir`] + done + else + fwd=2 + fi + return $fwd +} + +RestartRDISC () { + killall -HUP rdisc || rdisc -fs +} + +ABCMaskLen () { + local class; + + class=${1%%.*} + if [ "$1" = "" -o $class -eq 0 -o $class -ge 224 ]; then return 0 + elif [ $class -ge 224 ]; then return 0 + elif [ $class -ge 192 ]; then return 24 + elif [ $class -ge 128 ]; then return 16 + else return 8; fi +} + +label="label $1" +ldev="$1" +dev=${1%:*} +if [ "$dev" = "" -o "$1" = "help" ]; then + echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2 + echo " add - add new address" 1>&2 + echo " del - delete address" 1>&2 + echo " stop - completely disable IP" 1>&2 + exit 1 +fi +shift + +CheckForwarding +fwd=$? +if [ $fwd -ne 0 ]; then + echo "Forwarding is ON or its state is unknown ($fwd). OK, No RDISC." 1>&2 +fi + + +deleting=0 +case "$1" in +add) shift ;; +stop) + if [ "$ldev" != "$dev" ]; then + echo "Cannot stop alias $ldev" 1>&2 + exit 1; + fi + ip -4 addr flush dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 ;; +del*) + deleting=1; shift ;; +*) +esac + +ipaddr= +pfxlen= +if [ "$1" != "" ]; then + ipaddr=${1%/*} + if [ "$1" != "$ipaddr" ]; then + pfxlen=${1#*/} + fi + if [ "$ipaddr" = "" ]; then + echo "$1 is bad IP address." 1>&2 + exit 1 + fi +fi +shift + +peer=$1 +if [ "$peer" != "" ]; then + if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then + echo "Peer address with non-trivial netmask." 1>&2 + exit 1 + fi + pfx="$ipaddr peer $peer" +else + if [ "$pfxlen" = "" ]; then + ABCMaskLen $ipaddr + pfxlen=$? + fi + pfx="$ipaddr/$pfxlen" +fi + +if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then + label= +fi + +if [ $deleting -ne 0 ]; then + ip addr del $pfx dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 +fi + + +if ! ip link set up dev $dev ; then + echo "Error: cannot enable interface $dev." 1>&2 + exit 1 +fi +if [ "$ipaddr" = "" ]; then exit 0; fi + +if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then + echo "Error: some host already uses address $ipaddr on $dev." 1>&2 + exit 1 +fi + +if ! ip address add $pfx brd + dev $dev $label; then + echo "Error: failed to add $pfx on $dev." 1>&2 + exit 1 +fi + +arping -q -A -c 1 -I $dev $ipaddr +noarp=$? +( sleep 2 ; + arping -q -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null & + +ip route add unreachable 224.0.0.0/24 >& /dev/null +ip route add unreachable 255.255.255.255 >& /dev/null +if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then + ip route add 224.0.0.0/4 dev $dev scope global >& /dev/null +fi + +if [ $fwd -eq 0 ]; then + if [ $noarp -eq 0 ]; then + ip ro append default dev $dev metric 30000 scope global + elif [ "$peer" != "" ]; then + if ping -q -c 2 -w 4 $peer ; then + ip ro append default via $peer dev $dev metric 30001 + fi + fi + RestartRDISC +fi + +exit 0 diff --git a/ip/ip b/ip/ip new file mode 100755 index 0000000..5ba7f4e Binary files /dev/null and b/ip/ip differ diff --git a/ip/ip.c b/ip/ip.c new file mode 100644 index 0000000..6358ec4 --- /dev/null +++ b/ip/ip.c @@ -0,0 +1,171 @@ +/* + * ip.c "ip" utility frontend. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> + +#include "SNAPSHOT.h" +#include "utils.h" +#include "ip_common.h" + +int preferred_family = AF_UNSPEC; +int show_stats = 0; +int resolve_hosts = 0; +int oneline = 0; +char * _SL_ = NULL; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" +"where OBJECT := { link | addr | route | rule | neigh | tunnel |\n" +" maddr | mroute | monitor | xfrm }\n" +" OPTIONS := { -V[ersion] | -s[tatistics] | -r[esolve] |\n" +" -f[amily] { inet | inet6 | ipx | dnet | link } | -o[neline] }\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + char *basename; + + basename = strrchr(argv[0], '/'); + if (basename == NULL) + basename = argv[0]; + else + basename++; + + while (argc > 1) { + char *opt = argv[1]; + if (strcmp(opt,"--") == 0) { + argc--; argv++; + break; + } + if (opt[0] != '-') + break; + if (opt[1] == '-') + opt++; + if (matches(opt, "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "inet") == 0) + preferred_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + preferred_family = AF_INET6; + else if (strcmp(argv[1], "dnet") == 0) + preferred_family = AF_DECnet; + else if (strcmp(argv[1], "link") == 0) + preferred_family = AF_PACKET; + else if (strcmp(argv[1], "ipx") == 0) + preferred_family = AF_IPX; + else if (strcmp(argv[1], "help") == 0) + usage(); + else + invarg(argv[1], "invalid protocol family"); + } else if (strcmp(opt, "-4") == 0) { + preferred_family = AF_INET; + } else if (strcmp(opt, "-6") == 0) { + preferred_family = AF_INET6; + } else if (strcmp(opt, "-0") == 0) { + preferred_family = AF_PACKET; + } else if (strcmp(opt, "-I") == 0) { + preferred_family = AF_IPX; + } else if (strcmp(opt, "-D") == 0) { + preferred_family = AF_DECnet; + } else if (matches(opt, "-stats") == 0 || + matches(opt, "-statistics") == 0) { + ++show_stats; + } else if (matches(opt, "-resolve") == 0) { + ++resolve_hosts; + } else if (matches(opt, "-oneline") == 0) { + ++oneline; +#if 0 + } else if (matches(opt, "-numeric") == 0) { + rtnl_names_numeric++; +#endif + } else if (matches(opt, "-Version") == 0) { + printf("ip utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + } else if (matches(opt, "-help") == 0) { + usage(); + } else { + fprintf(stderr, "Option \"%s\" is unknown, try \"ip -help\".\n", opt); + exit(-1); + } + argc--; argv++; + } + + _SL_ = oneline ? "\\" : "\n" ; + + if (strcmp(basename, "ipaddr") == 0) + return do_ipaddr(argc-1, argv+1); + if (strcmp(basename, "ipmaddr") == 0) + return do_multiaddr(argc-1, argv+1); + if (strcmp(basename, "iproute") == 0) + return do_iproute(argc-1, argv+1); + if (strcmp(basename, "iprule") == 0) + return do_iprule(argc-1, argv+1); + if (strcmp(basename, "ipneigh") == 0) + return do_ipneigh(argc-1, argv+1); + if (strcmp(basename, "iplink") == 0) + return do_iplink(argc-1, argv+1); + if (strcmp(basename, "iptunnel") == 0) + return do_iptunnel(argc-1, argv+1); + if (strcmp(basename, "ipmonitor") == 0) + return do_ipmonitor(argc-1, argv+1); + if (strcmp(basename, "ipxfrm") == 0) + return do_xfrm(argc-1, argv+1); + + if (argc > 1) { + if (matches(argv[1], "address") == 0) + return do_ipaddr(argc-2, argv+2); + if (matches(argv[1], "maddress") == 0) + return do_multiaddr(argc-2, argv+2); + if (matches(argv[1], "route") == 0) + return do_iproute(argc-2, argv+2); + if (matches(argv[1], "rule") == 0) + return do_iprule(argc-2, argv+2); + if (matches(argv[1], "mroute") == 0) + return do_multiroute(argc-2, argv+2); + if (matches(argv[1], "neighbor") == 0 || + matches(argv[1], "neighbour") == 0) + return do_ipneigh(argc-2, argv+2); + if (matches(argv[1], "link") == 0) + return do_iplink(argc-2, argv+2); + if (matches(argv[1], "tunnel") == 0 || + strcmp(argv[1], "tunl") == 0) + return do_iptunnel(argc-2, argv+2); + if (matches(argv[1], "monitor") == 0) + return do_ipmonitor(argc-2, argv+2); + if (matches(argv[1], "xfrm") == 0) + return do_xfrm(argc-2, argv+2); + if (matches(argv[1], "help") == 0) + usage(); + fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv[1]); + exit(-1); + } + usage(); +} diff --git a/ip/ip.o b/ip/ip.o new file mode 100644 index 0000000..61cd6c4 Binary files /dev/null and b/ip/ip.o differ diff --git a/ip/ip_common.h b/ip/ip_common.h new file mode 100644 index 0000000..688d384 --- /dev/null +++ b/ip/ip_common.h @@ -0,0 +1,29 @@ +extern int print_linkinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg); +extern int print_addrinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg); +extern int print_neigh(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int ipaddr_list(int argc, char **argv); +extern int ipaddr_list_link(int argc, char **argv); +extern int iproute_monitor(int argc, char **argv); +extern void iplink_usage(void) __attribute__((noreturn)); +extern void iproute_reset_filter(void); +extern void ipaddr_reset_filter(int); +extern void ipneigh_reset_filter(void); +extern int print_route(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int print_prefix(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int do_ipaddr(int argc, char **argv); +extern int do_iproute(int argc, char **argv); +extern int do_iprule(int argc, char **argv); +extern int do_ipneigh(int argc, char **argv); +extern int do_iptunnel(int argc, char **argv); +extern int do_iplink(int argc, char **argv); +extern int do_ipmonitor(int argc, char **argv); +extern int do_multiaddr(int argc, char **argv); +extern int do_multiroute(int argc, char **argv); +extern int do_xfrm(int argc, char **argv); diff --git a/ip/ipaddress.c b/ip/ipaddress.c new file mode 100644 index 0000000..92f0089 --- /dev/null +++ b/ip/ipaddress.c @@ -0,0 +1,904 @@ +/* + * ipaddress.c "ip address". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Changes: + * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <fnmatch.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" +#include "ll_map.h" +#include "ip_common.h" + +static struct +{ + int ifindex; + int family; + int oneline; + int showqueue; + inet_prefix pfx; + int scope, scopemask; + int flags, flagmask; + int up; + char *label; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; +} filter; + +static int do_link; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + if (do_link) { + iplink_usage(); + } + fprintf(stderr, "Usage: ip addr {add|del} IFADDR dev STRING\n"); + fprintf(stderr, " ip addr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n"); + fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ]\n"); + fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n"); + fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n"); + fprintf(stderr, " [ label STRING ] [ scope SCOPE-ID ]\n"); + fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n"); + fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); + fprintf(stderr, "FLAG := [ permanent | dynamic | secondary | primary |\n"); + fprintf(stderr, " tentative | deprecated ]\n"); + exit(-1); +} + +void print_link_flags(FILE *fp, unsigned flags, unsigned mdown) +{ + fprintf(fp, "<"); + if (flags & IFF_UP && !(flags & IFF_RUNNING)) + fprintf(fp, "NO-CARRIER%s", flags ? "," : ""); + flags &= ~IFF_RUNNING; +#define _PF(f) if (flags&IFF_##f) { \ + flags &= ~IFF_##f ; \ + fprintf(fp, #f "%s", flags ? "," : ""); } + _PF(LOOPBACK); + _PF(BROADCAST); + _PF(POINTOPOINT); + _PF(MULTICAST); + _PF(NOARP); + _PF(ALLMULTI); + _PF(PROMISC); + _PF(MASTER); + _PF(SLAVE); + _PF(DEBUG); + _PF(DYNAMIC); + _PF(AUTOMEDIA); + _PF(PORTSEL); + _PF(NOTRAILERS); + _PF(UP); +#undef _PF + if (flags) + fprintf(fp, "%x", flags); + if (mdown) + fprintf(fp, ",M-DOWN"); + fprintf(fp, "> "); +} + +void print_queuelen(char *name) +{ + struct ifreq ifr; + int s; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, name); + if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { + perror("SIOCGIFXQLEN"); + close(s); + return; + } + close(s); + + if (ifr.ifr_qlen) + printf("qlen %d", ifr.ifr_qlen); +} + +int print_linkinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr * tb[IFLA_MAX+1]; + int len = n->nlmsg_len; + unsigned m_flag = 0; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (filter.ifindex && ifi->ifi_index != filter.ifindex) + return 0; + if (filter.up && !(ifi->ifi_flags&IFF_UP)) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (tb[IFLA_IFNAME] == NULL) { + fprintf(stderr, "BUG: nil ifname\n"); + return -1; + } + if (filter.label && + (!filter.family || filter.family == AF_PACKET) && + fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) + return 0; + + if (n->nlmsg_type == RTM_DELLINK) + fprintf(fp, "Deleted "); + + fprintf(fp, "%d: %s", ifi->ifi_index, + tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>"); + + if (tb[IFLA_LINK]) { + SPRINT_BUF(b1); + int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); + if (iflink == 0) + fprintf(fp, "@NONE: "); + else { + fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); + m_flag = ll_index_to_flags(iflink); + m_flag = !(m_flag & IFF_UP); + } + } else { + fprintf(fp, ": "); + } + print_link_flags(fp, ifi->ifi_flags, m_flag); + + if (tb[IFLA_MTU]) + fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); + if (tb[IFLA_QDISC]) + fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC])); +#ifdef IFLA_MASTER + if (tb[IFLA_MASTER]) { + SPRINT_BUF(b1); + fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); + } +#endif + if (filter.showqueue) + print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); + + if (!filter.family || filter.family == AF_PACKET) { + SPRINT_BUF(b1); + fprintf(fp, "%s", _SL_); + fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1))); + + if (tb[IFLA_ADDRESS]) { + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); + } + if (tb[IFLA_BROADCAST]) { + if (ifi->ifi_flags&IFF_POINTOPOINT) + fprintf(fp, " peer "); + else + fprintf(fp, " brd "); + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), + RTA_PAYLOAD(tb[IFLA_BROADCAST]), + ifi->ifi_type, + b1, sizeof(b1))); + } + } + if (do_link && tb[IFLA_STATS] && show_stats) { + struct rtnl_link_stats slocal; + struct rtnl_link_stats *s = RTA_DATA(tb[IFLA_STATS]); + if (((unsigned long)s) & (sizeof(unsigned long)-1)) { + memcpy(&slocal, s, sizeof(slocal)); + s = &slocal; + } + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", + s->rx_compressed ? "compressed" : "", _SL_); + fprintf(fp, " %-10u %-8u %-7u %-7u %-7u %-7u", + s->rx_bytes, s->rx_packets, s->rx_errors, + s->rx_dropped, s->rx_over_errors, + s->multicast + ); + if (s->rx_compressed) + fprintf(fp, " %-7u", s->rx_compressed); + if (show_stats > 1) { + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); + fprintf(fp, " %-7u %-7u %-7u %-7u %-7u", + s->rx_length_errors, + s->rx_crc_errors, + s->rx_frame_errors, + s->rx_fifo_errors, + s->rx_missed_errors + ); + } + fprintf(fp, "%s", _SL_); + fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", + s->tx_compressed ? "compressed" : "", _SL_); + fprintf(fp, " %-10u %-8u %-7u %-7u %-7u %-7u", + s->tx_bytes, s->tx_packets, s->tx_errors, + s->tx_dropped, s->tx_carrier_errors, s->collisions); + if (s->tx_compressed) + fprintf(fp, " %-7u", s->tx_compressed); + if (show_stats > 1) { + fprintf(fp, "%s", _SL_); + fprintf(fp, " TX errors: aborted fifo window heartbeat%s", _SL_); + fprintf(fp, " %-7u %-7u %-7u %-7u", + s->tx_aborted_errors, + s->tx_fifo_errors, + s->tx_window_errors, + s->tx_heartbeat_errors + ); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * rta_tb[IFA_MAX+1]; + char abuf[256]; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) + return 0; + len -= NLMSG_LENGTH(sizeof(*ifa)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWADDR) + return 0; + + parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + + if (!rta_tb[IFA_LOCAL]) + rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; + if (!rta_tb[IFA_ADDRESS]) + rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; + + if (filter.ifindex && filter.ifindex != ifa->ifa_index) + return 0; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + return 0; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + return 0; + if (filter.label) { + SPRINT_BUF(b1); + const char *label; + if (rta_tb[IFA_LABEL]) + label = RTA_DATA(rta_tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + return 0; + } + if (filter.pfx.family) { + if (rta_tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + return 0; + } + } + + if (filter.family && filter.family != ifa->ifa_family) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELADDR; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (n->nlmsg_type == RTM_DELADDR) + fprintf(fp, "Deleted "); + + if (filter.oneline || filter.flushb) + fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); + if (ifa->ifa_family == AF_INET) + fprintf(fp, " inet "); + else if (ifa->ifa_family == AF_INET6) + fprintf(fp, " inet6 "); + else if (ifa->ifa_family == AF_DECnet) + fprintf(fp, " dnet "); + else if (ifa->ifa_family == AF_IPX) + fprintf(fp, " ipx "); + else + fprintf(fp, " family %d ", ifa->ifa_family); + + if (rta_tb[IFA_LOCAL]) { + fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_LOCAL]), + RTA_DATA(rta_tb[IFA_LOCAL]), + abuf, sizeof(abuf))); + + if (rta_tb[IFA_ADDRESS] == NULL || + memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) { + fprintf(fp, "/%d ", ifa->ifa_prefixlen); + } else { + fprintf(fp, " peer %s/%d ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_ADDRESS]), + RTA_DATA(rta_tb[IFA_ADDRESS]), + abuf, sizeof(abuf)), + ifa->ifa_prefixlen); + } + } + + if (rta_tb[IFA_BROADCAST]) { + fprintf(fp, "brd %s ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_BROADCAST]), + RTA_DATA(rta_tb[IFA_BROADCAST]), + abuf, sizeof(abuf))); + } + if (rta_tb[IFA_ANYCAST]) { + fprintf(fp, "any %s ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_ANYCAST]), + RTA_DATA(rta_tb[IFA_ANYCAST]), + abuf, sizeof(abuf))); + } + fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); + if (ifa->ifa_flags&IFA_F_SECONDARY) { + ifa->ifa_flags &= ~IFA_F_SECONDARY; + fprintf(fp, "secondary "); + } + if (ifa->ifa_flags&IFA_F_TENTATIVE) { + ifa->ifa_flags &= ~IFA_F_TENTATIVE; + fprintf(fp, "tentative "); + } + if (ifa->ifa_flags&IFA_F_DEPRECATED) { + ifa->ifa_flags &= ~IFA_F_DEPRECATED; + fprintf(fp, "deprecated "); + } + if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { + fprintf(fp, "dynamic "); + } else + ifa->ifa_flags &= ~IFA_F_PERMANENT; + if (ifa->ifa_flags) + fprintf(fp, "flags %02x ", ifa->ifa_flags); + if (rta_tb[IFA_LABEL]) + fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL])); + if (rta_tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); + char buf[128]; + fprintf(fp, "%s", _SL_); + if (ci->ifa_valid == 0xFFFFFFFFU) + sprintf(buf, "valid_lft forever"); + else + sprintf(buf, "valid_lft %dsec", ci->ifa_valid); + if (ci->ifa_prefered == 0xFFFFFFFFU) + sprintf(buf+strlen(buf), " preferred_lft forever"); + else + sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered); + fprintf(fp, " %s", buf); + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +struct nlmsg_list +{ + struct nlmsg_list *next; + struct nlmsghdr h; +}; + +int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp) +{ + for ( ;ainfo ; ainfo = ainfo->next) { + struct nlmsghdr *n = &ainfo->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (n->nlmsg_type != RTM_NEWADDR) + continue; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) + return -1; + + if (ifa->ifa_index != ifindex || + (filter.family && filter.family != ifa->ifa_family)) + continue; + + print_addrinfo(NULL, n, fp); + } + return 0; +} + + +static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + struct nlmsg_list **linfo = (struct nlmsg_list**)arg; + struct nlmsg_list *h; + struct nlmsg_list **lp; + + h = malloc(n->nlmsg_len+sizeof(void*)); + if (h == NULL) + return -1; + + memcpy(&h->h, n, n->nlmsg_len); + h->next = NULL; + + for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */; + *lp = h; + + ll_remember_index(who, n, NULL); + return 0; +} + +int ipaddr_list_or_flush(int argc, char **argv, int flush) +{ + struct nlmsg_list *linfo = NULL; + struct nlmsg_list *ainfo = NULL; + struct nlmsg_list *l; + struct rtnl_handle rth; + char *filter_dev = NULL; + int no_link = 0; + + ipaddr_reset_filter(oneline); + filter.showqueue = 1; + + if (filter.family == AF_UNSPEC) + filter.family = preferred_family; + + if (flush) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + return -1; + } + if (filter.family == AF_PACKET) { + fprintf(stderr, "Cannot flush link addresses.\n"); + return -1; + } + } + + while (argc > 0) { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + get_prefix(&filter.pfx, *argv, filter.family); + if (filter.family == AF_UNSPEC) + filter.family = filter.pfx.family; + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"scope\"\n", *argv); + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + } else if (strcmp(*argv, "up") == 0) { + filter.up = 1; + } else if (strcmp(*argv, "dynamic") == 0) { + filter.flags &= ~IFA_F_PERMANENT; + filter.flagmask |= IFA_F_PERMANENT; + } else if (strcmp(*argv, "permanent") == 0) { + filter.flags |= IFA_F_PERMANENT; + filter.flagmask |= IFA_F_PERMANENT; + } else if (strcmp(*argv, "secondary") == 0) { + filter.flags |= IFA_F_SECONDARY; + filter.flagmask |= IFA_F_SECONDARY; + } else if (strcmp(*argv, "primary") == 0) { + filter.flags &= ~IFA_F_SECONDARY; + filter.flagmask |= IFA_F_SECONDARY; + } else if (strcmp(*argv, "tentative") == 0) { + filter.flags |= IFA_F_TENTATIVE; + filter.flagmask |= IFA_F_TENTATIVE; + } else if (strcmp(*argv, "deprecated") == 0) { + filter.flags |= IFA_F_DEPRECATED; + filter.flagmask |= IFA_F_DEPRECATED; + } else if (strcmp(*argv, "label") == 0) { + NEXT_ARG(); + filter.label = *argv; + } else { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (filter_dev) + duparg2("dev", *argv); + filter_dev = *argv; + } + argv++; argc--; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + if (filter_dev) { + filter.ifindex = ll_name_to_index(filter_dev); + if (filter.ifindex <= 0) { + fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev); + return -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + if (show_stats) { + printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (filter.family != AF_PACKET) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + + if (filter.family && filter.family != AF_PACKET) { + struct nlmsg_list **lp; + lp=&linfo; + + if (filter.oneline) + no_link = 1; + + while ((l=*lp)!=NULL) { + int ok = 0; + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + struct nlmsg_list *a; + + for (a=ainfo; a; a=a->next) { + struct nlmsghdr *n = &a->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (ifa->ifa_index != ifi->ifi_index || + (filter.family && filter.family != ifa->ifa_family)) + continue; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + continue; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + continue; + if (filter.pfx.family || filter.label) { + struct rtattr *tb[IFA_MAX+1]; + parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); + if (!tb[IFA_LOCAL]) + tb[IFA_LOCAL] = tb[IFA_ADDRESS]; + + if (filter.pfx.family && tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + continue; + } + if (filter.label) { + SPRINT_BUF(b1); + const char *label; + if (tb[IFA_LABEL]) + label = RTA_DATA(tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + continue; + } + } + + ok = 1; + break; + } + if (!ok) + *lp = l->next; + else + lp = &l->next; + } + } + + for (l=linfo; l; l = l->next) { + if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) { + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + if (filter.family != AF_PACKET) + print_selected_addrinfo(ifi->ifi_index, ainfo, stdout); + } + fflush(stdout); + } + + exit(0); +} + +int ipaddr_list_link(int argc, char **argv) +{ + preferred_family = AF_PACKET; + do_link = 1; + return ipaddr_list_or_flush(argc, argv, 0); +} + +void ipaddr_reset_filter(int oneline) +{ + memset(&filter, 0, sizeof(filter)); + filter.oneline = oneline; +} + +int default_scope(inet_prefix *lcl) +{ + if (lcl->family == AF_INET) { + if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127) + return RT_SCOPE_HOST; + } + return 0; +} + +int ipaddr_modify(int cmd, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ifaddrmsg ifa; + char buf[256]; + } req; + char *d = NULL; + char *l = NULL; + inet_prefix lcl; + inet_prefix peer; + int local_len = 0; + int peer_len = 0; + int brd_len = 0; + int any_len = 0; + int scoped = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.ifa.ifa_family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "peer") == 0 || + strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + + if (peer_len) + duparg("peer", *argv); + get_prefix(&peer, *argv, req.ifa.ifa_family); + peer_len = peer.bytelen; + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = peer.family; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); + req.ifa.ifa_prefixlen = peer.bitlen; + } else if (matches(*argv, "broadcast") == 0 || + strcmp(*argv, "brd") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (brd_len) + duparg("broadcast", *argv); + if (strcmp(*argv, "+") == 0) + brd_len = -1; + else if (strcmp(*argv, "-") == 0) + brd_len = -2; + else { + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); + brd_len = addr.bytelen; + } + } else if (strcmp(*argv, "anycast") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (any_len) + duparg("anycast", *argv); + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); + any_len = addr.bytelen; + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg(*argv, "invalid scope value."); + req.ifa.ifa_scope = scope; + scoped = 1; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "label") == 0) { + NEXT_ARG(); + l = *argv; + addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1); + } else { + if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (local_len) + duparg2("local", *argv); + get_prefix(&lcl, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = lcl.family; + addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen); + local_len = lcl.bytelen; + } + argc--; argv++; + } + if (d == NULL) { + fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); + return -1; + } + if (l && matches(d, l) != 0) { + fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l); + exit(1); + } + + if (peer_len == 0 && local_len && cmd != RTM_DELADDR) { + peer = lcl; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); + } + if (req.ifa.ifa_prefixlen == 0) + req.ifa.ifa_prefixlen = lcl.bitlen; + + if (brd_len < 0 && cmd != RTM_DELADDR) { + inet_prefix brd; + int i; + if (req.ifa.ifa_family != AF_INET) { + fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n"); + return -1; + } + brd = peer; + if (brd.bitlen <= 30) { + for (i=31; i>=brd.bitlen; i--) { + if (brd_len == -1) + brd.data[0] |= htonl(1<<(31-i)); + else + brd.data[0] &= ~htonl(1<<(31-i)); + } + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); + brd_len = brd.bytelen; + } + } + if (!scoped && cmd != RTM_DELADDR) + req.ifa.ifa_scope = default_scope(&lcl); + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + exit(0); +} + +int do_ipaddr(int argc, char **argv) +{ + if (argc < 1) + return ipaddr_list_or_flush(0, NULL, 0); + if (matches(*argv, "add") == 0) + return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipaddr_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return ipaddr_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) + return ipaddr_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv); + exit(-1); +} + diff --git a/ip/ipaddress.o b/ip/ipaddress.o new file mode 100644 index 0000000..9d3fa3b Binary files /dev/null and b/ip/ipaddress.o differ diff --git a/ip/iplink.c b/ip/iplink.c new file mode 100644 index 0000000..520280e --- /dev/null +++ b/ip/iplink.c @@ -0,0 +1,429 @@ +/* + * iplink.c "ip link". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include <linux/if.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#include <linux/sockios.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + + +static void usage(void) __attribute__((noreturn)); + +void iplink_usage(void) +{ + fprintf(stderr, "Usage: ip link set DEVICE { up | down |\n"); + fprintf(stderr, " arp { on | off } |\n"); + fprintf(stderr, " dynamic { on | off } |\n"); + fprintf(stderr, " multicast { on | off } |\n"); + fprintf(stderr, " allmulticast { on | off } |\n"); + fprintf(stderr, " promisc { on | off } |\n"); + fprintf(stderr, " trailers { on | off } |\n"); + fprintf(stderr, " txqueuelen PACKETS |\n"); + fprintf(stderr, " name NEWNAME |\n"); + fprintf(stderr, " address LLADDR | broadcast LLADDR |\n"); + fprintf(stderr, " mtu MTU }\n"); + fprintf(stderr, " ip link show [ DEVICE ]\n"); + exit(-1); +} + +static void usage(void) +{ + iplink_usage(); +} + +static int on_off(char *msg) +{ + fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg); + return -1; +} + +static int get_ctl_fd(void) +{ + int s_errno; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + s_errno = errno; + fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + fd = socket(PF_INET6, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + errno = s_errno; + perror("Cannot create control socket"); + return -1; +} + +static int do_chflags(const char *dev, __u32 flags, __u32 mask) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCGIFFLAGS, &ifr); + if (err) { + perror("SIOCGIFFLAGS"); + close(fd); + return -1; + } + if ((ifr.ifr_flags^flags)&mask) { + ifr.ifr_flags &= ~mask; + ifr.ifr_flags |= mask&flags; + err = ioctl(fd, SIOCSIFFLAGS, &ifr); + if (err) + perror("SIOCSIFFLAGS"); + } + close(fd); + return err; +} + +static int do_changename(const char *dev, const char *newdev) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + strncpy(ifr.ifr_newname, newdev, IFNAMSIZ); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCSIFNAME, &ifr); + if (err) { + perror("SIOCSIFNAME"); + close(fd); + return -1; + } + close(fd); + return err; +} + +static int set_qlen(const char *dev, int qlen) +{ + struct ifreq ifr; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + ifr.ifr_qlen = qlen; + if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { + perror("SIOCSIFXQLEN"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int set_mtu(const char *dev, int mtu) +{ + struct ifreq ifr; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + ifr.ifr_mtu = mtu; + if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { + perror("SIOCSIFMTU"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int get_address(const char *dev, int *htype) +{ + struct ifreq ifr; + struct sockaddr_ll me; + int alen; + int s; + + s = socket(PF_PACKET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_PACKET)"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + close(s); + return -1; + } + + memset(&me, 0, sizeof(me)); + me.sll_family = AF_PACKET; + me.sll_ifindex = ifr.ifr_ifindex; + me.sll_protocol = htons(ETH_P_LOOP); + if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { + perror("bind"); + close(s); + return -1; + } + + alen = sizeof(me); + if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { + perror("getsockname"); + close(s); + return -1; + } + close(s); + *htype = me.sll_hatype; + return me.sll_halen; +} + +static int parse_address(const char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) +{ + int alen; + + memset(ifr, 0, sizeof(*ifr)); + strncpy(ifr->ifr_name, dev, IFNAMSIZ); + ifr->ifr_hwaddr.sa_family = hatype; + alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); + if (alen < 0) + return -1; + if (alen != halen) { + fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); + return -1; + } + return 0; +} + +static int set_address(struct ifreq *ifr, int brd) +{ + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { + perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int do_set(int argc, char **argv) +{ + char *dev = NULL; + __u32 mask = 0; + __u32 flags = 0; + int qlen = -1; + int mtu = -1; + char *newaddr = NULL; + char *newbrd = NULL; + struct ifreq ifr0, ifr1; + char *newname = NULL; + int htype, halen; + + while (argc > 0) { + if (strcmp(*argv, "up") == 0) { + mask |= IFF_UP; + flags |= IFF_UP; + } else if (strcmp(*argv, "down") == 0) { + mask |= IFF_UP; + flags &= ~IFF_UP; + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + newname = *argv; + } else if (matches(*argv, "address") == 0) { + NEXT_ARG(); + newaddr = *argv; + } else if (matches(*argv, "broadcast") == 0 || + strcmp(*argv, "brd") == 0) { + NEXT_ARG(); + newbrd = *argv; + } else if (matches(*argv, "txqueuelen") == 0 || + strcmp(*argv, "qlen") == 0 || + matches(*argv, "txqlen") == 0) { + NEXT_ARG(); + if (qlen != -1) + duparg("txqueuelen", *argv); + if (get_integer(&qlen, *argv, 0)) + invarg("Invalid \"txqueuelen\" value\n", *argv); + } else if (strcmp(*argv, "mtu") == 0) { + NEXT_ARG(); + if (mtu != -1) + duparg("mtu", *argv); + if (get_integer(&mtu, *argv, 0)) + invarg("Invalid \"mtu\" value\n", *argv); + } else if (strcmp(*argv, "multicast") == 0) { + NEXT_ARG(); + mask |= IFF_MULTICAST; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_MULTICAST; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_MULTICAST; + } else + return on_off("multicast"); + } else if (strcmp(*argv, "allmulticast") == 0) { + NEXT_ARG(); + mask |= IFF_ALLMULTI; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_ALLMULTI; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_ALLMULTI; + } else + return on_off("allmulticast"); + } else if (strcmp(*argv, "promisc") == 0) { + NEXT_ARG(); + mask |= IFF_PROMISC; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_PROMISC; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_PROMISC; + } else + return on_off("promisc"); + } else if (strcmp(*argv, "trailers") == 0) { + NEXT_ARG(); + mask |= IFF_NOTRAILERS; + if (strcmp(*argv, "off") == 0) { + flags |= IFF_NOTRAILERS; + } else if (strcmp(*argv, "on") == 0) { + flags &= ~IFF_NOTRAILERS; + } else + return on_off("trailers"); + } else if (strcmp(*argv, "arp") == 0) { + NEXT_ARG(); + mask |= IFF_NOARP; + if (strcmp(*argv, "on") == 0) { + flags &= ~IFF_NOARP; + } else if (strcmp(*argv, "off") == 0) { + flags |= IFF_NOARP; + } else + return on_off("noarp"); +#ifdef IFF_DYNAMIC + } else if (matches(*argv, "dynamic") == 0) { + NEXT_ARG(); + mask |= IFF_DYNAMIC; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_DYNAMIC; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_DYNAMIC; + } else + return on_off("dynamic"); +#endif + } else { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (dev) + duparg2("dev", *argv); + dev = *argv; + } + argc--; argv++; + } + + if (!dev) { + fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); + exit(-1); + } + + if (newaddr || newbrd) { + halen = get_address(dev, &htype); + if (halen < 0) + return -1; + if (newaddr) { + if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) + return -1; + } + if (newbrd) { + if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) + return -1; + } + } + + if (newname && strcmp(dev, newname)) { + if (do_changename(dev, newname) < 0) + return -1; + dev = newname; + } + if (qlen != -1) { + if (set_qlen(dev, qlen) < 0) + return -1; + } + if (mtu != -1) { + if (set_mtu(dev, mtu) < 0) + return -1; + } + if (newaddr || newbrd) { + if (newbrd) { + if (set_address(&ifr1, 1) < 0) + return -1; + } + if (newaddr) { + if (set_address(&ifr0, 0) < 0) + return -1; + } + } + if (mask) + return do_chflags(dev, flags, mask); + return 0; +} + +int do_iplink(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "set") == 0) + return do_set(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return ipaddr_list_link(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return ipaddr_list_link(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); + exit(-1); +} diff --git a/ip/iplink.o b/ip/iplink.o new file mode 100644 index 0000000..ac0829c Binary files /dev/null and b/ip/iplink.o differ diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c new file mode 100644 index 0000000..1cdab0b --- /dev/null +++ b/ip/ipmaddr.c @@ -0,0 +1,343 @@ +/* + * ipmaddr.c "ip maddress". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" + +static struct { + char *dev; + int family; +} filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip maddr [ add | del ] MULTIADDR dev STRING\n"); + fprintf(stderr, " ip maddr show [ dev STRING ]\n"); + exit(-1); +} + +static int parse_hex(char *str, unsigned char *addr) +{ + int len=0; + + while (*str) { + int tmp; + if (str[1] == 0) + return -1; + if (sscanf(str, "%02x", &tmp) != 1) + return -1; + addr[len] = tmp; + len++; + str += 2; + } + return len; +} + +struct ma_info +{ + struct ma_info *next; + int index; + int users; + char *features; + char name[IFNAMSIZ]; + inet_prefix addr; +}; + +void maddr_ins(struct ma_info **lst, struct ma_info *m) +{ + struct ma_info *mp; + + for (; (mp=*lst) != NULL; lst = &mp->next) { + if (mp->index > m->index) + break; + } + m->next = *lst; + *lst = m; +} + +void read_dev_mcast(struct ma_info **result_p) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/dev_mcast", "r"); + + if (!fp) + return; + + while (fgets(buf, sizeof(buf), fp)) { + char hexa[256]; + struct ma_info m; + int len; + int st; + + memset(&m, 0, sizeof(m)); + sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st, + hexa); + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + m.addr.family = AF_PACKET; + + len = parse_hex(hexa, (unsigned char*)&m.addr.data); + if (len >= 0) { + struct ma_info *ma = malloc(sizeof(m)); + + memcpy(ma, &m, sizeof(m)); + ma->addr.bytelen = len; + ma->addr.bitlen = len<<3; + if (st) + ma->features = "static"; + maddr_ins(result_p, ma); + } + } + fclose(fp); +} + +void read_igmp(struct ma_info **result_p) +{ + struct ma_info m; + char buf[256]; + FILE *fp = fopen("/proc/net/igmp", "r"); + + if (!fp) + return; + memset(&m, 0, sizeof(m)); + fgets(buf, sizeof(buf), fp); + + m.addr.family = AF_INET; + m.addr.bitlen = 32; + m.addr.bytelen = 4; + + while (fgets(buf, sizeof(buf), fp)) { + struct ma_info *ma = malloc(sizeof(m)); + + if (buf[0] != '\t') { + sscanf(buf, "%d%s", &m.index, m.name); + continue; + } + + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users); + + ma = malloc(sizeof(m)); + memcpy(ma, &m, sizeof(m)); + maddr_ins(result_p, ma); + } + fclose(fp); +} + + +void read_igmp6(struct ma_info **result_p) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/igmp6", "r"); + + if (!fp) + return; + + while (fgets(buf, sizeof(buf), fp)) { + char hexa[256]; + struct ma_info m; + int len; + + memset(&m, 0, sizeof(m)); + sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users); + + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + m.addr.family = AF_INET6; + + len = parse_hex(hexa, (unsigned char*)&m.addr.data); + if (len >= 0) { + struct ma_info *ma = malloc(sizeof(m)); + + memcpy(ma, &m, sizeof(m)); + + ma->addr.bytelen = len; + ma->addr.bitlen = len<<3; + maddr_ins(result_p, ma); + } + } + fclose(fp); +} + +static void print_maddr(FILE *fp, struct ma_info *list) +{ + fprintf(fp, "\t"); + + if (list->addr.family == AF_PACKET) { + SPRINT_BUF(b1); + fprintf(fp, "link %s", ll_addr_n2a((unsigned char*)list->addr.data, + list->addr.bytelen, 0, + b1, sizeof(b1))); + } else { + char abuf[256]; + switch(list->addr.family) { + case AF_INET: + fprintf(fp, "inet "); + break; + case AF_INET6: + fprintf(fp, "inet6 "); + break; + default: + fprintf(fp, "family %d ", list->addr.family); + break; + } + fprintf(fp, "%s", + format_host(list->addr.family, + -1, + list->addr.data, + abuf, sizeof(abuf))); + } + if (list->users != 1) + fprintf(fp, " users %d", list->users); + if (list->features) + fprintf(fp, " %s", list->features); + fprintf(fp, "\n"); +} + +static void print_mlist(FILE *fp, struct ma_info *list) +{ + int cur_index = 0; + + for (; list; list = list->next) { + if (oneline) { + cur_index = list->index; + fprintf(fp, "%d:\t%s%s", cur_index, list->name, _SL_); + } else if (cur_index != list->index) { + cur_index = list->index; + fprintf(fp, "%d:\t%s\n", cur_index, list->name); + } + print_maddr(fp, list); + } +} + +static int multiaddr_list(int argc, char **argv) +{ + struct ma_info *list = NULL; + + if (!filter.family) + filter.family = preferred_family; + + while (argc > 0) { + if (1) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (filter.dev) + duparg2("dev", *argv); + filter.dev = *argv; + } + argv++; argc--; + } + + if (!filter.family || filter.family == AF_PACKET) + read_dev_mcast(&list); + if (!filter.family || filter.family == AF_INET) + read_igmp(&list); + if (!filter.family || filter.family == AF_INET6) + read_igmp6(&list); + print_mlist(stdout, list); + return 0; +} + +int multiaddr_modify(int cmd, int argc, char **argv) +{ + struct ifreq ifr; + int fd; + + memset(&ifr, 0, sizeof(ifr)); + + if (cmd == RTM_NEWADDR) + cmd = SIOCADDMULTI; + else + cmd = SIOCDELMULTI; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (ifr.ifr_name[0]) + duparg("dev", *argv); + strncpy(ifr.ifr_name, *argv, IFNAMSIZ); + } else { + if (matches(*argv, "address") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (ifr.ifr_hwaddr.sa_data[0]) + duparg("address", *argv); + if (ll_addr_a2n(ifr.ifr_hwaddr.sa_data, 14, *argv) < 0) { + fprintf(stderr, "Error: \"%s\" is not a legal ll address.\n", *argv); + exit(1); + } + } + argc--; argv++; + } + if (ifr.ifr_name[0] == 0) { + fprintf(stderr, "Not enough information: \"dev\" is required.\n"); + exit(-1); + } + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot create socket"); + exit(1); + } + if (ioctl(fd, cmd, (char*)&ifr) != 0) { + perror("ioctl"); + exit(1); + } + close(fd); + + exit(0); +} + + +int do_multiaddr(int argc, char **argv) +{ + if (argc < 1) + return multiaddr_list(0, NULL); + if (matches(*argv, "add") == 0) + return multiaddr_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return multiaddr_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return multiaddr_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip maddr help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipmaddr.o b/ip/ipmaddr.o new file mode 100644 index 0000000..fc862e5 Binary files /dev/null and b/ip/ipmaddr.o differ diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c new file mode 100644 index 0000000..cdaeb6f --- /dev/null +++ b/ip/ipmonitor.c @@ -0,0 +1,165 @@ +/* + * ipmonitor.c "ip monitor". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <time.h> + +#include "utils.h" +#include "ip_common.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ]\n"); + exit(-1); +} + + +int accept_msg(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + + if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) { + print_route(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) { + ll_remember_index(who, n, NULL); + print_linkinfo(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { + print_addrinfo(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWNEIGH || n->nlmsg_type == RTM_DELNEIGH) { + print_neigh(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWPREFIX) { + print_prefix(who, n, arg); + return 0; + } + if (n->nlmsg_type == 15) { + char *tstr; + time_t secs = ((__u32*)NLMSG_DATA(n))[0]; + long usecs = ((__u32*)NLMSG_DATA(n))[1]; + tstr = asctime(localtime(&secs)); + tstr[strlen(tstr)-1] = 0; + fprintf(fp, "Timestamp: %s %lu us\n", tstr, usecs); + return 0; + } + if (n->nlmsg_type == RTM_NEWQDISC || + n->nlmsg_type == RTM_DELQDISC || + n->nlmsg_type == RTM_NEWTCLASS || + n->nlmsg_type == RTM_DELTCLASS || + n->nlmsg_type == RTM_NEWTFILTER || + n->nlmsg_type == RTM_DELTFILTER) + return 0; + if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP && + n->nlmsg_type != NLMSG_DONE) { + fprintf(fp, "Unknown message: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + } + return 0; +} + +int do_ipmonitor(int argc, char **argv) +{ + struct rtnl_handle rth; + char *file = NULL; + unsigned groups = ~RTMGRP_TC; + int llink=0; + int laddr=0; + int lroute=0; + int lprefix=0; + + ipaddr_reset_filter(1); + iproute_reset_filter(); + ipneigh_reset_filter(); + + while (argc > 0) { + if (matches(*argv, "file") == 0) { + NEXT_ARG(); + file = *argv; + } else if (matches(*argv, "link") == 0) { + llink=1; + groups = 0; + } else if (matches(*argv, "address") == 0) { + laddr=1; + groups = 0; + } else if (matches(*argv, "route") == 0) { + lroute=1; + groups = 0; + } else if (matches(*argv, "prefix") == 0) { + lprefix=1; + groups = 0; + } else if (strcmp(*argv, "all") == 0) { + groups = ~RTMGRP_TC; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"ip monitor help\".\n", *argv); + exit(-1); + } + argc--; argv++; + } + + if (llink) + groups |= RTMGRP_LINK; + if (laddr) { + if (!preferred_family || preferred_family == AF_INET) + groups |= RTMGRP_IPV4_IFADDR; + if (!preferred_family || preferred_family == AF_INET6) + groups |= RTMGRP_IPV6_IFADDR; + } + if (lroute) { + if (!preferred_family || preferred_family == AF_INET) + groups |= RTMGRP_IPV4_ROUTE; + if (!preferred_family || preferred_family == AF_INET6) + groups |= RTMGRP_IPV6_ROUTE; + } + if (lprefix) { + if (!preferred_family || preferred_family == AF_INET6) + groups |= RTMGRP_IPV6_PREFIX; + } + + if (file) { + FILE *fp; + fp = fopen(file, "r"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + return rtnl_from_file(fp, accept_msg, (void*)stdout); + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + ll_init_map(&rth); + + if (rtnl_listen(&rth, accept_msg, (void*)stdout) < 0) + exit(2); + + exit(0); +} diff --git a/ip/ipmonitor.o b/ip/ipmonitor.o new file mode 100644 index 0000000..9783bef Binary files /dev/null and b/ip/ipmonitor.o differ diff --git a/ip/ipmroute.c b/ip/ipmroute.c new file mode 100644 index 0000000..b24caee --- /dev/null +++ b/ip/ipmroute.c @@ -0,0 +1,205 @@ +/* + * ipmroute.c "ip mroute". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "utils.h" + +char filter_dev[16]; +int filter_family; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip mroute show [ PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n"); +#if 0 + fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n"); +#endif + exit(-1); +} + +char *viftable[32]; + +struct rtfilter +{ + inet_prefix mdst; + inet_prefix msrc; +} filter; + +void read_viftable(void) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/ip_mr_vif", "r"); + + if (!fp) + return; + + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp)) { + int vifi; + char dev[256]; + + if (sscanf(buf, "%d%s", &vifi, dev) < 2) + continue; + + if (vifi<0 || vifi>31) + continue; + + viftable[vifi] = strdup(dev); + } + fclose(fp); +} + +void read_mroute_list(FILE *ofp) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/ip_mr_cache", "r"); + + if (!fp) + return; + + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp)) { + inet_prefix maddr, msrc; + unsigned pkts, b, w; + int vifi; + char oiflist[256]; + char sbuf[256]; + char mbuf[256]; + char obuf[256]; + + oiflist[0] = 0; + if (sscanf(buf, "%x%x%d%u%u%u%s", maddr.data, msrc.data, &vifi, + &pkts, &b, &w, oiflist) < 6) + continue; + + if (vifi!=-1 && (vifi < 0 || vifi>31)) + continue; + + if (filter_dev[0] && (vifi<0 || strcmp(filter_dev, viftable[vifi]))) + continue; + if (filter.mdst.family && inet_addr_match(&maddr, &filter.mdst, filter.mdst.bitlen)) + continue; + if (filter.msrc.family && inet_addr_match(&msrc, &filter.msrc, filter.msrc.bitlen)) + continue; + + snprintf(obuf, sizeof(obuf), "(%s, %s)", + format_host(AF_INET, 4, &msrc.data[0], sbuf, sizeof(sbuf)), + format_host(AF_INET, 4, &maddr.data[0], mbuf, sizeof(mbuf))); + + fprintf(ofp, "%-32s Iif: ", obuf); + + if (vifi == -1) + fprintf(ofp, "unresolved "); + else + fprintf(ofp, "%-10s ", viftable[vifi]); + + if (oiflist[0]) { + char *next = NULL; + char *p = oiflist; + int ovifi, ottl; + + fprintf(ofp, "Oifs: "); + + while (p) { + next = strchr(p, ' '); + if (next) { + *next = 0; + next++; + } + if (sscanf(p, "%d:%d", &ovifi, &ottl)<2) { + p = next; + continue; + } + p = next; + + fprintf(ofp, "%s", viftable[ovifi]); + if (ottl>1) + fprintf(ofp, "(ttl %d) ", ovifi); + else + fprintf(ofp, " "); + } + } + + if (show_stats && b) { + fprintf(ofp, "%s %u packets, %u bytes", _SL_, pkts, b); + if (w) + fprintf(ofp, ", %u arrived on wrong iif.", w); + } + fprintf(ofp, "\n"); + } + fclose(fp); +} + + +static int mroute_list(int argc, char **argv) +{ + while (argc > 0) { + if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + strncpy(filter_dev, *argv, sizeof(filter_dev)-1); + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, AF_INET); + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&filter.mdst, *argv, AF_INET); + } + argv++; argc--; + } + + read_viftable(); + read_mroute_list(stdout); + return 0; +} + +int do_multiroute(int argc, char **argv) +{ + if (argc < 1) + return mroute_list(0, NULL); +#if 0 + if (matches(*argv, "add") == 0) + return mroute_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return mroute_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "get") == 0) + return mroute_get(argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return mroute_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipmroute.o b/ip/ipmroute.o new file mode 100644 index 0000000..3783d57 Binary files /dev/null and b/ip/ipmroute.o differ diff --git a/ip/ipneigh.c b/ip/ipneigh.c new file mode 100644 index 0000000..e8ab291 --- /dev/null +++ b/ip/ipneigh.c @@ -0,0 +1,475 @@ +/* + * ipneigh.c "ip neigh". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <sys/time.h> +#include <net/if.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) + +static struct +{ + int family; + int index; + int state; + int unused_only; + inet_prefix pfx; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; +} filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n" + " [ nud { permanent | noarp | stale | reachable } ]\n" + " | proxy ADDR } [ dev DEV ]\n"); + fprintf(stderr, " ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"); + exit(-1); +} + +int nud_state_a2n(unsigned *state, char *arg) +{ + if (matches(arg, "permanent") == 0) + *state = NUD_PERMANENT; + else if (matches(arg, "reachable") == 0) + *state = NUD_REACHABLE; + else if (strcmp(arg, "noarp") == 0) + *state = NUD_NOARP; + else if (strcmp(arg, "none") == 0) + *state = NUD_NONE; + else if (strcmp(arg, "stale") == 0) + *state = NUD_STALE; + else if (strcmp(arg, "incomplete") == 0) + *state = NUD_INCOMPLETE; + else if (strcmp(arg, "delay") == 0) + *state = NUD_DELAY; + else if (strcmp(arg, "probe") == 0) + *state = NUD_PROBE; + else if (matches(arg, "failed") == 0) + *state = NUD_FAILED; + else { + if (get_unsigned(state, arg, 0)) + return -1; + if (*state>=0x100 || (*state&((*state)-1))) + return -1; + } + return 0; +} + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + + +static int ipneigh_modify(int cmd, int flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + char *d = NULL; + int dst_ok = 0; + int lladdr_ok = 0; + char * lla = NULL; + inet_prefix dst; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.ndm.ndm_family = preferred_family; + req.ndm.ndm_state = NUD_PERMANENT; + + while (argc > 0) { + if (matches(*argv, "lladdr") == 0) { + NEXT_ARG(); + if (lladdr_ok) + duparg("lladdr", *argv); + lla = *argv; + lladdr_ok = 1; + } else if (strcmp(*argv, "nud") == 0) { + unsigned state; + NEXT_ARG(); + if (nud_state_a2n(&state, *argv)) + invarg("nud state is bad", *argv); + req.ndm.ndm_state = state; + } else if (matches(*argv, "proxy") == 0) { + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg("address", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + req.ndm.ndm_flags |= NTF_PROXY; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) { + NEXT_ARG(); + } + if (dst_ok) + duparg2("to", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + } + argc--; argv++; + } + if (d == NULL || !dst_ok || dst.family == AF_UNSPEC) { + fprintf(stderr, "Device and destination are required arguments.\n"); + exit(-1); + } + req.ndm.ndm_family = dst.family; + addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen); + + if (lla && strcmp(lla, "null")) { + __u8 llabuf[16]; + int l; + + l = ll_addr_a2n(llabuf, sizeof(llabuf), lla); + addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if ((req.ndm.ndm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + exit(0); +} + + +int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ndmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[NDA_MAX+1]; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) { + fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + + return 0; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH) + return 0; + + if (filter.family && filter.family != r->ndm_family) + return 0; + if (filter.index && filter.index != r->ndm_ifindex) + return 0; + if (!(filter.state&r->ndm_state) && + (r->ndm_state || !(filter.state&0x100)) && + (r->ndm_family != AF_DECnet)) + return 0; + + parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (tb[NDA_DST]) { + if (filter.pfx.family) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = r->ndm_family; + memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + return 0; + } + } + if (filter.unused_only && tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + if (ci->ndm_refcnt) + return 0; + } + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELNEIGH; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (tb[NDA_DST]) { + fprintf(fp, "%s ", + format_host(r->ndm_family, + RTA_PAYLOAD(tb[NDA_DST]), + RTA_DATA(tb[NDA_DST]), + abuf, sizeof(abuf))); + } + if (!filter.index && r->ndm_ifindex) + fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); + if (tb[NDA_LLADDR]) { + SPRINT_BUF(b1); + fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), + RTA_PAYLOAD(tb[NDA_LLADDR]), + ll_index_to_type(r->ndm_ifindex), + b1, sizeof(b1))); + } + if (r->ndm_flags & NTF_ROUTER) { + fprintf(fp, " router"); + } + if (tb[NDA_CACHEINFO] && show_stats) { + static int hz; + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + if (!hz) + hz = get_hz(); + if (ci->ndm_refcnt) + printf(" ref %d", ci->ndm_refcnt); + fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz, + ci->ndm_confirmed/hz, ci->ndm_updated/hz); + } + +#ifdef NDA_PROBES + if (tb[NDA_PROBES] && show_stats) { + __u32 p = *(__u32 *) RTA_DATA(tb[NDA_PROBES]); + fprintf(fp, " probes %u", p); + } +#endif + + if (r->ndm_state) { + int nud = r->ndm_state; + fprintf(fp, " "); + +#define PRINT_FLAG(f) if (nud & NUD_##f) { \ + nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); } + PRINT_FLAG(INCOMPLETE); + PRINT_FLAG(REACHABLE); + PRINT_FLAG(STALE); + PRINT_FLAG(DELAY); + PRINT_FLAG(PROBE); + PRINT_FLAG(FAILED); + PRINT_FLAG(NOARP); + PRINT_FLAG(PERMANENT); +#undef PRINT_FLAG + } + fprintf(fp, "\n"); + + fflush(fp); + return 0; +} + +void ipneigh_reset_filter() +{ + memset(&filter, 0, sizeof(filter)); + filter.state = ~0; +} + +int do_show_or_flush(int argc, char **argv, int flush) +{ + char *filter_dev = NULL; + struct rtnl_handle rth; + int state_given = 0; + + ipneigh_reset_filter(); + + if (!filter.family) + filter.family = preferred_family; + + if (flush) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + return -1; + } + filter.state = ~(NUD_PERMANENT|NUD_NOARP); + } else + filter.state = 0xFF & ~NUD_NOARP; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg("dev", *argv); + filter_dev = *argv; + } else if (strcmp(*argv, "unused") == 0) { + filter.unused_only = 1; + } else if (strcmp(*argv, "nud") == 0) { + unsigned state; + NEXT_ARG(); + if (!state_given) { + state_given = 1; + filter.state = 0; + } + if (nud_state_a2n(&state, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("nud state is bad", *argv); + state = ~0; + if (flush) + state &= ~NUD_NOARP; + } + if (state == 0) + state = 0x100; + filter.state |= state; + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&filter.pfx, *argv, filter.family); + if (filter.family == AF_UNSPEC) + filter.family = filter.pfx.family; + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (filter_dev) { + if ((filter.index = ll_name_to_index(filter_dev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev); + return -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + filter.state &= ~NUD_FAILED; + + for (;;) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + return 0; +} + +int do_ipneigh(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); + if (matches(*argv, "change") == 0 || + strcmp(*argv, "chg") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1); + if (matches(*argv, "get") == 0) { + fprintf(stderr, "Sorry, \"neigh get\" is not implemented :-(\n"); + return -1; + } + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) + return do_show_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show_or_flush(0, NULL, 0); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipneigh.o b/ip/ipneigh.o new file mode 100644 index 0000000..876eab6 Binary files /dev/null and b/ip/ipneigh.o differ diff --git a/ip/ipprefix.c b/ip/ipprefix.c new file mode 100644 index 0000000..61d12f9 --- /dev/null +++ b/ip/ipprefix.c @@ -0,0 +1,106 @@ +/* + * Copyright (C)2005 USAGI/WIDE Project + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/icmp6.h> +#include "utils.h" + +/* prefix flags; see kernel's net/ipv6/addrconf.c and include/net/if_inet6.h */ +#define IF_PREFIX_ONLINK 0x01 +#define IF_PREFIX_AUTOCONF 0x02 + +int print_prefix(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct prefixmsg *prefix = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + int family = preferred_family; + + if (n->nlmsg_type != RTM_NEWPREFIX) { + fprintf(stderr, "Not a prefix: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*prefix)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (family == AF_UNSPEC) + family = AF_INET6; + if (family != AF_INET6) + return 0; + + if (prefix->prefix_family != AF_INET6) { + fprintf(stderr, "wrong family %d\n", prefix->prefix_family); + return 0; + } + if (prefix->prefix_type != ND_OPT_PREFIX_INFORMATION) { + fprintf(stderr, "wrong ND type %d\n", prefix->prefix_type); + return 0; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(prefix), len); + + fprintf(fp, "prefix "); + + if (tb[PREFIX_ADDRESS]) { + struct in6_addr *pfx; + char abuf[256]; + + pfx = (struct in6_addr *)RTA_DATA(tb[PREFIX_ADDRESS]); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*pfx), pfx, + abuf, sizeof(abuf))); + } + fprintf(fp, "/%u ", prefix->prefix_len); + + fprintf(fp, "dev %s ", ll_index_to_name(prefix->prefix_ifindex)); + + if (prefix->prefix_flags & IF_PREFIX_ONLINK) + fprintf(fp, "onlink "); + if (prefix->prefix_flags & IF_PREFIX_AUTOCONF) + fprintf(fp, "autoconf "); + + if (tb[PREFIX_CACHEINFO]) { + struct prefix_cacheinfo *pc; + pc = (struct prefix_cacheinfo *)tb[PREFIX_CACHEINFO]; + + fprintf(fp, "valid %u ", pc->valid_time); + fprintf(fp, "preferred %u ", pc->preferred_time); + } + + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + diff --git a/ip/ipprefix.o b/ip/ipprefix.o new file mode 100644 index 0000000..5186e99 Binary files /dev/null and b/ip/ipprefix.o differ diff --git a/ip/iproute.c b/ip/iproute.c new file mode 100644 index 0000000..1e23e49 --- /dev/null +++ b/ip/iproute.c @@ -0,0 +1,1416 @@ +/* + * iproute.c "ip route". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <linux/in_route.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip route { list | flush } SELECTOR\n"); + fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n"); + fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n"); + fprintf(stderr, " ip route { add | del | change | append | replace | monitor } ROUTE\n"); + fprintf(stderr, "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ type TYPE ] [ scope SCOPE ]\n"); + fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n"); + fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ scope SCOPE ] [ metric METRIC ]\n"); + fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"); + fprintf(stderr, "NH := [ via ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); + fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ]\n"); + fprintf(stderr, " [ rtt NUMBER ] [ rttvar NUMBER ]\n"); + fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ ssthresh NUMBER ]\n"); + fprintf(stderr, " [ realms REALM ]\n"); + fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n"); + fprintf(stderr, " unreachable | prohibit | blackhole | nat ]\n"); + fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); + fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n"); + fprintf(stderr, "FLAGS := [ equalize ]\n"); + fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n"); + fprintf(stderr, "RTPROTO := [ kernel | boot | static | NUMBER ]\n"); + exit(-1); +} + + +static struct +{ + int tb; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; + int protocol, protocolmask; + int scope, scopemask; + int type, typemask; + int tos, tosmask; + int iif, iifmask; + int oif, oifmask; + int realm, realmmask; + inet_prefix rprefsrc; + inet_prefix rvia; + inet_prefix rdst; + inet_prefix mdst; + inet_prefix rsrc; + inet_prefix msrc; +} filter; + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + inet_prefix dst; + inet_prefix src; + inet_prefix prefsrc; + inet_prefix via; + int host_len = -1; + SPRINT_BUF(b1); + + + if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { + fprintf(stderr, "Not a route: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) + return 0; + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_INET) + host_len = 32; + else if (r->rtm_family == AF_DECnet) + host_len = 16; + else if (r->rtm_family == AF_IPX) + host_len = 80; + + if (r->rtm_family == AF_INET6) { + if (filter.tb) { + if (filter.tb < 0) { + if (!(r->rtm_flags&RTM_F_CLONED)) + return 0; + } else { + if (r->rtm_flags&RTM_F_CLONED) + return 0; + if (filter.tb == RT_TABLE_LOCAL) { + if (r->rtm_type != RTN_LOCAL) + return 0; + } else if (filter.tb == RT_TABLE_MAIN) { + if (r->rtm_type == RTN_LOCAL) + return 0; + } else { + return 0; + } + } + } + } else { + if (filter.tb > 0 && filter.tb != r->rtm_table) + return 0; + } + if ((filter.protocol^r->rtm_protocol)&filter.protocolmask) + return 0; + if ((filter.scope^r->rtm_scope)&filter.scopemask) + return 0; + if ((filter.type^r->rtm_type)&filter.typemask) + return 0; + if ((filter.tos^r->rtm_tos)&filter.tosmask) + return 0; + if (filter.rdst.family && + (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) + return 0; + if (filter.mdst.family && + (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) + return 0; + if (filter.rsrc.family && + (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) + return 0; + if (filter.msrc.family && + (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) + return 0; + if (filter.rvia.family && r->rtm_family != filter.rvia.family) + return 0; + if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) + return 0; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + memset(&dst, 0, sizeof(dst)); + dst.family = r->rtm_family; + if (tb[RTA_DST]) + memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); + if (filter.rsrc.family || filter.msrc.family) { + memset(&src, 0, sizeof(src)); + src.family = r->rtm_family; + if (tb[RTA_SRC]) + memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); + } + if (filter.rvia.bitlen>0) { + memset(&via, 0, sizeof(via)); + via.family = r->rtm_family; + if (tb[RTA_GATEWAY]) + memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len); + } + if (filter.rprefsrc.bitlen>0) { + memset(&prefsrc, 0, sizeof(prefsrc)); + prefsrc.family = r->rtm_family; + if (tb[RTA_PREFSRC]) + memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len); + } + + if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + return 0; + if (filter.mdst.family && filter.mdst.bitlen >= 0 && + inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) + return 0; + + if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + return 0; + if (filter.msrc.family && filter.msrc.bitlen >= 0 && + inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) + return 0; + + if (filter.rvia.family && inet_addr_match(&via, &filter.rvia, filter.rvia.bitlen)) + return 0; + if (filter.rprefsrc.family && inet_addr_match(&prefsrc, &filter.rprefsrc, filter.rprefsrc.bitlen)) + return 0; + if (filter.realmmask) { + __u32 realms = 0; + if (tb[RTA_FLOW]) + realms = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + if ((realms^filter.realm)&filter.realmmask) + return 0; + } + if (filter.iifmask) { + int iif = 0; + if (tb[RTA_IIF]) + iif = *(int*)RTA_DATA(tb[RTA_IIF]); + if ((iif^filter.iif)&filter.iifmask) + return 0; + } + if (filter.oifmask) { + int oif = 0; + if (tb[RTA_OIF]) + oif = *(int*)RTA_DATA(tb[RTA_OIF]); + if ((oif^filter.oif)&filter.oifmask) + return 0; + } + if (filter.flushb && + r->rtm_family == AF_INET6 && + r->rtm_dst_len == 0 && + r->rtm_type == RTN_UNREACHABLE && + tb[RTA_PRIORITY] && + *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELROUTE; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (n->nlmsg_type == RTM_DELROUTE) + fprintf(fp, "Deleted "); + if (r->rtm_type != RTN_UNICAST && !filter.type) + fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "%s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "0/%d ", r->rtm_dst_len); + } else { + fprintf(fp, "default "); + } + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "from %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "from 0/%u ", r->rtm_src_len); + } + if (r->rtm_tos && filter.tosmask != -1) { + SPRINT_BUF(b1); + fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { + fprintf(fp, "via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + if (tb[RTA_OIF] && filter.oifmask != -1) + fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); + + if (!(r->rtm_flags&RTM_F_CLONED)) { + if (r->rtm_table != RT_TABLE_MAIN && !filter.tb) + fprintf(fp, " table %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); + if (r->rtm_protocol != RTPROT_BOOT && filter.protocolmask != -1) + fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); + if (r->rtm_scope != RT_SCOPE_UNIVERSE && filter.scopemask != -1) + fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); + } + if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { + /* Do not use format_host(). It is our local addr + and symbolic name will not be useful. + */ + fprintf(fp, " src %s ", + rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_PREFSRC]), + RTA_DATA(tb[RTA_PREFSRC]), + abuf, sizeof(abuf))); + } + if (tb[RTA_PRIORITY]) + fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); + if (r->rtm_flags & RTNH_F_DEAD) + fprintf(fp, "dead "); + if (r->rtm_flags & RTNH_F_ONLINK) + fprintf(fp, "onlink "); + if (r->rtm_flags & RTNH_F_PERVASIVE) + fprintf(fp, "pervasive "); + if (r->rtm_flags & RTM_F_EQUALIZE) + fprintf(fp, "equalize "); + if (r->rtm_flags & RTM_F_NOTIFY) + fprintf(fp, "notify "); + + if (tb[RTA_FLOW] && filter.realmmask != ~0U) { + __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + __u32 from = to>>16; + to &= 0xFFFF; + fprintf(fp, "realm%s ", from ? "s" : ""); + if (from) { + fprintf(fp, "%s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + } + fprintf(fp, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + if ((r->rtm_flags&RTM_F_CLONED) && r->rtm_family == AF_INET) { + __u32 flags = r->rtm_flags&~0xFFFF; + int first = 1; + + fprintf(fp, "%s cache ", _SL_); + +#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \ + flags &= ~RTCF_##fl; \ + fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \ + first = 0; } + PRTFL(LOCAL, "local"); + PRTFL(REJECT, "reject"); + PRTFL(MULTICAST, "mc"); + PRTFL(BROADCAST, "brd"); + PRTFL(DNAT, "dst-nat"); + PRTFL(SNAT, "src-nat"); + PRTFL(MASQ, "masq"); + PRTFL(DIRECTDST, "dst-direct"); + PRTFL(DIRECTSRC, "src-direct"); + PRTFL(REDIRECTED, "redirected"); + PRTFL(DOREDIRECT, "redirect"); + PRTFL(FAST, "fastroute"); + PRTFL(NOTIFY, "notify"); + PRTFL(TPROXY, "proxy"); +#ifdef RTCF_EQUALIZE + PRTFL(EQUALIZE, "equalize"); +#endif + if (flags) + fprintf(fp, "%s%x> ", first ? "<" : "", flags); + if (tb[RTA_CACHEINFO]) { + struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + static int hz; + if (!hz) + hz = get_user_hz(); + if (ci->rta_expires != 0) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + if (ci->rta_lastuse != 0) + fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + } +#ifdef RTNETLINK_HAVE_PEERINFO + if (ci->rta_id) + fprintf(fp, " ipid 0x%04x", ci->rta_id); + if (ci->rta_ts || ci->rta_tsage) + fprintf(fp, " ts 0x%x tsage %dsec", ci->rta_ts, ci->rta_tsage); +#endif + } + } else if (r->rtm_family == AF_INET6) { + struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) + ci = RTA_DATA(tb[RTA_CACHEINFO]); + if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { + static int hz; + if (!hz) + hz = get_user_hz(); + if (r->rtm_flags & RTM_F_CLONED) + fprintf(fp, "%s cache ", _SL_); + if (ci->rta_expires) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + if (ci->rta_lastuse != 0) + fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + } + } else if (ci) { + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + } + } + if (tb[RTA_METRICS]) { + int i; + unsigned mxlock = 0; + struct rtattr *mxrta[RTAX_MAX+1]; + + parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), + RTA_PAYLOAD(tb[RTA_METRICS])); + if (mxrta[RTAX_LOCK]) + mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]); + + for (i=2; i<=RTAX_MAX; i++) { + static char *mx_names[] = + { + "mtu", + "window", + "rtt", + "rttvar", + "ssthresh", + "cwnd", + "advmss", + "reordering", + }; + static int hz; + if (mxrta[i] == NULL) + continue; + if (!hz) + hz = get_hz(); + if (i-2 < sizeof(mx_names)/sizeof(char*)) + fprintf(fp, " %s", mx_names[i-2]); + else + fprintf(fp, " metric %d", i); + if (mxlock & (1<<i)) + fprintf(fp, " lock"); + + if (i != RTAX_RTT && i != RTAX_RTTVAR) + fprintf(fp, " %u", *(unsigned*)RTA_DATA(mxrta[i])); + else { + unsigned val = *(unsigned*)RTA_DATA(mxrta[i]); + + val *= 1000; + if (i == RTAX_RTT) + val /= 8; + else + val /= 4; + if (val >= hz) + fprintf(fp, " %ums", val/hz); + else + fprintf(fp, " %.2fms", (float)val/hz); + } + } + } + if (tb[RTA_IIF] && filter.iifmask != -1) { + fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); + } + if (tb[RTA_MULTIPATH]) { + struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); + int first = 0; + + len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); + + for (;;) { + if (len < sizeof(*nh)) + break; + if (nh->rtnh_len > len) + break; + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + if (first) + fprintf(fp, " Oifs:"); + else + fprintf(fp, " "); + } else + fprintf(fp, "%s\tnexthop", _SL_); + if (nh->rtnh_len > sizeof(*nh)) { + parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh)); + if (tb[RTA_GATEWAY]) { + fprintf(fp, " via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + } + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + fprintf(fp, " %s", ll_index_to_name(nh->rtnh_ifindex)); + if (nh->rtnh_hops != 1) + fprintf(fp, "(ttl>%d)", nh->rtnh_hops); + } else { + fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex)); + fprintf(fp, " weight %d", nh->rtnh_hops+1); + } + if (nh->rtnh_flags & RTNH_F_DEAD) + fprintf(fp, " dead"); + if (nh->rtnh_flags & RTNH_F_ONLINK) + fprintf(fp, " onlink"); + if (nh->rtnh_flags & RTNH_F_PERVASIVE) + fprintf(fp, " pervasive"); + len -= NLMSG_ALIGN(nh->rtnh_len); + nh = RTNH_NEXT(nh); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +int parse_one_nh(struct rtattr *rta, struct rtnexthop *rtnh, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (++argv, --argc > 0) { + if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + rta_addattr32(rta, 4096, RTA_GATEWAY, get_addr32(*argv)); + rtnh->rtnh_len += sizeof(struct rtattr) + 4; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if ((rtnh->rtnh_ifindex = ll_name_to_index(*argv)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", *argv); + exit(1); + } + } else if (strcmp(*argv, "weight") == 0) { + unsigned w; + NEXT_ARG(); + if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256) + invarg("\"weight\" is invalid\n", *argv); + rtnh->rtnh_hops = w - 1; + } else if (strcmp(*argv, "onlink") == 0) { + rtnh->rtnh_flags |= RTNH_F_ONLINK; + } else + break; + } + *argcp = argc; + *argvp = argv; + return 0; +} + +int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char **argv) +{ + char buf[1024]; + struct rtattr *rta = (void*)buf; + struct rtnexthop *rtnh; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH(0); + rtnh = RTA_DATA(rta); + + while (argc > 0) { + if (strcmp(*argv, "nexthop") != 0) { + fprintf(stderr, "Error: \"nexthop\" or end of line is expected instead of \"%s\"\n", *argv); + exit(-1); + } + if (argc <= 1) { + fprintf(stderr, "Error: unexpected end of line after \"nexthop\"\n"); + exit(-1); + } + memset(rtnh, 0, sizeof(*rtnh)); + rtnh->rtnh_len = sizeof(*rtnh); + rtnh->rtnh_ifindex = 0; + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = 0; + rta->rta_len += rtnh->rtnh_len; + parse_one_nh(rta, rtnh, &argc, &argv); + rtnh = RTNH_NEXT(rtnh); + } + + if (rta->rta_len > RTA_LENGTH(0)) + addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); + return 0; +} + + +int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char mxbuf[256]; + struct rtattr * mxrta = (void*)mxbuf; + unsigned mxlock = 0; + char *d = NULL; + int gw_ok = 0; + int dst_ok = 0; + int nhs_ok = 0; + int scope_ok = 0; + int table_ok = 0; + int proto_ok = 0; + int type_ok = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.r.rtm_family = preferred_family; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_scope = RT_SCOPE_NOWHERE; + + if (cmd != RTM_DELROUTE) { + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + } + + mxrta->rta_type = RTA_METRICS; + mxrta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + gw_ok = 1; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("\"tos\" value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "metric") == 0 || + matches(*argv, "priority") == 0 || + matches(*argv, "preference") == 0) { + __u32 metric; + NEXT_ARG(); + if (get_u32(&metric, *argv, 0)) + invarg("\"metric\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg("invalid \"scope\" value\n", *argv); + req.r.rtm_scope = scope; + scope_ok = 1; + } else if (strcmp(*argv, "mtu") == 0) { + unsigned mtu; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_MTU); + NEXT_ARG(); + } + if (get_unsigned(&mtu, *argv, 0)) + invarg("\"mtu\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); +#ifdef RTAX_ADVMSS + } else if (strcmp(*argv, "advmss") == 0) { + unsigned mss; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_ADVMSS); + NEXT_ARG(); + } + if (get_unsigned(&mss, *argv, 0)) + invarg("\"mss\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss); +#endif +#ifdef RTAX_REORDERING + } else if (matches(*argv, "reordering") == 0) { + unsigned reord; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_REORDERING); + NEXT_ARG(); + } + if (get_unsigned(&reord, *argv, 0)) + invarg("\"reordering\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord); +#endif + } else if (strcmp(*argv, "rtt") == 0) { + unsigned rtt; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTT); + NEXT_ARG(); + } + if (get_unsigned(&rtt, *argv, 0)) + invarg("\"rtt\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, rtt); + } else if (matches(*argv, "window") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_WINDOW); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"window\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win); + } else if (matches(*argv, "cwnd") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_CWND); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"cwnd\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win); + } else if (matches(*argv, "rttvar") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTTVAR); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"rttvar\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR, win); + } else if (matches(*argv, "ssthresh") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_SSTHRESH); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"ssthresh\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("\"realm\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (strcmp(*argv, "onlink") == 0) { + req.r.rtm_flags |= RTNH_F_ONLINK; + } else if (matches(*argv, "equalize") == 0 || + strcmp(*argv, "eql") == 0) { + req.r.rtm_flags |= RTM_F_EQUALIZE; + } else if (strcmp(*argv, "nexthop") == 0) { + nhs_ok = 1; + break; + } else if (matches(*argv, "protocol") == 0) { + int prot; + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + req.r.rtm_protocol = prot; + proto_ok =1; + } else if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("\"table\" value is invalid\n", *argv); + req.r.rtm_table = tid; + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + d = *argv; + } else { + int type; + inet_prefix dst; + + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if ((**argv < '0' || **argv > '9') && + rtnl_rtntype_a2n(&type, *argv) == 0) { + NEXT_ARG(); + req.r.rtm_type = type; + type_ok = 1; + } + + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg2("to", *argv); + get_prefix(&dst, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = dst.family; + req.r.rtm_dst_len = dst.bitlen; + dst_ok = 1; + if (dst.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (d || nhs_ok) { + int idx; + + ll_init_map(&rth); + + if (d) { + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (mxrta->rta_len > RTA_LENGTH(0)) { + if (mxlock) + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); + addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); + } + + if (nhs_ok) + parse_nexthops(&req.n, &req.r, argc, argv); + + if (!table_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_NAT || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_table = RT_TABLE_LOCAL; + } + if (!scope_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_NAT) + req.r.rtm_scope = RT_SCOPE_HOST; + else if (req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_MULTICAST || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_scope = RT_SCOPE_LINK; + else if (req.r.rtm_type == RTN_UNICAST || + req.r.rtm_type == RTN_UNSPEC) { + if (cmd == RTM_DELROUTE) + req.r.rtm_scope = RT_SCOPE_NOWHERE; + else if (!gw_ok && !nhs_ok) + req.r.rtm_scope = RT_SCOPE_LINK; + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + return 0; +} + +static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + memset(&req, 0, sizeof(req)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = RTM_GETROUTE; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.rtm.rtm_family = family; + req.rtm.rtm_flags |= RTM_F_CLONED; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +static int iproute_flush_cache(void) +{ +#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" + + int len; + int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); + char *buffer = "-1"; + + if (flush_fd < 0) { + fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); + return -1; + } + + len = strlen (buffer); + + if ((write (flush_fd, (void *)buffer, len)) < len) { + fprintf (stderr, "Cannot flush routing cache\n"); + return -1; + } + close(flush_fd); + return 0; +} + + +static int iproute_list_or_flush(int argc, char **argv, int flush) +{ + int do_ipv6 = preferred_family; + struct rtnl_handle rth; + char *id = NULL; + char *od = NULL; + + iproute_reset_filter(); + filter.tb = RT_TABLE_MAIN; + + if (flush && argc <= 0) { + fprintf(stderr, "\"ip route flush\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) { + if (strcmp(*argv, "all") == 0) { + tid = 0; + } else if (strcmp(*argv, "cache") == 0) { + tid = -1; + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("table id value is invalid\n", *argv); + } + } + filter.tb = tid; + } else if (matches(*argv, "cached") == 0 || + matches(*argv, "cloned") == 0) { + filter.tb = -1; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + filter.tos = tos; + filter.tosmask = -1; + } else if (matches(*argv, "protocol") == 0) { + int prot = 0; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"protocol\"\n", *argv); + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (matches(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"scope\"\n", *argv); + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + } else if (matches(*argv, "type") == 0) { + int type; + NEXT_ARG(); + filter.typemask = -1; + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("node type value is invalid\n", *argv); + filter.type = type; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + od = *argv; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + get_prefix(&filter.rvia, *argv, do_ipv6); + } else if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + get_prefix(&filter.rprefsrc, *argv, do_ipv6); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("invalid realms\n", *argv); + filter.realm = realm; + filter.realmmask = ~0U; + if ((filter.realm&0xFFFF) == 0 && + (*argv)[strlen(*argv) - 1] == '/') + filter.realmmask &= ~0xFFFF; + if ((filter.realm&0xFFFF0000U) == 0 && + (strchr(*argv, '/') == NULL || + (*argv)[0] == '/')) + filter.realmmask &= ~0xFFFF0000U; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rsrc, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.msrc, *argv, do_ipv6); + filter.rsrc = filter.msrc; + } + } else { + if (matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rdst, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.mdst, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.mdst, *argv, do_ipv6); + filter.rdst = filter.mdst; + } + } + argc--; argv++; + } + + if (do_ipv6 == AF_UNSPEC && filter.tb) + do_ipv6 = AF_INET; + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (id || od) { + int idx; + + if (id) { + if ((idx = ll_name_to_index(id)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", id); + return -1; + } + filter.iif = idx; + filter.iifmask = -1; + } + if (od) { + if ((idx = ll_name_to_index(od)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", od); + return -1; + } + filter.oif = idx; + filter.oifmask = -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + time_t start = time(0); + + if (filter.tb == -1) { + if (do_ipv6 != AF_INET6) { + iproute_flush_cache(); + if (show_stats) + printf("*** IPv4 routing cache is flushed.\n"); + } + if (do_ipv6 == AF_INET) + return 0; + } + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + if (filter.tb != -1 || do_ipv6 == AF_INET6) + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + + if (time(0) - start > 30) { + printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n", + time(0) - start, filter.flushed); + exit(1); + } + + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (filter.tb != -1) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } else { + if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } + + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + exit(0); +} + + +int iproute_get(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char *idev = NULL; + char *odev = NULL; + int connected = 0; + int from_ok = 0; + + memset(&req, 0, sizeof(req)); + + iproute_reset_filter(); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + req.r.rtm_family = preferred_family; + req.r.rtm_table = 0; + req.r.rtm_protocol = 0; + req.r.rtm_scope = 0; + req.r.rtm_type = 0; + req.r.rtm_src_len = 0; + req.r.rtm_dst_len = 0; + req.r.rtm_tos = 0; + + while (argc > 0) { + if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + from_ok = 1; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (matches(*argv, "iif") == 0) { + NEXT_ARG(); + idev = *argv; + } else if (matches(*argv, "oif") == 0 || + strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + odev = *argv; + } else if (matches(*argv, "notify") == 0) { + req.r.rtm_flags |= RTM_F_NOTIFY; + } else if (matches(*argv, "connected") == 0) { + connected = 1; + } else { + inet_prefix addr; + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); + req.r.rtm_dst_len = addr.bitlen; + } + argc--; argv++; + } + + if (req.r.rtm_dst_len == 0) { + fprintf(stderr, "need at least destination address\n"); + exit(1); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (idev || odev) { + int idx; + + if (idev) { + if ((idx = ll_name_to_index(idev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", idev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_IIF, idx); + } + if (odev) { + if ((idx = ll_name_to_index(odev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", odev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + + if (connected && !from_ok) { + struct rtmsg *r = NLMSG_DATA(&req.n); + int len = req.n.nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + if (req.n.nlmsg_type != RTM_NEWROUTE) { + fprintf(stderr, "Not a route?\n"); + return -1; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PREFSRC]) { + tb[RTA_PREFSRC]->rta_type = RTA_SRC; + r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); + } else if (!tb[RTA_SRC]) { + fprintf(stderr, "Failed to connect the route\n"); + return -1; + } + if (!odev && tb[RTA_OIF]) + tb[RTA_OIF]->rta_type = 0; + if (tb[RTA_GATEWAY]) + tb[RTA_GATEWAY]->rta_type = 0; + if (!idev && tb[RTA_IIF]) + tb[RTA_IIF]->rta_type = 0; + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + } + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + exit(0); +} + +void iproute_reset_filter() +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; +} + +int do_iproute(int argc, char **argv) +{ + if (argc < 1) + return iproute_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "prepend") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE, + argc-1, argv+1); + if (matches(*argv, "append") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND, + argc-1, argv+1); + if (matches(*argv, "test") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return iproute_modify(RTM_DELROUTE, 0, + argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return iproute_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return iproute_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return iproute_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv); + exit(-1); +} + diff --git a/ip/iproute.c.initvar b/ip/iproute.c.initvar new file mode 100644 index 0000000..b2ddb6e --- /dev/null +++ b/ip/iproute.c.initvar @@ -0,0 +1,1413 @@ +/* + * iproute.c "ip route". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <linux/in_route.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip route { list | flush } SELECTOR\n"); + fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n"); + fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n"); + fprintf(stderr, " ip route { add | del | change | append | replace | monitor } ROUTE\n"); + fprintf(stderr, "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ type TYPE ] [ scope SCOPE ]\n"); + fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n"); + fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ scope SCOPE ] [ metric METRIC ]\n"); + fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"); + fprintf(stderr, "NH := [ via ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); + fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ]\n"); + fprintf(stderr, " [ rtt NUMBER ] [ rttvar NUMBER ]\n"); + fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ ssthresh NUMBER ]\n"); + fprintf(stderr, " [ realms REALM ]\n"); + fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n"); + fprintf(stderr, " unreachable | prohibit | blackhole | nat ]\n"); + fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); + fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n"); + fprintf(stderr, "FLAGS := [ equalize ]\n"); + fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n"); + fprintf(stderr, "RTPROTO := [ kernel | boot | static | NUMBER ]\n"); + exit(-1); +} + + +static struct +{ + int tb; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; + int protocol, protocolmask; + int scope, scopemask; + int type, typemask; + int tos, tosmask; + int iif, iifmask; + int oif, oifmask; + int realm, realmmask; + inet_prefix rprefsrc; + inet_prefix rvia; + inet_prefix rdst; + inet_prefix mdst; + inet_prefix rsrc; + inet_prefix msrc; +} filter; + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + inet_prefix dst; + inet_prefix src; + inet_prefix prefsrc; + inet_prefix via; + int host_len = -1; + SPRINT_BUF(b1); + + + if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { + fprintf(stderr, "Not a route: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) + return 0; + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_INET) + host_len = 32; + else if (r->rtm_family == AF_DECnet) + host_len = 16; + else if (r->rtm_family == AF_IPX) + host_len = 80; + + if (r->rtm_family == AF_INET6) { + if (filter.tb) { + if (filter.tb < 0) { + if (!(r->rtm_flags&RTM_F_CLONED)) + return 0; + } else { + if (r->rtm_flags&RTM_F_CLONED) + return 0; + if (filter.tb == RT_TABLE_LOCAL) { + if (r->rtm_type != RTN_LOCAL) + return 0; + } else if (filter.tb == RT_TABLE_MAIN) { + if (r->rtm_type == RTN_LOCAL) + return 0; + } else { + return 0; + } + } + } + } else { + if (filter.tb > 0 && filter.tb != r->rtm_table) + return 0; + } + if ((filter.protocol^r->rtm_protocol)&filter.protocolmask) + return 0; + if ((filter.scope^r->rtm_scope)&filter.scopemask) + return 0; + if ((filter.type^r->rtm_type)&filter.typemask) + return 0; + if ((filter.tos^r->rtm_tos)&filter.tosmask) + return 0; + if (filter.rdst.family && + (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) + return 0; + if (filter.mdst.family && + (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) + return 0; + if (filter.rsrc.family && + (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) + return 0; + if (filter.msrc.family && + (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) + return 0; + if (filter.rvia.family && r->rtm_family != filter.rvia.family) + return 0; + if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) + return 0; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + memset(&dst, 0, sizeof(dst)); + dst.family = r->rtm_family; + if (tb[RTA_DST]) + memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); + if (filter.rsrc.family || filter.msrc.family) { + memset(&src, 0, sizeof(src)); + src.family = r->rtm_family; + if (tb[RTA_SRC]) + memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); + } + if (filter.rvia.bitlen>0) { + memset(&via, 0, sizeof(via)); + via.family = r->rtm_family; + if (tb[RTA_GATEWAY]) + memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len); + } + if (filter.rprefsrc.bitlen>0) { + memset(&prefsrc, 0, sizeof(prefsrc)); + prefsrc.family = r->rtm_family; + if (tb[RTA_PREFSRC]) + memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len); + } + + if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + return 0; + if (filter.mdst.family && filter.mdst.bitlen >= 0 && + inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) + return 0; + + if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + return 0; + if (filter.msrc.family && filter.msrc.bitlen >= 0 && + inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) + return 0; + + if (filter.rvia.family && inet_addr_match(&via, &filter.rvia, filter.rvia.bitlen)) + return 0; + if (filter.rprefsrc.family && inet_addr_match(&prefsrc, &filter.rprefsrc, filter.rprefsrc.bitlen)) + return 0; + if (filter.realmmask) { + __u32 realms = 0; + if (tb[RTA_FLOW]) + realms = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + if ((realms^filter.realm)&filter.realmmask) + return 0; + } + if (filter.iifmask) { + int iif = 0; + if (tb[RTA_IIF]) + iif = *(int*)RTA_DATA(tb[RTA_IIF]); + if ((iif^filter.iif)&filter.iifmask) + return 0; + } + if (filter.oifmask) { + int oif = 0; + if (tb[RTA_OIF]) + oif = *(int*)RTA_DATA(tb[RTA_OIF]); + if ((oif^filter.oif)&filter.oifmask) + return 0; + } + if (filter.flushb && + r->rtm_family == AF_INET6 && + r->rtm_dst_len == 0 && + r->rtm_type == RTN_UNREACHABLE && + tb[RTA_PRIORITY] && + *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELROUTE; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (n->nlmsg_type == RTM_DELROUTE) + fprintf(fp, "Deleted "); + if (r->rtm_type != RTN_UNICAST && !filter.type) + fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "%s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "0/%d ", r->rtm_dst_len); + } else { + fprintf(fp, "default "); + } + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "from %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "from 0/%u ", r->rtm_src_len); + } + if (r->rtm_tos && filter.tosmask != -1) { + SPRINT_BUF(b1); + fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { + fprintf(fp, "via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + if (tb[RTA_OIF] && filter.oifmask != -1) + fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); + + if (!(r->rtm_flags&RTM_F_CLONED)) { + if (r->rtm_table != RT_TABLE_MAIN && !filter.tb) + fprintf(fp, " table %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); + if (r->rtm_protocol != RTPROT_BOOT && filter.protocolmask != -1) + fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); + if (r->rtm_scope != RT_SCOPE_UNIVERSE && filter.scopemask != -1) + fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); + } + if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { + /* Do not use format_host(). It is our local addr + and symbolic name will not be useful. + */ + fprintf(fp, " src %s ", + rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_PREFSRC]), + RTA_DATA(tb[RTA_PREFSRC]), + abuf, sizeof(abuf))); + } + if (tb[RTA_PRIORITY]) + fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); + if (r->rtm_flags & RTNH_F_DEAD) + fprintf(fp, "dead "); + if (r->rtm_flags & RTNH_F_ONLINK) + fprintf(fp, "onlink "); + if (r->rtm_flags & RTNH_F_PERVASIVE) + fprintf(fp, "pervasive "); + if (r->rtm_flags & RTM_F_EQUALIZE) + fprintf(fp, "equalize "); + if (r->rtm_flags & RTM_F_NOTIFY) + fprintf(fp, "notify "); + + if (tb[RTA_FLOW] && filter.realmmask != ~0U) { + __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + __u32 from = to>>16; + to &= 0xFFFF; + fprintf(fp, "realm%s ", from ? "s" : ""); + if (from) { + fprintf(fp, "%s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + } + fprintf(fp, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + if ((r->rtm_flags&RTM_F_CLONED) && r->rtm_family == AF_INET) { + __u32 flags = r->rtm_flags&~0xFFFF; + int first = 1; + + fprintf(fp, "%s cache ", _SL_); + +#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \ + flags &= ~RTCF_##fl; \ + fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \ + first = 0; } + PRTFL(LOCAL, "local"); + PRTFL(REJECT, "reject"); + PRTFL(MULTICAST, "mc"); + PRTFL(BROADCAST, "brd"); + PRTFL(DNAT, "dst-nat"); + PRTFL(SNAT, "src-nat"); + PRTFL(MASQ, "masq"); + PRTFL(DIRECTDST, "dst-direct"); + PRTFL(DIRECTSRC, "src-direct"); + PRTFL(REDIRECTED, "redirected"); + PRTFL(DOREDIRECT, "redirect"); + PRTFL(FAST, "fastroute"); + PRTFL(NOTIFY, "notify"); + PRTFL(TPROXY, "proxy"); +#ifdef RTCF_EQUALIZE + PRTFL(EQUALIZE, "equalize"); +#endif + if (flags) + fprintf(fp, "%s%x> ", first ? "<" : "", flags); + if (tb[RTA_CACHEINFO]) { + struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + static int hz; + if (!hz) + hz = get_user_hz(); + if (ci->rta_expires != 0) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + if (ci->rta_lastuse != 0) + fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + } +#ifdef RTNETLINK_HAVE_PEERINFO + if (ci->rta_id) + fprintf(fp, " ipid 0x%04x", ci->rta_id); + if (ci->rta_ts || ci->rta_tsage) + fprintf(fp, " ts 0x%x tsage %dsec", ci->rta_ts, ci->rta_tsage); +#endif + } + } else if (r->rtm_family == AF_INET6) { + struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) + ci = RTA_DATA(tb[RTA_CACHEINFO]); + if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { + static int hz; + if (!hz) + hz = get_user_hz(); + if (r->rtm_flags & RTM_F_CLONED) + fprintf(fp, "%s cache ", _SL_); + if (ci->rta_expires) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + if (ci->rta_lastuse != 0) + fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + } + } else if (ci) { + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + } + } + if (tb[RTA_METRICS]) { + int i; + unsigned mxlock = 0; + struct rtattr *mxrta[RTAX_MAX+1]; + + parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), + RTA_PAYLOAD(tb[RTA_METRICS])); + if (mxrta[RTAX_LOCK]) + mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]); + + for (i=2; i<=RTAX_MAX; i++) { + static char *mx_names[] = + { + "mtu", + "window", + "rtt", + "rttvar", + "ssthresh", + "cwnd", + "advmss", + "reordering", + }; + static int hz; + if (mxrta[i] == NULL) + continue; + if (!hz) + hz = get_hz(); + if (i-2 < sizeof(mx_names)/sizeof(char*)) + fprintf(fp, " %s", mx_names[i-2]); + else + fprintf(fp, " metric %d", i); + if (mxlock & (1<<i)) + fprintf(fp, " lock"); + + if (i != RTAX_RTT && i != RTAX_RTTVAR) + fprintf(fp, " %u", *(unsigned*)RTA_DATA(mxrta[i])); + else { + unsigned val = *(unsigned*)RTA_DATA(mxrta[i]); + + val *= 1000; + if (i == RTAX_RTT) + val /= 8; + else + val /= 4; + if (val >= hz) + fprintf(fp, " %ums", val/hz); + else + fprintf(fp, " %.2fms", (float)val/hz); + } + } + } + if (tb[RTA_IIF] && filter.iifmask != -1) { + fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); + } + if (tb[RTA_MULTIPATH]) { + struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); + int first = 0; + + len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); + + for (;;) { + if (len < sizeof(*nh)) + break; + if (nh->rtnh_len > len) + break; + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + if (first) + fprintf(fp, " Oifs:"); + else + fprintf(fp, " "); + } else + fprintf(fp, "%s\tnexthop", _SL_); + if (nh->rtnh_len > sizeof(*nh)) { + parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh)); + if (tb[RTA_GATEWAY]) { + fprintf(fp, " via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + } + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + fprintf(fp, " %s", ll_index_to_name(nh->rtnh_ifindex)); + if (nh->rtnh_hops != 1) + fprintf(fp, "(ttl>%d)", nh->rtnh_hops); + } else { + fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex)); + fprintf(fp, " weight %d", nh->rtnh_hops+1); + } + if (nh->rtnh_flags & RTNH_F_DEAD) + fprintf(fp, " dead"); + if (nh->rtnh_flags & RTNH_F_ONLINK) + fprintf(fp, " onlink"); + if (nh->rtnh_flags & RTNH_F_PERVASIVE) + fprintf(fp, " pervasive"); + len -= NLMSG_ALIGN(nh->rtnh_len); + nh = RTNH_NEXT(nh); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +int parse_one_nh(struct rtattr *rta, struct rtnexthop *rtnh, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (++argv, --argc > 0) { + if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + rta_addattr32(rta, 4096, RTA_GATEWAY, get_addr32(*argv)); + rtnh->rtnh_len += sizeof(struct rtattr) + 4; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if ((rtnh->rtnh_ifindex = ll_name_to_index(*argv)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", *argv); + exit(1); + } + } else if (strcmp(*argv, "weight") == 0) { + unsigned w; + NEXT_ARG(); + if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256) + invarg("\"weight\" is invalid\n", *argv); + rtnh->rtnh_hops = w - 1; + } else if (strcmp(*argv, "onlink") == 0) { + rtnh->rtnh_flags |= RTNH_F_ONLINK; + } else + break; + } + *argcp = argc; + *argvp = argv; + return 0; +} + +int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char **argv) +{ + char buf[1024]; + struct rtattr *rta = (void*)buf; + struct rtnexthop *rtnh; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH(0); + rtnh = RTA_DATA(rta); + + while (argc > 0) { + if (strcmp(*argv, "nexthop") != 0) { + fprintf(stderr, "Error: \"nexthop\" or end of line is expected instead of \"%s\"\n", *argv); + exit(-1); + } + if (argc <= 1) { + fprintf(stderr, "Error: unexpected end of line after \"nexthop\"\n"); + exit(-1); + } + memset(rtnh, 0, sizeof(*rtnh)); + rtnh->rtnh_len = sizeof(*rtnh); + rta->rta_len += rtnh->rtnh_len; + parse_one_nh(rta, rtnh, &argc, &argv); + rtnh = RTNH_NEXT(rtnh); + } + + if (rta->rta_len > RTA_LENGTH(0)) + addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); + return 0; +} + + +int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char mxbuf[256]; + struct rtattr * mxrta = (void*)mxbuf; + unsigned mxlock = 0; + char *d = NULL; + int gw_ok = 0; + int dst_ok = 0; + int nhs_ok = 0; + int scope_ok = 0; + int table_ok = 0; + int proto_ok = 0; + int type_ok = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.r.rtm_family = preferred_family; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_scope = RT_SCOPE_NOWHERE; + + if (cmd != RTM_DELROUTE) { + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + } + + mxrta->rta_type = RTA_METRICS; + mxrta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + gw_ok = 1; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("\"tos\" value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "metric") == 0 || + matches(*argv, "priority") == 0 || + matches(*argv, "preference") == 0) { + __u32 metric; + NEXT_ARG(); + if (get_u32(&metric, *argv, 0)) + invarg("\"metric\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg("invalid \"scope\" value\n", *argv); + req.r.rtm_scope = scope; + scope_ok = 1; + } else if (strcmp(*argv, "mtu") == 0) { + unsigned mtu; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_MTU); + NEXT_ARG(); + } + if (get_unsigned(&mtu, *argv, 0)) + invarg("\"mtu\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); +#ifdef RTAX_ADVMSS + } else if (strcmp(*argv, "advmss") == 0) { + unsigned mss; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_ADVMSS); + NEXT_ARG(); + } + if (get_unsigned(&mss, *argv, 0)) + invarg("\"mss\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss); +#endif +#ifdef RTAX_REORDERING + } else if (matches(*argv, "reordering") == 0) { + unsigned reord; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_REORDERING); + NEXT_ARG(); + } + if (get_unsigned(&reord, *argv, 0)) + invarg("\"reordering\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord); +#endif + } else if (strcmp(*argv, "rtt") == 0) { + unsigned rtt; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTT); + NEXT_ARG(); + } + if (get_unsigned(&rtt, *argv, 0)) + invarg("\"rtt\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, rtt); + } else if (matches(*argv, "window") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_WINDOW); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"window\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win); + } else if (matches(*argv, "cwnd") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_CWND); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"cwnd\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win); + } else if (matches(*argv, "rttvar") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTTVAR); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"rttvar\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR, win); + } else if (matches(*argv, "ssthresh") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_SSTHRESH); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"ssthresh\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("\"realm\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (strcmp(*argv, "onlink") == 0) { + req.r.rtm_flags |= RTNH_F_ONLINK; + } else if (matches(*argv, "equalize") == 0 || + strcmp(*argv, "eql") == 0) { + req.r.rtm_flags |= RTM_F_EQUALIZE; + } else if (strcmp(*argv, "nexthop") == 0) { + nhs_ok = 1; + break; + } else if (matches(*argv, "protocol") == 0) { + int prot; + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + req.r.rtm_protocol = prot; + proto_ok =1; + } else if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("\"table\" value is invalid\n", *argv); + req.r.rtm_table = tid; + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + d = *argv; + } else { + int type; + inet_prefix dst; + + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if ((**argv < '0' || **argv > '9') && + rtnl_rtntype_a2n(&type, *argv) == 0) { + NEXT_ARG(); + req.r.rtm_type = type; + type_ok = 1; + } + + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg2("to", *argv); + get_prefix(&dst, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = dst.family; + req.r.rtm_dst_len = dst.bitlen; + dst_ok = 1; + if (dst.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (d || nhs_ok) { + int idx; + + ll_init_map(&rth); + + if (d) { + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (mxrta->rta_len > RTA_LENGTH(0)) { + if (mxlock) + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); + addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); + } + + if (nhs_ok) + parse_nexthops(&req.n, &req.r, argc, argv); + + if (!table_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_NAT || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_table = RT_TABLE_LOCAL; + } + if (!scope_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_NAT) + req.r.rtm_scope = RT_SCOPE_HOST; + else if (req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_MULTICAST || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_scope = RT_SCOPE_LINK; + else if (req.r.rtm_type == RTN_UNICAST || + req.r.rtm_type == RTN_UNSPEC) { + if (cmd == RTM_DELROUTE) + req.r.rtm_scope = RT_SCOPE_NOWHERE; + else if (!gw_ok && !nhs_ok) + req.r.rtm_scope = RT_SCOPE_LINK; + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + return 0; +} + +static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + memset(&req, 0, sizeof(req)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = RTM_GETROUTE; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.rtm.rtm_family = family; + req.rtm.rtm_flags |= RTM_F_CLONED; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +static int iproute_flush_cache(void) +{ +#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" + + int len; + int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); + char *buffer = "-1"; + + if (flush_fd < 0) { + fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); + return -1; + } + + len = strlen (buffer); + + if ((write (flush_fd, (void *)buffer, len)) < len) { + fprintf (stderr, "Cannot flush routing cache\n"); + return -1; + } + close(flush_fd); + return 0; +} + + +static int iproute_list_or_flush(int argc, char **argv, int flush) +{ + int do_ipv6 = preferred_family; + struct rtnl_handle rth; + char *id = NULL; + char *od = NULL; + + iproute_reset_filter(); + filter.tb = RT_TABLE_MAIN; + + if (flush && argc <= 0) { + fprintf(stderr, "\"ip route flush\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) { + if (strcmp(*argv, "all") == 0) { + tid = 0; + } else if (strcmp(*argv, "cache") == 0) { + tid = -1; + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("table id value is invalid\n", *argv); + } + } + filter.tb = tid; + } else if (matches(*argv, "cached") == 0 || + matches(*argv, "cloned") == 0) { + filter.tb = -1; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + filter.tos = tos; + filter.tosmask = -1; + } else if (matches(*argv, "protocol") == 0) { + int prot = 0; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"protocol\"\n", *argv); + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (matches(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"scope\"\n", *argv); + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + } else if (matches(*argv, "type") == 0) { + int type; + NEXT_ARG(); + filter.typemask = -1; + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("node type value is invalid\n", *argv); + filter.type = type; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + od = *argv; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + get_prefix(&filter.rvia, *argv, do_ipv6); + } else if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + get_prefix(&filter.rprefsrc, *argv, do_ipv6); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("invalid realms\n", *argv); + filter.realm = realm; + filter.realmmask = ~0U; + if ((filter.realm&0xFFFF) == 0 && + (*argv)[strlen(*argv) - 1] == '/') + filter.realmmask &= ~0xFFFF; + if ((filter.realm&0xFFFF0000U) == 0 && + (strchr(*argv, '/') == NULL || + (*argv)[0] == '/')) + filter.realmmask &= ~0xFFFF0000U; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rsrc, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.msrc, *argv, do_ipv6); + filter.rsrc = filter.msrc; + } + } else { + if (matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rdst, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.mdst, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.mdst, *argv, do_ipv6); + filter.rdst = filter.mdst; + } + } + argc--; argv++; + } + + if (do_ipv6 == AF_UNSPEC && filter.tb) + do_ipv6 = AF_INET; + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (id || od) { + int idx; + + if (id) { + if ((idx = ll_name_to_index(id)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", id); + return -1; + } + filter.iif = idx; + filter.iifmask = -1; + } + if (od) { + if ((idx = ll_name_to_index(od)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", od); + return -1; + } + filter.oif = idx; + filter.oifmask = -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + time_t start = time(0); + + if (filter.tb == -1) { + if (do_ipv6 != AF_INET6) { + iproute_flush_cache(); + if (show_stats) + printf("*** IPv4 routing cache is flushed.\n"); + } + if (do_ipv6 == AF_INET) + return 0; + } + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + if (filter.tb != -1 || do_ipv6 == AF_INET6) + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + + if (time(0) - start > 30) { + printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n", + time(0) - start, filter.flushed); + exit(1); + } + + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (filter.tb != -1) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } else { + if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } + + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + exit(0); +} + + +int iproute_get(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char *idev = NULL; + char *odev = NULL; + int connected = 0; + int from_ok = 0; + + memset(&req, 0, sizeof(req)); + + iproute_reset_filter(); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + req.r.rtm_family = preferred_family; + req.r.rtm_table = 0; + req.r.rtm_protocol = 0; + req.r.rtm_scope = 0; + req.r.rtm_type = 0; + req.r.rtm_src_len = 0; + req.r.rtm_dst_len = 0; + req.r.rtm_tos = 0; + + while (argc > 0) { + if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + from_ok = 1; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (matches(*argv, "iif") == 0) { + NEXT_ARG(); + idev = *argv; + } else if (matches(*argv, "oif") == 0 || + strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + odev = *argv; + } else if (matches(*argv, "notify") == 0) { + req.r.rtm_flags |= RTM_F_NOTIFY; + } else if (matches(*argv, "connected") == 0) { + connected = 1; + } else { + inet_prefix addr; + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); + req.r.rtm_dst_len = addr.bitlen; + } + argc--; argv++; + } + + if (req.r.rtm_dst_len == 0) { + fprintf(stderr, "need at least destination address\n"); + exit(1); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (idev || odev) { + int idx; + + if (idev) { + if ((idx = ll_name_to_index(idev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", idev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_IIF, idx); + } + if (odev) { + if ((idx = ll_name_to_index(odev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", odev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + + if (connected && !from_ok) { + struct rtmsg *r = NLMSG_DATA(&req.n); + int len = req.n.nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + if (req.n.nlmsg_type != RTM_NEWROUTE) { + fprintf(stderr, "Not a route?\n"); + return -1; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PREFSRC]) { + tb[RTA_PREFSRC]->rta_type = RTA_SRC; + r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); + } else if (!tb[RTA_SRC]) { + fprintf(stderr, "Failed to connect the route\n"); + return -1; + } + if (!odev && tb[RTA_OIF]) + tb[RTA_OIF]->rta_type = 0; + if (tb[RTA_GATEWAY]) + tb[RTA_GATEWAY]->rta_type = 0; + if (!idev && tb[RTA_IIF]) + tb[RTA_IIF]->rta_type = 0; + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + } + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + exit(0); +} + +void iproute_reset_filter() +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; +} + +int do_iproute(int argc, char **argv) +{ + if (argc < 1) + return iproute_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "prepend") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE, + argc-1, argv+1); + if (matches(*argv, "append") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND, + argc-1, argv+1); + if (matches(*argv, "test") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return iproute_modify(RTM_DELROUTE, 0, + argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return iproute_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return iproute_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return iproute_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv); + exit(-1); +} + diff --git a/ip/iproute.o b/ip/iproute.o new file mode 100644 index 0000000..37f2779 Binary files /dev/null and b/ip/iproute.o differ diff --git a/ip/iprule.c b/ip/iprule.c new file mode 100644 index 0000000..764edc8 --- /dev/null +++ b/ip/iprule.c @@ -0,0 +1,383 @@ +/* + * iprule.c "ip rule". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <string.h> + +#include "rt_names.h" +#include "utils.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip rule [ list | add | del | flush ] SELECTOR ACTION\n"); + fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n"); + fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n"); + fprintf(stderr, "ACTION := [ table TABLE_ID ]\n"); + fprintf(stderr, " [ prohibit | reject | unreachable ]\n"); + fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n"); + fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n"); + exit(-1); +} + +static int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + int host_len = -1; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWRULE) + return 0; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) + return -1; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (r->rtm_family == AF_INET) + host_len = 32; + else if (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_DECnet) + host_len = 16; + else if (r->rtm_family == AF_IPX) + host_len = 80; + + if (tb[RTA_PRIORITY]) + fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])); + else + fprintf(fp, "0:\t"); + + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "from %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "from 0/%d ", r->rtm_src_len); + } else { + fprintf(fp, "from all "); + } + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "to %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf))); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "to 0/%d ", r->rtm_dst_len); + } + + if (r->rtm_tos) { + SPRINT_BUF(b1); + fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + if (tb[RTA_PROTOINFO]) { + fprintf(fp, "fwmark %#x ", *(__u32*)RTA_DATA(tb[RTA_PROTOINFO])); + } + + if (tb[RTA_IIF]) { + fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF])); + } + + if (r->rtm_table) + fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); + + if (tb[RTA_FLOW]) { + __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + __u32 from = to>>16; + to &= 0xFFFF; + if (from) { + fprintf(fp, "realms %s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + } + fprintf(fp, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + + if (r->rtm_type == RTN_NAT) { + if (tb[RTA_GATEWAY]) { + fprintf(fp, "map-to %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } else + fprintf(fp, "masquerade"); + } else if (r->rtm_type != RTN_UNICAST) + fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int iprule_list(int argc, char **argv) +{ + struct rtnl_handle rth; + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET; + + if (argc > 0) { + fprintf(stderr, "\"ip rule show\" does not take any arguments.\n"); + return -1; + } + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + + +static int iprule_modify(int cmd, int argc, char **argv) +{ + int table_ok = 0; + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_type = cmd; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.r.rtm_family = preferred_family; + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_table = 0; + req.r.rtm_type = RTN_UNSPEC; + + if (cmd == RTM_NEWRULE) { + req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; + req.r.rtm_type = RTN_UNICAST; + } + + while (argc > 0) { + if (strcmp(*argv, "from") == 0) { + inet_prefix dst; + NEXT_ARG(); + get_prefix(&dst, *argv, req.r.rtm_family); + req.r.rtm_src_len = dst.bitlen; + addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen); + } else if (strcmp(*argv, "to") == 0) { + inet_prefix dst; + NEXT_ARG(); + get_prefix(&dst, *argv, req.r.rtm_family); + req.r.rtm_dst_len = dst.bitlen; + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "order") == 0 || + matches(*argv, "priority") == 0) { + __u32 pref; + NEXT_ARG(); + if (get_u32(&pref, *argv, 0)) + invarg("preference value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref); + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (strcmp(*argv, "fwmark") == 0) { + __u32 fwmark; + NEXT_ARG(); + if (get_u32(&fwmark, *argv, 0)) + invarg("fwmark value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("invalid realms\n", *argv); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (matches(*argv, "table") == 0 || + strcmp(*argv, "lookup") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("invalid table ID\n", *argv); + req.r.rtm_table = tid; + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1); + } else if (strcmp(*argv, "nat") == 0 || + matches(*argv, "map-to") == 0) { + NEXT_ARG(); + fprintf(stderr, "Warning: route NAT is deprecated\n"); + addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); + req.r.rtm_type = RTN_NAT; + } else { + int type; + + if (strcmp(*argv, "type") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("Failed to parse rule type", *argv); + req.r.rtm_type = type; + } + argc--; + argv++; + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (!table_ok && cmd == RTM_NEWRULE) + req.r.rtm_table = RT_TABLE_MAIN; + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + return 2; + + return 0; +} + + +static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + struct rtnl_handle rth; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) + return -1; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PRIORITY]) { + n->nlmsg_type = RTM_DELRULE; + n->nlmsg_flags = NLM_F_REQUEST; + + if (rtnl_open(&rth, 0) < 0) + return -1; + + if (rtnl_talk(&rth, n, 0, 0, NULL, NULL, NULL) < 0) + return -2; + } + + return 0; +} + +static int iprule_flush(int argc, char **argv) +{ + struct rtnl_handle rth; + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET; + + if (argc > 0) { + fprintf(stderr, "\"ip rule flush\" need not any arguments.\n"); + return -1; + } + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, flush_rule, NULL, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + return 1; + } + + return 0; +} + +int do_iprule(int argc, char **argv) +{ + if (argc < 1) { + return iprule_list(0, NULL); + } else if (matches(argv[0], "list") == 0 || + matches(argv[0], "lst") == 0 || + matches(argv[0], "show") == 0) { + return iprule_list(argc-1, argv+1); + } else if (matches(argv[0], "add") == 0) { + return iprule_modify(RTM_NEWRULE, argc-1, argv+1); + } else if (matches(argv[0], "delete") == 0) { + return iprule_modify(RTM_DELRULE, argc-1, argv+1); + } else if (matches(argv[0], "flush") == 0) { + return iprule_flush(argc-1, argv+1); + } else if (matches(argv[0], "help") == 0) + usage(); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); + exit(-1); +} + diff --git a/ip/iprule.o b/ip/iprule.o new file mode 100644 index 0000000..f880305 Binary files /dev/null and b/ip/iprule.o differ diff --git a/ip/iptunnel.c b/ip/iptunnel.c new file mode 100644 index 0000000..2da3df1 --- /dev/null +++ b/ip/iptunnel.c @@ -0,0 +1,585 @@ +/* + * iptunnel.c "ip tunnel" + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit + * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/ip.h> + +#ifndef __constant_htons +#define __constant_htons(x) htons(x) +#endif + +#include <linux/if_tunnel.h> + +#include "rt_names.h" +#include "utils.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip tunnel { add | change | del | show } [ NAME ]\n"); + fprintf(stderr, " [ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n"); + fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n"); + fprintf(stderr, " [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where: NAME := STRING\n"); + fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n"); + fprintf(stderr, " TOS := { NUMBER | inherit }\n"); + fprintf(stderr, " TTL := { 1..255 | inherit }\n"); + fprintf(stderr, " KEY := { DOTTED_QUAD | NUMBER }\n"); + exit(-1); +} + +static int do_ioctl_get_ifindex(const char *dev) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFINDEX, &ifr); + if (err) { + perror("ioctl"); + return 0; + } + close(fd); + return ifr.ifr_ifindex; +} + +static int do_ioctl_get_iftype(const char *dev) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFHWADDR, &ifr); + if (err) { + perror("ioctl"); + return -1; + } + close(fd); + return ifr.ifr_addr.sa_family; +} + + +static char * do_ioctl_get_ifname(int idx) +{ + static struct ifreq ifr; + int fd; + int err; + + ifr.ifr_ifindex = idx; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFNAME, &ifr); + if (err) { + perror("ioctl"); + return NULL; + } + close(fd); + return ifr.ifr_name; +} + + +static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGETTUNNEL, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (cmd == SIOCCHGTUNNEL && p->name[0]) + strncpy(ifr.ifr_name, p->name, IFNAMSIZ); + else + strncpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, cmd, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (p->name[0]) + strncpy(ifr.ifr_name, p->name, IFNAMSIZ); + else + strncpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCDELTUNNEL, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) +{ + int count = 0; + char medium[IFNAMSIZ]; + + memset(p, 0, sizeof(*p)); + memset(&medium, 0, sizeof(medium)); + + p->iph.version = 4; + p->iph.ihl = 5; +#ifndef IP_DF +#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ +#endif + p->iph.frag_off = htons(IP_DF); + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ipip") == 0 || + strcmp(*argv, "ip/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = IPPROTO_IPIP; + } else if (strcmp(*argv, "gre") == 0 || + strcmp(*argv, "gre/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = IPPROTO_GRE; + } else if (strcmp(*argv, "sit") == 0 || + strcmp(*argv, "ipv6/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = IPPROTO_IPV6; + } else { + fprintf(stderr,"Cannot guess tunnel mode.\n"); + exit(-1); + } + } else if (strcmp(*argv, "key") == 0) { + unsigned uval; + NEXT_ARG(); + p->i_flags |= GRE_KEY; + p->o_flags |= GRE_KEY; + if (strchr(*argv, '.')) + p->i_key = p->o_key = get_addr32(*argv); + else { + if (get_unsigned(&uval, *argv, 0)<0) { + fprintf(stderr, "invalid value of \"key\"\n"); + exit(-1); + } + p->i_key = p->o_key = htonl(uval); + } + } else if (strcmp(*argv, "ikey") == 0) { + unsigned uval; + NEXT_ARG(); + p->i_flags |= GRE_KEY; + if (strchr(*argv, '.')) + p->o_key = get_addr32(*argv); + else { + if (get_unsigned(&uval, *argv, 0)<0) { + fprintf(stderr, "invalid value of \"ikey\"\n"); + exit(-1); + } + p->i_key = htonl(uval); + } + } else if (strcmp(*argv, "okey") == 0) { + unsigned uval; + NEXT_ARG(); + p->o_flags |= GRE_KEY; + if (strchr(*argv, '.')) + p->o_key = get_addr32(*argv); + else { + if (get_unsigned(&uval, *argv, 0)<0) { + fprintf(stderr, "invalid value of \"okey\"\n"); + exit(-1); + } + p->o_key = htonl(uval); + } + } else if (strcmp(*argv, "seq") == 0) { + p->i_flags |= GRE_SEQ; + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "iseq") == 0) { + p->i_flags |= GRE_SEQ; + } else if (strcmp(*argv, "oseq") == 0) { + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "csum") == 0) { + p->i_flags |= GRE_CSUM; + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "icsum") == 0) { + p->i_flags |= GRE_CSUM; + } else if (strcmp(*argv, "ocsum") == 0) { + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "nopmtudisc") == 0) { + p->iph.frag_off = 0; + } else if (strcmp(*argv, "pmtudisc") == 0) { + p->iph.frag_off = htons(IP_DF); + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.daddr = get_addr32(*argv); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.saddr = get_addr32(*argv); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(medium, *argv, IFNAMSIZ-1); + } else if (strcmp(*argv, "ttl") == 0) { + unsigned uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_unsigned(&uval, *argv, 0)) + invarg("invalid TTL\n", *argv); + if (uval > 255) + invarg("TTL must be <=255\n", *argv); + p->iph.ttl = uval; + } + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + p->iph.tos = uval; + } else + p->iph.tos = 1; + } else { + if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (p->name[0]) + duparg2("name", *argv); + strncpy(p->name, *argv, IFNAMSIZ); + if (cmd == SIOCCHGTUNNEL && count == 0) { + struct ip_tunnel_parm old_p; + memset(&old_p, 0, sizeof(old_p)); + if (do_get_ioctl(*argv, &old_p)) + return -1; + *p = old_p; + } + } + count++; + argc--; argv++; + } + + + if (p->iph.protocol == 0) { + if (memcmp(p->name, "gre", 3) == 0) + p->iph.protocol = IPPROTO_GRE; + else if (memcmp(p->name, "ipip", 4) == 0) + p->iph.protocol = IPPROTO_IPIP; + else if (memcmp(p->name, "sit", 3) == 0) + p->iph.protocol = IPPROTO_IPV6; + } + + if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { + if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { + fprintf(stderr, "Keys are not allowed with ipip and sit.\n"); + return -1; + } + } + + if (medium[0]) { + p->link = do_ioctl_get_ifindex(medium); + if (p->link == 0) + return -1; + } + + if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->i_key = p->iph.daddr; + p->i_flags |= GRE_KEY; + } + if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->o_key = p->iph.daddr; + p->o_flags |= GRE_KEY; + } + if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { + fprintf(stderr, "Broadcast tunnel requires a source address.\n"); + return -1; + } + return 0; +} + + +static int do_add(int cmd, int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, cmd, &p) < 0) + return -1; + + if (p.iph.ttl && p.iph.frag_off == 0) { + fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n"); + return -1; + } + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_add_ioctl(cmd, "tunl0", &p); + case IPPROTO_GRE: + return do_add_ioctl(cmd, "gre0", &p); + case IPPROTO_IPV6: + return do_add_ioctl(cmd, "sit0", &p); + default: + fprintf(stderr, "cannot determine tunnel mode (ipip, gre or sit)\n"); + return -1; + } + return -1; +} + +int do_del(int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_del_ioctl("tunl0", &p); + case IPPROTO_GRE: + return do_del_ioctl("gre0", &p); + case IPPROTO_IPV6: + return do_del_ioctl("sit0", &p); + default: + return do_del_ioctl(p.name, &p); + } + return -1; +} + +void print_tunnel(struct ip_tunnel_parm *p) +{ + char s1[1024]; + char s2[1024]; + char s3[64]; + char s4[64]; + + inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3)); + inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4)); + + /* Do not use format_host() for local addr, + * symbolic name will not be useful. + */ + printf("%s: %s/ip remote %s local %s ", + p->name, + p->iph.protocol == IPPROTO_IPIP ? "ip" : + (p->iph.protocol == IPPROTO_GRE ? "gre" : + (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")), + p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any", + p->iph.saddr ? rt_addr_n2a(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any"); + + if (p->link) { + char *n = do_ioctl_get_ifname(p->link); + if (n) + printf(" dev %s ", n); + } + + if (p->iph.ttl) + printf(" ttl %d ", p->iph.ttl); + else + printf(" ttl inherit "); + + if (p->iph.tos) { + SPRINT_BUF(b1); + printf(" tos"); + if (p->iph.tos&1) + printf(" inherit"); + if (p->iph.tos&~1) + printf("%c%s ", p->iph.tos&1 ? '/' : ' ', + rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1))); + } + + if (!(p->iph.frag_off&htons(IP_DF))) + printf(" nopmtudisc"); + + if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key) + printf(" key %s", s3); + else if ((p->i_flags|p->o_flags)&GRE_KEY) { + if (p->i_flags&GRE_KEY) + printf(" ikey %s ", s3); + if (p->o_flags&GRE_KEY) + printf(" okey %s ", s4); + } + + if (p->i_flags&GRE_SEQ) + printf("%s Drop packets out of sequence.\n", _SL_); + if (p->i_flags&GRE_CSUM) + printf("%s Checksum in received packet is required.", _SL_); + if (p->o_flags&GRE_SEQ) + printf("%s Sequence packets on output.", _SL_); + if (p->o_flags&GRE_CSUM) + printf("%s Checksum output packets.", _SL_); +} + +static int do_tunnels_list(struct ip_tunnel_parm *p) +{ + char name[IFNAMSIZ]; + unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, + rx_fifo, rx_frame, + tx_bytes, tx_packets, tx_errs, tx_drops, + tx_fifo, tx_colls, tx_carrier, rx_multi; + int type; + struct ip_tunnel_parm p1; + + char buf[512]; + FILE *fp = fopen("/proc/net/dev", "r"); + if (fp == NULL) { + perror("fopen"); + return -1; + } + + fgets(buf, sizeof(buf), fp); + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *ptr; + buf[sizeof(buf) - 1] = 0; + if ((ptr = strchr(buf, ':')) == NULL || + (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { + fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n"); + return -1; + } + if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", + &rx_bytes, &rx_packets, &rx_errs, &rx_drops, + &rx_fifo, &rx_frame, &rx_multi, + &tx_bytes, &tx_packets, &tx_errs, &tx_drops, + &tx_fifo, &tx_colls, &tx_carrier) != 14) + continue; + if (p->name[0] && strcmp(p->name, name)) + continue; + type = do_ioctl_get_iftype(name); + if (type == -1) { + fprintf(stderr, "Failed to get type of [%s]\n", name); + continue; + } + if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) + continue; + memset(&p1, 0, sizeof(p1)); + if (do_get_ioctl(name, &p1)) + continue; + if ((p->link && p1.link != p->link) || + (p->name[0] && strcmp(p1.name, p->name)) || + (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || + (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || + (p->i_key && p1.i_key != p->i_key)) + continue; + print_tunnel(&p1); + if (show_stats) { + printf("%s", _SL_); + printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); + printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s", + rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_); + printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); + printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld", + tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops); + } + printf("\n"); + } + return 0; +} + +static int do_show(int argc, char **argv) +{ + int err; + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p); + break; + case IPPROTO_GRE: + err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p); + break; + case IPPROTO_IPV6: + err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p); + break; + default: + do_tunnels_list(&p); + return 0; + } + if (err) + return -1; + + print_tunnel(&p); + printf("\n"); + return 0; +} + +int do_iptunnel(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(SIOCADDTUNNEL, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return do_add(SIOCCHGTUNNEL, argc-1, argv+1); + if (matches(*argv, "del") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv); + exit(-1); +} diff --git a/ip/iptunnel.o b/ip/iptunnel.o new file mode 100644 index 0000000..4408086 Binary files /dev/null and b/ip/iptunnel.o differ diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c new file mode 100644 index 0000000..fc0f0d9 --- /dev/null +++ b/ip/ipxfrm.c @@ -0,0 +1,1051 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <time.h> +#include <netdb.h> +#include <net/if.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/xfrm.h> + +#include "utils.h" +#include "xfrm.h" + +struct xfrm_filter filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip xfrm XFRM_OBJECT { COMMAND | help }\n" + "where XFRM_OBJECT := { state | policy }\n"); + exit(-1); +} + +/* This is based on utils.c(inet_addr_match) */ +int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits) +{ + __u32 *a1 = (__u32 *)x1; + __u32 *a2 = (__u32 *)x2; + int words = bits >> 0x05; + + bits &= 0x1f; + + if (words) + if (memcmp(a1, a2, words << 2)) + return -1; + + if (bits) { + __u32 w1, w2; + __u32 mask; + + w1 = a1[words]; + w2 = a2[words]; + + mask = htonl((0xffffffff) << (0x20 - bits)); + + if ((w1 ^ w2) & mask) + return 1; + } + + return 0; +} + +struct typeent { + const char *t_name; + int t_type; +}; + +static const struct typeent xfrmproto_types[]= { + { "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, { "comp", IPPROTO_COMP }, + { NULL, -1 } +}; + +int xfrm_xfrmproto_getbyname(char *name) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &xfrmproto_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (strcmp(t->t_name, name) == 0) + return t->t_type; + } + + return -1; +} + +const char *strxf_xfrmproto(__u8 proto) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &xfrmproto_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (t->t_type == proto) + return t->t_name; + } + + return NULL; +} + +static const struct typeent algo_types[]= { + { "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH }, + { "comp", XFRMA_ALG_COMP }, { NULL, -1 } +}; + +int xfrm_algotype_getbyname(char *name) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &algo_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (strcmp(t->t_name, name) == 0) + return t->t_type; + } + + return -1; +} + +const char *strxf_algotype(int type) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &algo_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (t->t_type == type) + return t->t_name; + } + + return NULL; +} + +const char *strxf_mask8(__u8 mask) +{ + static char str[16]; + const int sn = sizeof(mask) * 8 - 1; + __u8 b; + int i = 0; + + for (b = (1 << sn); b > 0; b >>= 1) + str[i++] = ((b & mask) ? '1' : '0'); + str[i] = '\0'; + + return str; +} + +const char *strxf_mask32(__u32 mask) +{ + static char str[16]; + + sprintf(str, "%.8x", mask); + + return str; +} + +const char *strxf_share(__u8 share) +{ + static char str[32]; + + switch (share) { + case XFRM_SHARE_ANY: + strcpy(str, "any"); + break; + case XFRM_SHARE_SESSION: + strcpy(str, "session"); + break; + case XFRM_SHARE_USER: + strcpy(str, "user"); + break; + case XFRM_SHARE_UNIQUE: + strcpy(str, "unique"); + break; + default: + sprintf(str, "%u", share); + break; + } + + return str; +} + +const char *strxf_proto(__u8 proto) +{ + static char buf[32]; + struct protoent *pp; + const char *p; + + pp = getprotobynumber(proto); + if (pp) + p = pp->p_name; + else { + sprintf(buf, "%u", proto); + p = buf; + } + + return p; +} + +void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, + __u8 mode, __u32 reqid, __u16 family, int force_spi, + FILE *fp, const char *prefix) +{ + char abuf[256]; + + if (prefix) + fprintf(fp, prefix); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), + saddr, abuf, sizeof(abuf))); + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), + &id->daddr, abuf, sizeof(abuf))); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + + fprintf(fp, "proto %s ", strxf_xfrmproto(id->proto)); + + + if (show_stats > 0 || force_spi || id->spi) { + __u32 spi = ntohl(id->spi); + fprintf(fp, "spi 0x%08x", spi); + if (show_stats > 0) + fprintf(fp, "(%u)", spi); + fprintf(fp, " "); + } + + fprintf(fp, "reqid %u", reqid); + if (show_stats > 0) + fprintf(fp, "(0x%08x)", reqid); + fprintf(fp, " "); + + fprintf(fp, "mode "); + switch (mode) { + case 0: + fprintf(fp, "transport"); + break; + case 1: + fprintf(fp, "tunnel"); + break; + default: + fprintf(fp, "%u", mode); + break; + } + fprintf(fp, "%s", _SL_); +} + +static const char *strxf_limit(__u64 limit) +{ + static char str[32]; + if (limit == XFRM_INF) + strcpy(str, "(INF)"); + else + sprintf(str, "%llu", (unsigned long long) limit); + + return str; +} + +void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix) +{ + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "stats:"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "replay-window %u ", s->replay_window); + fprintf(fp, "replay %u ", s->replay); + fprintf(fp, "failed %u", s->integrity_failed); + fprintf(fp, "%s", _SL_); +} + +static const char *strxf_time(__u64 time) +{ + static char str[32]; + + if (time == 0) + strcpy(str, "-"); + else { + time_t t; + struct tm *tp; + + /* XXX: treat time in the same manner of kernel's + * net/xfrm/xfrm_{user,state}.c + */ + t = (long)time; + tp = localtime(&t); + + strftime(str, sizeof(str), "%Y-%m-%d %T", tp); + } + + return str; +} + +void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, + struct xfrm_lifetime_cur *cur, + FILE *fp, const char *prefix) +{ + if (cfg) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "lifetime config:"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "limit: "); + fprintf(fp, "soft "); + fprintf(fp, strxf_limit(cfg->soft_byte_limit)); + fprintf(fp, "(bytes), hard "); + fprintf(fp, strxf_limit(cfg->hard_byte_limit)); + fprintf(fp, "(bytes)"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "limit: "); + fprintf(fp, "soft "); + fprintf(fp, strxf_limit(cfg->soft_packet_limit)); + fprintf(fp, "(packets), hard "); + fprintf(fp, strxf_limit(cfg->hard_packet_limit)); + fprintf(fp, "(packets)"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "expire add: "); + fprintf(fp, "soft "); + fprintf(fp, "%llu", (unsigned long long) cfg->soft_add_expires_seconds); + fprintf(fp, "(sec), hard "); + fprintf(fp, "%llu", (unsigned long long) cfg->hard_add_expires_seconds); + fprintf(fp, "(sec)"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "expire use: "); + fprintf(fp, "soft "); + fprintf(fp, "%llu", (unsigned long long) cfg->soft_use_expires_seconds); + fprintf(fp, "(sec), hard "); + fprintf(fp, "%llu", (unsigned long long) cfg->hard_use_expires_seconds); + fprintf(fp, "(sec)"); + fprintf(fp, "%s", _SL_); + } + if (cur) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "lifetime current:"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "%llu(bytes), ", (unsigned long long) cur->bytes); + fprintf(fp, "%llu(packets)", (unsigned long long) cur->packets); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "add %s ", strxf_time(cur->add_time)); + fprintf(fp, "use %s", strxf_time(cur->use_time)); + fprintf(fp, "%s", _SL_); + } +} + +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix) +{ + char abuf[256]; + __u16 f; + + f = sel->family; + if (f == AF_UNSPEC) + f = family; + if (f == AF_UNSPEC) + f = preferred_family; + + if (prefix) + fprintf(fp, prefix); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "src %s/%u ", rt_addr_n2a(f, sizeof(sel->saddr), + &sel->saddr, abuf, sizeof(abuf)), + sel->prefixlen_s); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "dst %s/%u ", rt_addr_n2a(f, sizeof(sel->daddr), + &sel->daddr, abuf, sizeof(abuf)), + sel->prefixlen_d); + + if (sel->proto) + fprintf(fp, "proto %s ", strxf_proto(sel->proto)); + switch (sel->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + default: /* XXX */ + if (sel->sport_mask) + fprintf(fp, "sport %u ", ntohs(sel->sport)); + if (sel->dport_mask) + fprintf(fp, "dport %u ", ntohs(sel->dport)); + break; + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + /* type/code is stored at sport/dport in selector */ + if (sel->sport_mask) + fprintf(fp, "type %u ", ntohs(sel->sport)); + if (sel->dport_mask) + fprintf(fp, "code %u ", ntohs(sel->dport)); + break; + } + + if (sel->ifindex > 0) { + char buf[IFNAMSIZ]; + + memset(buf, '\0', sizeof(buf)); + if_indextoname(sel->ifindex, buf); + fprintf(fp, "dev %s ", buf); + } + + if (show_stats > 0) + fprintf(fp, "uid %u", sel->user); + + fprintf(fp, "%s", _SL_); +} + +static void xfrm_algo_print(struct xfrm_algo *algo, int type, int len, + FILE *fp, const char *prefix) +{ + int keylen; + int i; + + if (prefix) + fprintf(fp, prefix); + + fprintf(fp, "%s ", strxf_algotype(type)); + + if (len < sizeof(*algo)) { + fprintf(fp, "(ERROR truncated)"); + goto fin; + } + len -= sizeof(*algo); + + fprintf(fp, "%s ", algo->alg_name); + + keylen = algo->alg_key_len / 8; + if (len < keylen) { + fprintf(fp, "(ERROR truncated)"); + goto fin; + } + + fprintf(fp, "0x"); + for (i = 0; i < keylen; i ++) + fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]); + + if (show_stats > 0) + fprintf(fp, " (%d bits)", algo->alg_key_len); + + fin: + fprintf(fp, "%s", _SL_); +} + +static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int len, + __u16 family, FILE *fp, const char *prefix) +{ + int ntmpls = len / sizeof(struct xfrm_user_tmpl); + int i; + + if (ntmpls <= 0) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "(ERROR \"tmpl\" truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + + for (i = 0; i < ntmpls; i++) { + struct xfrm_user_tmpl *tmpl = &tmpls[i]; + + if (prefix) + fprintf(fp, prefix); + + fprintf(fp, "tmpl"); + xfrm_id_info_print(&tmpl->saddr, &tmpl->id, tmpl->mode, + tmpl->reqid, family, 0, fp, prefix); + + if (show_stats > 0 || tmpl->optional) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + switch (tmpl->optional) { + case 0: + if (show_stats > 0) + fprintf(fp, "level required "); + break; + case 1: + fprintf(fp, "level use "); + break; + default: + fprintf(fp, "level %u ", tmpl->optional); + break; + } + + if (show_stats > 0) + fprintf(fp, "share %s ", strxf_share(tmpl->share)); + + fprintf(fp, "%s", _SL_); + } + + if (show_stats > 0) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_mask32(tmpl->ealgos)); + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_AUTH), + strxf_mask32(tmpl->aalgos)); + fprintf(fp, "%s-mask %s", + strxf_algotype(XFRMA_ALG_COMP), + strxf_mask32(tmpl->calgos)); + + fprintf(fp, "%s", _SL_); + } + } +} + +void xfrm_xfrma_print(struct rtattr *tb[], __u16 family, + FILE *fp, const char *prefix) +{ + if (tb[XFRMA_ALG_AUTH]) { + struct rtattr *rta = tb[XFRMA_ALG_AUTH]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), + XFRMA_ALG_AUTH, RTA_PAYLOAD(rta), fp, prefix); + } + + if (tb[XFRMA_ALG_CRYPT]) { + struct rtattr *rta = tb[XFRMA_ALG_CRYPT]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), + XFRMA_ALG_CRYPT, RTA_PAYLOAD(rta), fp, prefix); + } + + if (tb[XFRMA_ALG_COMP]) { + struct rtattr *rta = tb[XFRMA_ALG_COMP]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), + XFRMA_ALG_COMP, RTA_PAYLOAD(rta), fp, prefix); + } + + if (tb[XFRMA_ENCAP]) { + struct xfrm_encap_tmpl *e; + char abuf[256]; + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "encap "); + + if (RTA_PAYLOAD(tb[XFRMA_ENCAP]) < sizeof(*e)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + e = (struct xfrm_encap_tmpl *) RTA_DATA(tb[XFRMA_ENCAP]); + + fprintf(fp, "type "); + switch (e->encap_type) { + case 1: + fprintf(fp, "espinudp-nonike "); + break; + case 2: + fprintf(fp, "espinudp "); + break; + default: + fprintf(fp, "%u ", e->encap_type); + break; + } + fprintf(fp, "sport %u ", ntohs(e->encap_sport)); + fprintf(fp, "dport %u ", ntohs(e->encap_dport)); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "addr %s", + rt_addr_n2a(family, sizeof(e->encap_oa), + &e->encap_oa, abuf, sizeof(abuf))); + fprintf(fp, "%s", _SL_); + } + + if (tb[XFRMA_TMPL]) { + struct rtattr *rta = tb[XFRMA_TMPL]; + xfrm_tmpl_print((struct xfrm_user_tmpl *) RTA_DATA(rta), + RTA_PAYLOAD(rta), family, fp, prefix); + } +} + +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int loose, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst; + inet_prefix src; + + memset(&dst, 0, sizeof(dst)); + memset(&src, 0, sizeof(src)); + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("\"src\" address family is AF_UNSPEC", *argv); + if (family) + *family = src.family; + + memcpy(saddr, &src.data, sizeof(*saddr)); + + filter.id_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("\"dst\" address family is AF_UNSPEC", *argv); + if (family) + *family = dst.family; + + memcpy(&id->daddr, &dst.data, sizeof(id->daddr)); + + filter.id_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "proto") == 0) { + int ret; + + NEXT_ARG(); + + ret = xfrm_xfrmproto_getbyname(*argv); + if (ret < 0) + invarg("\"XFRM_PROTO\" is invalid", *argv); + + id->proto = (__u8)ret; + + filter.id_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "spi") == 0) { + __u32 spi; + + NEXT_ARG(); + if (get_u32(&spi, *argv, 0)) + invarg("\"SPI\" is invalid", *argv); + + spi = htonl(spi); + id->spi = spi; + + filter.id_spi_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between \"src\" and \"dst\"", *argv); + + if (loose == 0 && id->proto == 0) + missarg("XFRM_PROTO"); + if (argc == *argcp) + missarg("ID"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (matches(*argv, "transport") == 0) + *mode = 0; + else if (matches(*argv, "tunnel") == 0) + *mode = 1; + else + invarg("\"MODE\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "espinudp-nonike") == 0) + *type = 1; + else if (strcmp(*argv, "espinudp") == 0) + *type = 2; + else + invarg("\"ENCAP-TYPE\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +/* NOTE: reqid is used by host-byte order */ +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (get_u32(reqid, *argv, 0)) + invarg("\"REQID\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_selector_upspec_parse(struct xfrm_selector *sel, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + char *sportp = NULL; + char *dportp = NULL; + char *typep = NULL; + char *codep = NULL; + + while (1) { + if (strcmp(*argv, "proto") == 0) { + __u8 upspec; + + NEXT_ARG(); + + if (strcmp(*argv, "any") == 0) + upspec = 0; + else { + struct protoent *pp; + pp = getprotobyname(*argv); + if (pp) + upspec = pp->p_proto; + else { + if (get_u8(&upspec, *argv, 0)) + invarg("\"PROTO\" is invalid", *argv); + } + } + sel->proto = upspec; + + filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "sport") == 0) { + sportp = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->sport, *argv, 0)) + invarg("\"PORT\" is invalid", *argv); + sel->sport = htons(sel->sport); + if (sel->sport) + sel->sport_mask = ~((__u16)0); + + filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "dport") == 0) { + dportp = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->dport, *argv, 0)) + invarg("\"PORT\" is invalid", *argv); + sel->dport = htons(sel->dport); + if (sel->dport) + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "type") == 0) { + typep = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->sport, *argv, 0) || + (sel->sport & ~((__u16)0xff))) + invarg("\"type\" value is invalid", *argv); + sel->sport = htons(sel->sport); + sel->sport_mask = ~((__u16)0); + + filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; + + + } else if (strcmp(*argv, "code") == 0) { + codep = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->dport, *argv, 0) || + (sel->dport & ~((__u16)0xff))) + invarg("\"code\" value is invalid", *argv); + sel->dport = htons(sel->dport); + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + if (argc == *argcp) + missarg("UPSPEC"); + if (sportp || dportp) { + switch (sel->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + break; + default: + fprintf(stderr, "\"sport\" and \"dport\" are invalid with proto=%s\n", strxf_proto(sel->proto)); + exit(1); + } + } + if (typep || codep) { + switch (sel->proto) { + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + break; + default: + fprintf(stderr, "\"type\" and \"code\" are invalid with proto=%s\n", strxf_proto(sel->proto)); + exit(1); + } + } + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst; + inet_prefix src; + char *upspecp = NULL; + + memset(&dst, 0, sizeof(dst)); + memset(&src, 0, sizeof(src)); + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("\"src\" address family is AF_UNSPEC", *argv); + sel->family = src.family; + + memcpy(&sel->saddr, &src.data, sizeof(sel->saddr)); + sel->prefixlen_s = src.bitlen; + + filter.sel_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("\"dst\" address family is AF_UNSPEC", *argv); + sel->family = dst.family; + + memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr)); + sel->prefixlen_d = dst.bitlen; + + filter.sel_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "dev") == 0) { + int ifindex; + + NEXT_ARG(); + + if (strcmp(*argv, "none") == 0) + ifindex = 0; + else { + ifindex = if_nametoindex(*argv); + if (ifindex <= 0) + invarg("\"DEV\" is invalid", *argv); + } + sel->ifindex = ifindex; + + filter.sel_dev_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (upspecp) { + PREV_ARG(); /* back track */ + break; + } else { + upspecp = *argv; + xfrm_selector_upspec_parse(sel, &argc, &argv); + } + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between \"src\" and \"dst\"", *argv); + + if (argc == *argcp) + missarg("SELECTOR"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + + if (strcmp(*argv, "time-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_add_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "time-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_add_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "time-use-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_use_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-use-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "time-use-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_use_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-use-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "byte-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_byte_limit, *argv, 0); + if (ret) + invarg("\"byte-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "byte-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_byte_limit, *argv, 0); + if (ret) + invarg("\"byte-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "packet-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_packet_limit, *argv, 0); + if (ret) + invarg("\"packet-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "packet-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_packet_limit, *argv, 0); + if (ret) + invarg("\"packet-hard\" value is invalid", *argv); + } else + invarg("\"LIMIT\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int do_xfrm(int argc, char **argv) +{ + memset(&filter, 0, sizeof(filter)); + + if (argc < 1) + usage(); + + if (matches(*argv, "state") == 0 || + matches(*argv, "sa") == 0) { + return do_xfrm_state(argc-1, argv+1); + } else if (matches(*argv, "policy") == 0) + return do_xfrm_policy(argc-1, argv+1); + else if (matches(*argv, "help") == 0) { + usage(); + fprintf(stderr, "xfrm Object \"%s\" is unknown.\n", *argv); + exit(-1); + } + usage(); +} diff --git a/ip/ipxfrm.o b/ip/ipxfrm.o new file mode 100644 index 0000000..5c4380f Binary files /dev/null and b/ip/ipxfrm.o differ diff --git a/ip/routef b/ip/routef new file mode 100755 index 0000000..db43b5d --- /dev/null +++ b/ip/routef @@ -0,0 +1,3 @@ +#! /bin/sh + +exec ip -4 ro flush scope global type unicast diff --git a/ip/routel b/ip/routel new file mode 100755 index 0000000..8d1d352 --- /dev/null +++ b/ip/routel @@ -0,0 +1,60 @@ +#!/bin/sh +#$Id$ + +# +# Script created by: Stephen R. van den Berg <srb@cuci.nl>, 1999/04/18 +# Donated to the public domain. +# +# This script transforms the output of "ip" into more readable text. +# "ip" is the Linux-advanced-routing configuration tool part of the +# iproute package. +# + +test "X-h" = "X$1" && echo "Usage: $0 [tablenr [raw ip args...]]" && exit 64 + +test -z "$*" && set 0 + +ip route list table "$@" | + while read network rest + do set xx $rest + shift + proto="" + via="" + dev="" + scope="" + src="" + table="" + case $network in + broadcast|local|unreachable) via=$network + network=$1 + shift + ;; + esac + while test $# != 0 + do + key=$1 + val=$2 + eval "$key=$val" + shift 2 + done + echo "$network $via $src $proto $scope $dev $table" + done | awk -F ' ' ' +BEGIN { + format="%15s%-3s %15s %15s %8s %8s%7s %s\n"; + printf(format,"target","","gateway","source","proto","scope","dev","tbl"); + } + { network=$1; + mask=""; + if(match(network,"/")) + { mask=" "substr(network,RSTART+1); + network=substr(network,0,RSTART); + } + via=$2; + src=$3; + proto=$4; + scope=$5; + dev=$6; + table=$7; + printf(format,network,mask,via,src,proto,scope,dev,table); + } +' diff --git a/ip/rtm_map.c b/ip/rtm_map.c new file mode 100644 index 0000000..21e818b --- /dev/null +++ b/ip/rtm_map.c @@ -0,0 +1,116 @@ +/* + * rtm_map.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include "rt_names.h" +#include "utils.h" + +char *rtnl_rtntype_n2a(int id, char *buf, int len) +{ + switch (id) { + case RTN_UNSPEC: + return "none"; + case RTN_UNICAST: + return "unicast"; + case RTN_LOCAL: + return "local"; + case RTN_BROADCAST: + return "broadcast"; + case RTN_ANYCAST: + return "anycast"; + case RTN_MULTICAST: + return "multicast"; + case RTN_BLACKHOLE: + return "blackhole"; + case RTN_UNREACHABLE: + return "unreachable"; + case RTN_PROHIBIT: + return "prohibit"; + case RTN_THROW: + return "throw"; + case RTN_NAT: + return "nat"; + case RTN_XRESOLVE: + return "xresolve"; + default: + snprintf(buf, len, "%d", id); + return buf; + } +} + + +int rtnl_rtntype_a2n(int *id, char *arg) +{ + char *end; + unsigned long res; + + if (strcmp(arg, "local") == 0) + res = RTN_LOCAL; + else if (strcmp(arg, "nat") == 0) + res = RTN_NAT; + else if (matches(arg, "broadcast") == 0 || + strcmp(arg, "brd") == 0) + res = RTN_BROADCAST; + else if (matches(arg, "anycast") == 0) + res = RTN_ANYCAST; + else if (matches(arg, "multicast") == 0) + res = RTN_MULTICAST; + else if (matches(arg, "prohibit") == 0) + res = RTN_PROHIBIT; + else if (matches(arg, "unreachable") == 0) + res = RTN_UNREACHABLE; + else if (matches(arg, "blackhole") == 0) + res = RTN_BLACKHOLE; + else if (matches(arg, "xresolve") == 0) + res = RTN_XRESOLVE; + else if (matches(arg, "unicast") == 0) + res = RTN_UNICAST; + else if (strcmp(arg, "throw") == 0) + res = RTN_THROW; + else { + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + } + *id = res; + return 0; +} + +int get_rt_realms(__u32 *realms, char *arg) +{ + __u32 realm = 0; + char *p = strchr(arg, '/'); + + *realms = 0; + if (p) { + *p = 0; + if (rtnl_rtrealm_a2n(realms, arg)) { + *p = '/'; + return -1; + } + *realms <<= 16; + *p = '/'; + arg = p+1; + } + if (*arg && rtnl_rtrealm_a2n(&realm, arg)) + return -1; + *realms |= realm; + return 0; +} diff --git a/ip/rtm_map.o b/ip/rtm_map.o new file mode 100644 index 0000000..2daff0e Binary files /dev/null and b/ip/rtm_map.o differ diff --git a/ip/rtmon b/ip/rtmon new file mode 100755 index 0000000..179849b Binary files /dev/null and b/ip/rtmon differ diff --git a/ip/rtmon.c b/ip/rtmon.c new file mode 100644 index 0000000..5ce7731 --- /dev/null +++ b/ip/rtmon.c @@ -0,0 +1,178 @@ +/* + * rtmon.c RTnetlink listener. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <string.h> + +#include "SNAPSHOT.h" + +#include "utils.h" +#include "libnetlink.h" + +int resolve_hosts = 0; +static int init_phase = 1; + +static void write_stamp(FILE *fp) +{ + char buf[128]; + struct nlmsghdr *n1 = (void*)buf; + struct timeval tv; + + n1->nlmsg_type = 15; + n1->nlmsg_flags = 0; + n1->nlmsg_seq = 0; + n1->nlmsg_pid = 0; + n1->nlmsg_len = NLMSG_LENGTH(4*2); + gettimeofday(&tv, NULL); + ((__u32*)NLMSG_DATA(n1))[0] = tv.tv_sec; + ((__u32*)NLMSG_DATA(n1))[1] = tv.tv_usec; + fwrite((void*)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp); +} + +static int dump_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + if (!init_phase) + write_stamp(fp); + fwrite((void*)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp); + fflush(fp); + return 0; +} + +void usage(void) +{ + fprintf(stderr, "Usage: rtmon file FILE [ all | LISTofOBJECTS]\n"); + fprintf(stderr, "LISTofOBJECTS := [ link ] [ address ] [ route ]\n"); + exit(-1); +} + +int +main(int argc, char **argv) +{ + FILE *fp; + struct rtnl_handle rth; + int family = AF_UNSPEC; + unsigned groups = ~0U; + int llink = 0; + int laddr = 0; + int lroute = 0; + char *file = NULL; + + while (argc > 1) { + if (matches(argv[1], "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "inet") == 0) + family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "link") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "help") == 0) + usage(); + else { + fprintf(stderr, "Protocol ID \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + } else if (strcmp(argv[1], "-4") == 0) { + family = AF_INET; + } else if (strcmp(argv[1], "-6") == 0) { + family = AF_INET6; + } else if (strcmp(argv[1], "-0") == 0) { + family = AF_PACKET; + } else if (matches(argv[1], "-Version") == 0) { + printf("rtmon utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + } else if (matches(argv[1], "file") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + file = argv[1]; + } else if (matches(argv[1], "link") == 0) { + llink=1; + groups = 0; + } else if (matches(argv[1], "address") == 0) { + laddr=1; + groups = 0; + } else if (matches(argv[1], "route") == 0) { + lroute=1; + groups = 0; + } else if (strcmp(argv[1], "all") == 0) { + groups = ~0U; + } else if (matches(argv[1], "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + argc--; argv++; + } + + if (file == NULL) { + fprintf(stderr, "Not enough information: argument \"file\" is required\n"); + exit(-1); + } + if (llink) + groups |= RTMGRP_LINK; + if (laddr) { + if (!family || family == AF_INET) + groups |= RTMGRP_IPV4_IFADDR; + if (!family || family == AF_INET6) + groups |= RTMGRP_IPV6_IFADDR; + } + if (lroute) { + if (!family || family == AF_INET) + groups |= RTMGRP_IPV4_ROUTE; + if (!family || family == AF_INET6) + groups |= RTMGRP_IPV6_ROUTE; + } + + fp = fopen(file, "w"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + write_stamp(fp); + + if (rtnl_dump_filter(&rth, dump_msg, fp, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + init_phase = 0; + + if (rtnl_listen(&rth, dump_msg, (void*)fp) < 0) + exit(2); + + exit(0); +} diff --git a/ip/rtmon.o b/ip/rtmon.o new file mode 100644 index 0000000..7729575 Binary files /dev/null and b/ip/rtmon.o differ diff --git a/ip/rtpr b/ip/rtpr new file mode 100755 index 0000000..c3629fd --- /dev/null +++ b/ip/rtpr @@ -0,0 +1,4 @@ +#! /bin/bash + +exec tr "[\\\\]" "[ +]" diff --git a/ip/xfrm.h b/ip/xfrm.h new file mode 100644 index 0000000..fa551b1 --- /dev/null +++ b/ip/xfrm.h @@ -0,0 +1,119 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#ifndef __XFRM_H__ +#define __XFRM_H__ 1 + +#include <stdio.h> +#include <sys/socket.h> +#include <linux/xfrm.h> + +#ifndef IPPROTO_SCTP +# define IPPROTO_SCTP 132 +#endif + +#define XFRMS_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_info)))) +#define XFRMS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_info)) + +#define XFRMP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_info)))) +#define XFRMP_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_info)) + +#define XFRM_FLAG_PRINT(fp, flags, f, s) \ + do { \ + if (flags & f) { \ + flags &= ~f; \ + fprintf(fp, s "%s", (flags ? " " : "")); \ + } \ + } while(0) + +struct xfrm_buffer { + char *buf; + int size; + int offset; + + int nlmsg_count; + struct rtnl_handle *rth; +}; + +struct xfrm_filter { + int use; + + struct xfrm_usersa_info xsinfo; + __u8 id_src_mask; + __u8 id_dst_mask; + __u8 id_proto_mask; + __u32 id_spi_mask; + __u8 mode_mask; + __u32 reqid_mask; + __u8 state_flags_mask; + + struct xfrm_userpolicy_info xpinfo; + __u8 dir_mask; + __u8 sel_src_mask; + __u8 sel_dst_mask; + __u32 sel_dev_mask; + __u8 upspec_proto_mask; + __u16 upspec_sport_mask; + __u16 upspec_dport_mask; + __u32 index_mask; + __u8 action_mask; + __u32 priority_mask; +}; +#define XFRM_FILTER_MASK_FULL (~0) + +extern struct xfrm_filter filter; + +int do_xfrm_state(int argc, char **argv); +int do_xfrm_policy(int argc, char **argv); + +int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits); +int xfrm_xfrmproto_getbyname(char *name); +int xfrm_algotype_getbyname(char *name); +const char *strxf_xfrmproto(__u8 proto); +const char *strxf_algotype(int type); +const char *strxf_mask8(__u8 mask); +const char *strxf_mask32(__u32 mask); +const char *strxf_share(__u8 share); +const char *strxf_proto(__u8 proto); +void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, + __u8 mode, __u32 reqid, __u16 family, int force_spi, + FILE *fp, const char *prefix); +void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix); +void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, + struct xfrm_lifetime_cur *cur, + FILE *fp, const char *prefix); +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix); +void xfrm_xfrma_print(struct rtattr *tb[], __u16 family, + FILE *fp, const char *prefix); +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int loose, int *argcp, char ***argvp); +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp); +int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp); +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp); +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp); +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp); + +#endif diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c new file mode 100644 index 0000000..c1331a4 --- /dev/null +++ b/ip/xfrm_policy.c @@ -0,0 +1,736 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <linux/netlink.h> +#include <linux/xfrm.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +//#define NLMSG_FLUSH_BUF_SIZE (4096-512) +#define NLMSG_FLUSH_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_userpolicy_info + * rtattr + * data = struct xfrm_user_tmpl[] + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_TMPLS_BUF_SIZE 1024 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip xfrm policy { add | update } dir DIR SELECTOR [ index INDEX ] \n"); + fprintf(stderr, " [ action ACTION ] [ priority PRIORITY ] [ LIMIT-LIST ] [ TMPL-LIST ]\n"); + fprintf(stderr, "Usage: ip xfrm policy { delete | get } dir DIR [ SELECTOR | index INDEX ]\n"); + fprintf(stderr, "Usage: ip xfrm policy { flush | list } [ dir DIR ] [ SELECTOR ]\n"); + fprintf(stderr, " [ index INDEX ] [ action ACTION ] [ priority PRIORITY ]\n"); + fprintf(stderr, "DIR := [ in | out | fwd ]\n"); + + fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ UPSPEC ] [ dev DEV ]\n"); + + fprintf(stderr, "UPSPEC := proto PROTO [ [ sport PORT ] [ dport PORT ] |\n"); + fprintf(stderr, " [ type NUMBER ] [ code NUMBER ] ]\n"); + + //fprintf(stderr, "DEV - device name(default=none)\n"); + + fprintf(stderr, "ACTION := [ allow | block ](default=allow)\n"); + + //fprintf(stderr, "PRIORITY - priority value(default=0)\n"); + + fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]\n"); + fprintf(stderr, "LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |\n"); + fprintf(stderr, " [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] NUMBER ]\n"); + + fprintf(stderr, "TMPL-LIST := [ TMPL-LIST ] | [ tmpl TMPL ]\n"); + fprintf(stderr, "TMPL := ID [ mode MODE ] [ reqid REQID ] [ level LEVEL ]\n"); + fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n"); + + //fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n"); + fprintf(stderr, "XFRM_PROTO := [ "); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP)); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH)); + fprintf(stderr, "%s", strxf_xfrmproto(IPPROTO_COMP)); + fprintf(stderr, " ]\n"); + + fprintf(stderr, "MODE := [ transport | tunnel ](default=transport)\n"); + //fprintf(stderr, "REQID - number(default=0)\n"); + fprintf(stderr, "LEVEL := [ required | use ](default=required)\n"); + + exit(-1); +} + +static int xfrm_policy_dir_parse(__u8 *dir, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "in") == 0) + *dir = XFRM_POLICY_IN; + else if (strcmp(*argv, "out") == 0) + *dir = XFRM_POLICY_OUT; + else if (strcmp(*argv, "fwd") == 0) + *dir = XFRM_POLICY_FWD; + else + invarg("\"DIR\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_tmpl_parse(struct xfrm_user_tmpl *tmpl, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + char *idp = NULL; + + while (1) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&tmpl->mode, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&tmpl->reqid, &argc, &argv); + } else if (strcmp(*argv, "level") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "required") == 0) + tmpl->optional = 0; + else if (strcmp(*argv, "use") == 0) + tmpl->optional = 1; + else + invarg("\"LEVEL\" is invalid\n", *argv); + + } else { + if (idp) { + PREV_ARG(); /* back track */ + break; + } + idp = *argv; + xfrm_id_parse(&tmpl->saddr, &tmpl->id, &tmpl->family, + 0, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = tmpl->family; + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + if (argc == *argcp) + missarg("TMPL"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_info xpinfo; + char buf[RTA_BUF_SIZE]; + } req; + char *dirp = NULL; + char *selp = NULL; + char tmpls_buf[XFRM_TMPLS_BUF_SIZE]; + int tmpls_len = 0; + + memset(&req, 0, sizeof(req)); + memset(&tmpls_buf, 0, sizeof(tmpls_buf)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.xpinfo.sel.family = preferred_family; + + req.xpinfo.lft.soft_byte_limit = XFRM_INF; + req.xpinfo.lft.hard_byte_limit = XFRM_INF; + req.xpinfo.lft.soft_packet_limit = XFRM_INF; + req.xpinfo.lft.hard_packet_limit = XFRM_INF; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpinfo.dir, &argc, &argv); + + filter.dir_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + filter.index_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + req.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + req.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("\"action\" value is invalid\n", *argv); + + filter.action_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.priority, *argv, 0)) + invarg("\"PRIORITY\" is invalid", *argv); + + filter.priority_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xpinfo.lft, &argc, &argv); + } else if (strcmp(*argv, "tmpl") == 0) { + struct xfrm_user_tmpl *tmpl; + + if (tmpls_len + sizeof(*tmpl) > sizeof(tmpls_buf)) { + fprintf(stderr, "Too many tmpls: buffer overflow\n"); + exit(1); + } + tmpl = (struct xfrm_user_tmpl *)((char *)tmpls_buf + tmpls_len); + + tmpl->family = preferred_family; + tmpl->aalgos = (~(__u32)0); + tmpl->ealgos = (~(__u32)0); + tmpl->calgos = (~(__u32)0); + + NEXT_ARG(); + xfrm_tmpl_parse(tmpl, &argc, &argv); + + tmpls_len += sizeof(*tmpl); + } else { + if (selp) + duparg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&req.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpinfo.sel.family; + } + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: \"DIR\" is required.\n"); + exit(1); + } + + if (tmpls_len > 0) { + addattr_l(&req.n, sizeof(req), XFRMA_TMPL, + (void *)tmpls_buf, tmpls_len); + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpinfo.sel.family == AF_UNSPEC) + req.xpinfo.sel.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_filter_match(struct xfrm_userpolicy_info *xpinfo) +{ + if (!filter.use) + return 1; + + if ((xpinfo->dir^filter.xpinfo.dir)&filter.dir_mask) + return 0; + + if (filter.sel_src_mask) { + if (xfrm_addr_match(&xpinfo->sel.saddr, &filter.xpinfo.sel.saddr, + filter.sel_src_mask)) + return 0; + } + + if (filter.sel_dst_mask) { + if (xfrm_addr_match(&xpinfo->sel.daddr, &filter.xpinfo.sel.daddr, + filter.sel_dst_mask)) + return 0; + } + + if ((xpinfo->sel.ifindex^filter.xpinfo.sel.ifindex)&filter.sel_dev_mask) + return 0; + + if ((xpinfo->sel.proto^filter.xpinfo.sel.proto)&filter.upspec_proto_mask) + return 0; + + if (filter.upspec_sport_mask) { + if ((xpinfo->sel.sport^filter.xpinfo.sel.sport)&filter.upspec_sport_mask) + return 0; + } + + if (filter.upspec_dport_mask) { + if ((xpinfo->sel.dport^filter.xpinfo.sel.dport)&filter.upspec_dport_mask) + return 0; + } + + if ((xpinfo->index^filter.xpinfo.index)&filter.index_mask) + return 0; + + if ((xpinfo->action^filter.xpinfo.action)&filter.action_mask) + return 0; + + if ((xpinfo->priority^filter.xpinfo.priority)&filter.priority_mask) + return 0; + + return 1; +} + +static int xfrm_policy_print(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[XFRMA_MAX+1]; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY && + n->nlmsg_type != XFRM_MSG_DELPOLICY) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xpinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_policy_filter_match(xpinfo)) + return 0; + + parse_rtattr(tb, XFRMA_MAX, XFRMP_RTA(xpinfo), len); + + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) + fprintf(fp, "Deleted "); + + xfrm_selector_print(&xpinfo->sel, preferred_family, fp, NULL); + + fprintf(fp, "\t"); + fprintf(fp, "dir "); + switch (xpinfo->dir) { + case XFRM_POLICY_IN: + fprintf(fp, "in"); + break; + case XFRM_POLICY_OUT: + fprintf(fp, "out"); + break; + case XFRM_POLICY_FWD: + fprintf(fp, "fwd"); + break; + default: + fprintf(fp, "%u", xpinfo->dir); + break; + } + fprintf(fp, " "); + + switch (xpinfo->action) { + case XFRM_POLICY_ALLOW: + if (show_stats > 0) + fprintf(fp, "action allow "); + break; + case XFRM_POLICY_BLOCK: + fprintf(fp, "action block "); + break; + default: + fprintf(fp, "action %u ", xpinfo->action); + break; + } + + if (show_stats) + fprintf(fp, "index %u ", xpinfo->index); + fprintf(fp, "priority %u ", xpinfo->priority); + if (show_stats > 0) { + fprintf(fp, "share %s ", strxf_share(xpinfo->share)); + fprintf(fp, "flag 0x%s", strxf_mask8(xpinfo->flags)); + } + fprintf(fp, "%s", _SL_); + + if (show_stats > 0) + xfrm_lifetime_print(&xpinfo->lft, &xpinfo->curlft, fp, "\t"); + + xfrm_xfrma_print(tb, xpinfo->sel.family, fp, "\t"); + + if (oneline) + fprintf(fp, "\n"); + + return 0; +} + +static int xfrm_policy_get_or_delete(int argc, char **argv, int delete, + void *res_nlbuf) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_id xpid; + } req; + char *dirp = NULL; + char *selp = NULL; + char *indexp = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY : XFRM_MSG_GETPOLICY; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpid.dir, &argc, &argv); + + } else if (strcmp(*argv, "index") == 0) { + if (indexp) + duparg("index", *argv); + indexp = *argv; + + NEXT_ARG(); + if (get_u32(&req.xpid.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + } else { + if (selp) + invarg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&req.xpid.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpid.sel.family; + + } + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: \"DIR\" is required.\n"); + exit(1); + } + if (!selp && !indexp) { + fprintf(stderr, "Not enough information: either \"SELECTOR\" or \"INDEX\" is required.\n"); + exit(1); + } + if (selp && indexp) + duparg2("SELECTOR", "INDEX"); + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpid.sel.family == AF_UNSPEC) + req.xpid.sel.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, res_nlbuf, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_delete(int argc, char **argv) +{ + return xfrm_policy_get_or_delete(argc, argv, 1, NULL); +} + +static int xfrm_policy_get(int argc, char **argv) +{ + char buf[NLMSG_BUF_SIZE]; + struct nlmsghdr *n = (struct nlmsghdr *)buf; + + memset(buf, 0, sizeof(buf)); + + xfrm_policy_get_or_delete(argc, argv, 0, n); + + if (xfrm_policy_print(NULL, n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + return 0; +} + +/* + * With an existing policy of nlmsg, make new nlmsg for deleting the policy + * and store it to buffer. + */ +static int xfrm_policy_keep(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct nlmsghdr *new_n; + struct xfrm_userpolicy_id *xpid; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xpinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_policy_filter_match(xpinfo)) + return 0; + + if (xb->offset > xb->size) { + fprintf(stderr, "Flush buffer overflow\n"); + return -1; + } + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xpid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELPOLICY; + new_n->nlmsg_seq = ++rth->seq; + + xpid = NLMSG_DATA(new_n); + memcpy(&xpid->sel, &xpinfo->sel, sizeof(xpid->sel)); + xpid->dir = xpinfo->dir; + xpid->index = xpinfo->index; + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count ++; + + return 0; +} + +static int xfrm_policy_list_or_flush(int argc, char **argv, int flush) +{ + char *selp = NULL; + struct rtnl_handle rth; + + if (argc > 0) + filter.use = 1; + filter.xpinfo.sel.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + NEXT_ARG(); + xfrm_policy_dir_parse(&filter.xpinfo.dir, &argc, &argv); + + filter.dir_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + filter.index_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + filter.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + filter.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("\"ACTION\" is invalid\n", *argv); + + filter.action_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.priority, *argv, 0)) + invarg("\"PRIORITY\" is invalid", *argv); + + filter.priority_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (selp) + invarg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&filter.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xpinfo.sel.family; + + } + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (flush) { + struct xfrm_buffer xb; + char buf[NLMSG_FLUSH_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Flush round = %d\n", i); + + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_keep, &xb, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Flush completed\n"); + break; + } + + if (rtnl_send(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send flush request\n"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Flushed nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + } else { + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_print, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +static int xfrm_policy_flush_all(void) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(0); /* nlmsg data is nothing */ + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_FLUSHPOLICY; + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (show_stats > 1) + fprintf(stderr, "Flush all\n"); + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +int do_xfrm_policy(int argc, char **argv) +{ + if (argc < 1) + return xfrm_policy_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_policy_modify(XFRM_MSG_UPDPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "delete") == 0 || matches(*argv, "del") == 0) + return xfrm_policy_delete(argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_policy_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_policy_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) { + if (argc-1 < 1) + return xfrm_policy_flush_all(); + else + return xfrm_policy_list_or_flush(argc-1, argv+1, 1); + } + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm policy help\".\n", *argv); + exit(-1); +} diff --git a/ip/xfrm_policy.o b/ip/xfrm_policy.o new file mode 100644 index 0000000..34cbd9e Binary files /dev/null and b/ip/xfrm_policy.o differ diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c new file mode 100644 index 0000000..b5b6214 --- /dev/null +++ b/ip/xfrm_state.c @@ -0,0 +1,762 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <linux/xfrm.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +//#define NLMSG_FLUSH_BUF_SIZE (4096-512) +#define NLMSG_FLUSH_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_usersa_info + * rtattr + * rtattr + * ... (max count of rtattr is XFRM_MAX+1 + * + * each rtattr data = struct xfrm_algo(dynamic size) or xfrm_address_t + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_ALGO_KEY_BUF_SIZE 512 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n"); + fprintf(stderr, " [ reqid REQID ] [ replay-window SIZE ] [ flag FLAG-LIST ]\n"); + fprintf(stderr, " [ encap ENCAP ] [ sel SELECTOR ] [ LIMIT-LIST ]\n"); + fprintf(stderr, "Usage: ip xfrm state { delete | get } ID\n"); + fprintf(stderr, "Usage: ip xfrm state { flush | list } [ ID ] [ mode MODE ] [ reqid REQID ]\n"); + fprintf(stderr, " [ flag FLAG_LIST ]\n"); + + fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n"); + //fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n"); + fprintf(stderr, "XFRM_PROTO := [ "); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP)); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH)); + fprintf(stderr, "%s ", strxf_xfrmproto(IPPROTO_COMP)); + fprintf(stderr, "]\n"); + + //fprintf(stderr, "SPI - security parameter index(default=0)\n"); + + fprintf(stderr, "MODE := [ transport | tunnel ](default=transport)\n"); + //fprintf(stderr, "REQID - number(default=0)\n"); + + fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); + fprintf(stderr, "FLAG := [ noecn | decap-dscp ]\n"); + + fprintf(stderr, "ENCAP := ENCAP-TYPE SPORT DPORT OADDR\n"); + fprintf(stderr, "ENCAP-TYPE := espinudp | espinudp-nonike\n"); + + fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] | [ ALGO ]\n"); + fprintf(stderr, "ALGO := ALGO_TYPE ALGO_NAME ALGO_KEY\n"); + fprintf(stderr, "ALGO_TYPE := [ "); + fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT)); + fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_AUTH)); + fprintf(stderr, "%s ", strxf_algotype(XFRMA_ALG_COMP)); + fprintf(stderr, "]\n"); + + //fprintf(stderr, "ALGO_NAME - algorithm name\n"); + //fprintf(stderr, "ALGO_KEY - algorithm key\n"); + + fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ UPSPEC ] [ dev DEV ]\n"); + + fprintf(stderr, "UPSPEC := proto PROTO [ [ sport PORT ] [ dport PORT ] |\n"); + fprintf(stderr, " [ type NUMBER ] [ code NUMBER ] ]\n"); + + + //fprintf(stderr, "DEV - device name(default=none)\n"); + fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]\n"); + fprintf(stderr, "LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |\n"); + fprintf(stderr, " [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] COUNT ]\n"); + exit(-1); +} + +static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type, + char *name, char *key, int max) +{ + int len; + int slen = strlen(key); + +#if 0 + /* XXX: verifying both name and key is required! */ + fprintf(stderr, "warning: ALGONAME/ALGOKEY will send to kernel promiscuously!(verifying them isn't implemented yet)\n"); +#endif + + strncpy(alg->alg_name, name, sizeof(alg->alg_name)); + + if (slen > 2 && strncmp(key, "0x", 2) == 0) { + /* split two chars "0x" from the top */ + char *p = key + 2; + int plen = slen - 2; + int i; + int j; + + /* Converting hexadecimal numbered string into real key; + * Convert each two chars into one char(value). If number + * of the length is odd, add zero on the top for rounding. + */ + + /* calculate length of the converted values(real key) */ + len = (plen + 1) / 2; + if (len > max) + invarg("\"ALGOKEY\" makes buffer overflow\n", key); + + for (i = - (plen % 2), j = 0; j < len; i += 2, j++) { + char vbuf[3]; + char val; + + vbuf[0] = i >= 0 ? p[i] : '0'; + vbuf[1] = p[i + 1]; + vbuf[2] = '\0'; + + if (get_u8(&val, vbuf, 16)) + invarg("\"ALGOKEY\" is invalid", key); + + alg->alg_key[j] = val; + } + } else { + len = slen; + if (len > 0) { + if (len > max) + invarg("\"ALGOKEY\" makes buffer overflow\n", key); + + strncpy(alg->alg_key, key, len); + } + } + + alg->alg_key_len = len * 8; + + return 0; +} + +static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int len = strlen(*argv); + + if (len > 2 && strncmp(*argv, "0x", 2) == 0) { + __u8 val = 0; + + if (get_u8(&val, *argv, 16)) + invarg("\"FLAG\" is invalid", *argv); + *flags = val; + } else { + while (1) { + if (strcmp(*argv, "noecn") == 0) + *flags |= XFRM_STATE_NOECN; + else if (strcmp(*argv, "decap-dscp") == 0) + *flags |= XFRM_STATE_DECAP_DSCP; + else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + } + + filter.state_flags_mask = XFRM_FILTER_MASK_FULL; + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_info xsinfo; + char buf[RTA_BUF_SIZE]; + } req; + char *idp = NULL; + char *ealgop = NULL; + char *aalgop = NULL; + char *calgop = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.xsinfo.family = preferred_family; + + req.xsinfo.lft.soft_byte_limit = XFRM_INF; + req.xsinfo.lft.hard_byte_limit = XFRM_INF; + req.xsinfo.lft.soft_packet_limit = XFRM_INF; + req.xsinfo.lft.hard_packet_limit = XFRM_INF; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); + } else if (strcmp(*argv, "replay-window") == 0) { + NEXT_ARG(); + if (get_u8(&req.xsinfo.replay_window, *argv, 0)) + invarg("\"replay-window\" value is invalid", *argv); + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); + } else if (strcmp(*argv, "sel") == 0) { + NEXT_ARG(); + xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); + } else if (strcmp(*argv, "encap") == 0) { + struct xfrm_encap_tmpl encap; + inet_prefix oa; + NEXT_ARG(); + xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); + NEXT_ARG(); + if (get_u16(&encap.encap_sport, *argv, 0)) + invarg("\"encap\" sport value is invalid", *argv); + encap.encap_sport = htons(encap.encap_sport); + NEXT_ARG(); + if (get_u16(&encap.encap_dport, *argv, 0)) + invarg("\"encap\" dport value is invalid", *argv); + encap.encap_dport = htons(encap.encap_dport); + NEXT_ARG(); + get_addr(&oa, *argv, AF_UNSPEC); + memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); + addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, + (void *)&encap, sizeof(encap)); + } else { + /* try to assume ALGO */ + int type = xfrm_algotype_getbyname(*argv); + switch (type) { + case XFRMA_ALG_CRYPT: + case XFRMA_ALG_AUTH: + case XFRMA_ALG_COMP: + { + /* ALGO */ + struct { + struct xfrm_algo alg; + char buf[XFRM_ALGO_KEY_BUF_SIZE]; + } alg; + int len; + char *name; + char *key; + + switch (type) { + case XFRMA_ALG_CRYPT: + if (ealgop) + duparg("ALGOTYPE", *argv); + ealgop = *argv; + break; + case XFRMA_ALG_AUTH: + if (aalgop) + duparg("ALGOTYPE", *argv); + aalgop = *argv; + break; + case XFRMA_ALG_COMP: + if (calgop) + duparg("ALGOTYPE", *argv); + calgop = *argv; + break; + default: + /* not reached */ + invarg("\"ALGOTYPE\" is invalid\n", *argv); + } + + if (!NEXT_ARG_OK()) + missarg("ALGONAME"); + NEXT_ARG(); + name = *argv; + + if (!NEXT_ARG_OK()) + missarg("ALGOKEY"); + NEXT_ARG(); + key = *argv; + + memset(&alg, 0, sizeof(alg)); + + xfrm_algo_parse((void *)&alg, type, name, key, + sizeof(alg.buf)); + len = sizeof(struct xfrm_algo) + alg.alg.alg_key_len; + + addattr_l(&req.n, sizeof(req.buf), type, + (void *)&alg, len); + break; + } + default: + /* try to assume ID */ + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, + &req.xsinfo.family, 0, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xsinfo.family; + } + } + argc--; argv++; + } + + if (!idp) { + fprintf(stderr, "Not enough information: \"ID\" is required\n"); + exit(1); + } + + if (ealgop || aalgop || calgop) { + if (req.xsinfo.id.proto != IPPROTO_ESP && + req.xsinfo.id.proto != IPPROTO_AH && + req.xsinfo.id.proto != IPPROTO_COMP) { + fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + } else { + if (req.xsinfo.id.proto == IPPROTO_ESP || + req.xsinfo.id.proto == IPPROTO_AH || + req.xsinfo.id.proto == IPPROTO_COMP) { + fprintf(stderr, "\"ALGO\" is required with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); + exit (1); + } + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsinfo.family == AF_UNSPEC) + req.xsinfo.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo) +{ + if (!filter.use) + return 1; + + if (filter.id_src_mask) + if (xfrm_addr_match(&xsinfo->saddr, &filter.xsinfo.saddr, + filter.id_src_mask)) + return 0; + if (filter.id_dst_mask) + if (xfrm_addr_match(&xsinfo->id.daddr, &filter.xsinfo.id.daddr, + filter.id_dst_mask)) + return 0; + if ((xsinfo->id.proto^filter.xsinfo.id.proto)&filter.id_proto_mask) + return 0; + if ((xsinfo->id.spi^filter.xsinfo.id.spi)&filter.id_spi_mask) + return 0; + if ((xsinfo->mode^filter.xsinfo.mode)&filter.mode_mask) + return 0; + if ((xsinfo->reqid^filter.xsinfo.reqid)&filter.reqid_mask) + return 0; + if (filter.state_flags_mask) + if ((xsinfo->flags & filter.xsinfo.flags) == 0) + return 0; + + return 1; +} + +static int xfrm_selector_iszero(struct xfrm_selector *s) +{ + struct xfrm_selector s0; + + memset(&s0, 0, sizeof(s0)); + + return (memcmp(&s0, s, sizeof(s0)) == 0); +} + +static int xfrm_state_print(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[XFRMA_MAX+1]; + + if (n->nlmsg_type != XFRM_MSG_NEWSA && + n->nlmsg_type != XFRM_MSG_DELSA) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xsinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_state_filter_match(xsinfo)) + return 0; + + parse_rtattr(tb, XFRMA_MAX, XFRMS_RTA(xsinfo), len); + + if (n->nlmsg_type == XFRM_MSG_DELSA) + fprintf(fp, "Deleted "); + + xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode, + xsinfo->reqid, xsinfo->family, 1, fp, NULL); + + fprintf(fp, "\t"); + fprintf(fp, "replay-window %u ", xsinfo->replay_window); + if (show_stats > 0) + fprintf(fp, "seq 0x%08u ", xsinfo->seq); + if (show_stats > 0 || xsinfo->flags) { + __u8 flags = xsinfo->flags; + + fprintf(fp, "flag "); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_NOECN, "noecn"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_DECAP_DSCP, "decap-dscp"); + if (flags) + fprintf(fp, "%x", flags); + if (show_stats > 0) + fprintf(fp, " (0x%s)", strxf_mask8(flags)); + } + fprintf(fp, "%s", _SL_); + + xfrm_xfrma_print(tb, xsinfo->family, fp, "\t"); + + if (!xfrm_selector_iszero(&xsinfo->sel)) + xfrm_selector_print(&xsinfo->sel, xsinfo->family, fp, "\tsel "); + + if (show_stats > 0) { + xfrm_lifetime_print(&xsinfo->lft, &xsinfo->curlft, fp, "\t"); + xfrm_stats_print(&xsinfo->stats, fp, "\t"); + } + + if (oneline) + fprintf(fp, "\n"); + + return 0; +} + +static int xfrm_state_get_or_delete(int argc, char **argv, int delete) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_id xsid; + } req; + struct xfrm_id id; + char *idp = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA; + req.xsid.family = preferred_family; + + while (argc > 0) { + /* + * XXX: Source address is not used and ignore it to follow + * XXX: a manner of setkey e.g. in the case of deleting/getting + * XXX: message of IPsec SA. + */ + xfrm_address_t ignore_saddr; + + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + memset(&id, 0, sizeof(id)); + xfrm_id_parse(&ignore_saddr, &id, &req.xsid.family, 0, + &argc, &argv); + + memcpy(&req.xsid.daddr, &id.daddr, sizeof(req.xsid.daddr)); + req.xsid.spi = id.spi; + req.xsid.proto = id.proto; + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsid.family == AF_UNSPEC) + req.xsid.family = AF_INET; + + if (delete) { + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + } else { + char buf[NLMSG_BUF_SIZE]; + struct nlmsghdr *res_n = (struct nlmsghdr *)buf; + + memset(buf, 0, sizeof(buf)); + + if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0) + exit(2); + + if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + } + + rtnl_close(&rth); + + return 0; +} + +/* + * With an existing state of nlmsg, make new nlmsg for deleting the state + * and store it to buffer. + */ +static int xfrm_state_keep(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct nlmsghdr *new_n; + struct xfrm_usersa_id *xsid; + + if (n->nlmsg_type != XFRM_MSG_NEWSA) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xsinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_state_filter_match(xsinfo)) + return 0; + + if (xb->offset > xb->size) { + fprintf(stderr, "Flush buffer overflow\n"); + return -1; + } + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xsid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELSA; + new_n->nlmsg_seq = ++rth->seq; + + xsid = NLMSG_DATA(new_n); + xsid->family = xsinfo->family; + memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr)); + xsid->spi = xsinfo->id.spi; + xsid->proto = xsinfo->id.proto; + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count ++; + + return 0; +} + +static int xfrm_state_list_or_flush(int argc, char **argv, int flush) +{ + char *idp = NULL; + struct rtnl_handle rth; + + if(argc > 0) + filter.use = 1; + filter.xsinfo.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv); + + filter.mode_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&filter.xsinfo.reqid, &argc, &argv); + + filter.reqid_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&filter.xsinfo.flags, &argc, &argv); + + filter.state_flags_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&filter.xsinfo.saddr, &filter.xsinfo.id, + &filter.xsinfo.family, 1, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xsinfo.family; + } + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (flush) { + struct xfrm_buffer xb; + char buf[NLMSG_FLUSH_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Flush round = %d\n", i); + + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Flush completed\n"); + break; + } + + if (rtnl_send(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send flush request\n"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Flushed nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + + } else { + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_state_print, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +static int xfrm_state_flush_all(void) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_flush xsf; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_FLUSHSA; + req.xsf.proto = IPSEC_PROTO_ANY; + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (show_stats > 1) + fprintf(stderr, "Flush all\n"); + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +int do_xfrm_state(int argc, char **argv) +{ + if (argc < 1) + return xfrm_state_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_state_modify(XFRM_MSG_NEWSA, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_state_modify(XFRM_MSG_UPDSA, 0, + argc-1, argv+1); + if (matches(*argv, "delete") == 0 || matches(*argv, "del") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_state_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) { + if (argc-1 < 1) + return xfrm_state_flush_all(); + else + return xfrm_state_list_or_flush(argc-1, argv+1, 1); + } + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv); + exit(-1); +} diff --git a/ip/xfrm_state.o b/ip/xfrm_state.o new file mode 100644 index 0000000..8fa15f7 Binary files /dev/null and b/ip/xfrm_state.o differ diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..bc270bf --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,18 @@ + +UTILOBJ=utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o inet_proto.o + +NLOBJ=ll_map.o libnetlink.o + +all: libnetlink.a libutil.a + +libnetlink.a: $(NLOBJ) + $(AR) rcs $@ $(NLOBJ) + +libutil.a: $(UTILOBJ) $(ADDLIB) + $(AR) rcs $@ $(UTILOBJ) $(ADDLIB) + +install: + +clean: + rm -f $(NLOBJ) $(UTILOBJ) $(ADDLIB) libnetlink.a libutil.a + diff --git a/lib/dnet_ntop.c b/lib/dnet_ntop.c new file mode 100644 index 0000000..9500df8 --- /dev/null +++ b/lib/dnet_ntop.c @@ -0,0 +1,98 @@ +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static __inline__ u_int16_t dn_ntohs(u_int16_t addr) +{ + union { + u_int8_t byte[2]; + u_int16_t word; + } u; + + u.word = addr; + return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8); +} + +static __inline__ int do_digit(char *str, u_int16_t *addr, u_int16_t scale, size_t *pos, size_t len, int *started) +{ + u_int16_t tmp = *addr / scale; + + if (*pos == len) + return 1; + + if (((tmp) > 0) || *started || (scale == 1)) { + *str = tmp + '0'; + *started = 1; + (*pos)++; + *addr -= (tmp * scale); + } + + return 0; +} + + +static const char *dnet_ntop1(const struct dn_naddr *dna, char *str, size_t len) +{ + u_int16_t addr = dn_ntohs(*(u_int16_t *)dna->a_addr); + u_int16_t area = addr >> 10; + size_t pos = 0; + int started = 0; + + if (dna->a_len != 2) + return NULL; + + addr &= 0x03ff; + + if (len == 0) + return str; + + if (do_digit(str + pos, &area, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &area, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = '.'; + pos++; + started = 0; + + if (do_digit(str + pos, &addr, 1000, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 100, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = 0; + + return str; +} + + +const char *dnet_ntop(int af, const void *addr, char *str, size_t len) +{ + switch(af) { + case AF_DECnet: + errno = 0; + return dnet_ntop1((struct dn_naddr *)addr, str, len); + default: + errno = EAFNOSUPPORT; + } + + return NULL; +} + + diff --git a/lib/dnet_ntop.o b/lib/dnet_ntop.o new file mode 100644 index 0000000..8cb237d Binary files /dev/null and b/lib/dnet_ntop.o differ diff --git a/lib/dnet_pton.c b/lib/dnet_pton.c new file mode 100644 index 0000000..bd7727a --- /dev/null +++ b/lib/dnet_pton.c @@ -0,0 +1,71 @@ +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static __inline__ u_int16_t dn_htons(u_int16_t addr) +{ + union { + u_int8_t byte[2]; + u_int16_t word; + } u; + + u.word = addr; + return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8); +} + + +static int dnet_num(const char *src, u_int16_t * dst) +{ + int rv = 0; + int tmp; + *dst = 0; + + while ((tmp = *src++) != 0) { + tmp -= '0'; + if ((tmp < 0) || (tmp > 9)) + return rv; + + rv++; + (*dst) *= 10; + (*dst) += tmp; + } + + return rv; +} + +static int dnet_pton1(const char *src, struct dn_naddr *dna) +{ + u_int16_t area = 0; + u_int16_t node = 0; + int pos; + + pos = dnet_num(src, &area); + if ((pos == 0) || (area > 63) || (*(src + pos) != '.')) + return 0; + pos = dnet_num(src + pos + 1, &node); + if ((pos == 0) || (node > 1023)) + return 0; + dna->a_len = 2; + *(u_int16_t *)dna->a_addr = dn_htons((area << 10) | node); + + return 1; +} + +int dnet_pton(int af, const char *src, void *addr) +{ + int err; + + switch (af) { + case AF_DECnet: + errno = 0; + err = dnet_pton1(src, (struct dn_naddr *)addr); + break; + default: + errno = EAFNOSUPPORT; + err = -1; + } + + return err; +} diff --git a/lib/dnet_pton.o b/lib/dnet_pton.o new file mode 100644 index 0000000..e784a7e Binary files /dev/null and b/lib/dnet_pton.o differ diff --git a/lib/inet_proto.c b/lib/inet_proto.c new file mode 100644 index 0000000..a55e0e7 --- /dev/null +++ b/lib/inet_proto.c @@ -0,0 +1,70 @@ +/* + * inet_proto.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <string.h> + +#include "utils.h" + +char *inet_proto_n2a(int proto, char *buf, int len) +{ + static char ncache[16]; + static int icache = -1; + struct protoent *pe; + + if (proto == icache) + return ncache; + + pe = getprotobynumber(proto); + if (pe) { + icache = proto; + strncpy(ncache, pe->p_name, 16); + strncpy(buf, pe->p_name, len); + return buf; + } + snprintf(buf, len, "ipproto-%d", proto); + return buf; +} + +int inet_proto_a2n(char *buf) +{ + static char ncache[16]; + static int icache = -1; + struct protoent *pe; + + if (icache>=0 && strcmp(ncache, buf) == 0) + return icache; + + if (buf[0] >= '0' && buf[0] <= '9') { + __u8 ret; + if (get_u8(&ret, buf, 10)) + return -1; + return ret; + } + + pe = getprotobyname(buf); + if (pe) { + icache = pe->p_proto; + strncpy(ncache, pe->p_name, 16); + return pe->p_proto; + } + return -1; +} + + diff --git a/lib/inet_proto.o b/lib/inet_proto.o new file mode 100644 index 0000000..d7d93e1 Binary files /dev/null and b/lib/inet_proto.o differ diff --git a/lib/ipx_ntop.c b/lib/ipx_ntop.c new file mode 100644 index 0000000..b2d6790 --- /dev/null +++ b/lib/ipx_ntop.c @@ -0,0 +1,71 @@ +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static __inline__ int do_digit(char *str, u_int32_t addr, u_int32_t scale, size_t *pos, size_t len) +{ + u_int32_t tmp = addr >> (scale * 4); + + if (*pos == len) + return 1; + + tmp &= 0x0f; + if (tmp > 9) + *str = tmp + 'A' - 10; + else + *str = tmp + '0'; + (*pos)++; + + return 0; +} + +static const char *ipx_ntop1(const struct ipx_addr *addr, char *str, size_t len) +{ + int i; + size_t pos = 0; + + if (len == 0) + return str; + + for(i = 7; i >= 0; i--) + if (do_digit(str + pos, ntohl(addr->ipx_net), i, &pos, len)) + return str; + + if (pos == len) + return str; + + *(str + pos) = '.'; + pos++; + + for(i = 0; i < 6; i++) { + if (do_digit(str + pos, addr->ipx_node[i], 1, &pos, len)) + return str; + if (do_digit(str + pos, addr->ipx_node[i], 0, &pos, len)) + return str; + } + + if (pos == len) + return str; + + *(str + pos) = 0; + + return str; +} + + +const char *ipx_ntop(int af, const void *addr, char *str, size_t len) +{ + switch(af) { + case AF_IPX: + errno = 0; + return ipx_ntop1((struct ipx_addr *)addr, str, len); + default: + errno = EAFNOSUPPORT; + } + + return NULL; +} + + diff --git a/lib/ipx_ntop.o b/lib/ipx_ntop.o new file mode 100644 index 0000000..03effc2 Binary files /dev/null and b/lib/ipx_ntop.o differ diff --git a/lib/ipx_pton.c b/lib/ipx_pton.c new file mode 100644 index 0000000..1a52b7f --- /dev/null +++ b/lib/ipx_pton.c @@ -0,0 +1,107 @@ +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static u_int32_t hexget(char c) +{ + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + + return 0xf0; +} + +static int ipx_getnet(u_int32_t *net, const char *str) +{ + int i; + u_int32_t tmp; + + for(i = 0; *str && (i < 8); i++) { + + if ((tmp = hexget(*str)) & 0xf0) { + if (*str == '.') + return 0; + else + return -1; + } + + str++; + (*net) <<= 4; + (*net) |= tmp; + } + + if (*str == 0) + return 0; + + return -1; +} + +static int ipx_getnode(u_int8_t *node, const char *str) +{ + int i; + u_int32_t tmp; + + for(i = 0; i < 6; i++) { + if ((tmp = hexget(*str++)) & 0xf0) + return -1; + node[i] = (u_int8_t)tmp; + node[i] <<= 4; + if ((tmp = hexget(*str++)) & 0xf0) + return -1; + node[i] |= (u_int8_t)tmp; + if (*str == ':') + str++; + } + + return 0; +} + +static int ipx_pton1(const char *src, struct ipx_addr *addr) +{ + char *sep = (char *)src; + int no_node = 0; + + memset(addr, 0, sizeof(struct ipx_addr)); + + while(*sep && (*sep != '.')) + sep++; + + if (*sep != '.') + no_node = 1; + + if (ipx_getnet(&addr->ipx_net, src)) + return 0; + + addr->ipx_net = htonl(addr->ipx_net); + + if (no_node) + return 1; + + if (ipx_getnode(addr->ipx_node, sep + 1)) + return 0; + + return 1; +} + +int ipx_pton(int af, const char *src, void *addr) +{ + int err; + + switch (af) { + case AF_IPX: + errno = 0; + err = ipx_pton1(src, (struct ipx_addr *)addr); + break; + default: + errno = EAFNOSUPPORT; + err = -1; + } + + return err; +} diff --git a/lib/ipx_pton.o b/lib/ipx_pton.o new file mode 100644 index 0000000..e6064cc Binary files /dev/null and b/lib/ipx_pton.o differ diff --git a/lib/libnetlink.a b/lib/libnetlink.a new file mode 100644 index 0000000..b4edfee Binary files /dev/null and b/lib/libnetlink.a differ diff --git a/lib/libnetlink.c b/lib/libnetlink.c new file mode 100644 index 0000000..4cd2b2a --- /dev/null +++ b/lib/libnetlink.c @@ -0,0 +1,580 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <net/if_arp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/uio.h> + +#include "libnetlink.h" + +void rtnl_close(struct rtnl_handle *rth) +{ + close(rth->fd); +} + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol) +{ + int addr_len; + int sndbuf = 32768; + int rcvbuf = 32768; + + memset(rth, 0, sizeof(rth)); + + rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (rth->fd < 0) { + perror("Cannot open netlink socket"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + perror("SO_RCVBUF"); + return -1; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + perror("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + perror("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + fprintf(stderr, "Wrong address length %d\n", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.g.rtgen_family = family; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_send(struct rtnl_handle *rth, const char *buf, int len) +{ + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr; + struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + iov, 2, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_filter(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2) +{ + char buf[16384]; + struct sockaddr_nl nladdr; + struct iovec iov = { buf, sizeof(buf) }; + + while (1) { + int status; + struct nlmsghdr *h; + + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + exit(1); + } + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) { + if (junk) { + err = junk(&nladdr, h, arg2); + if (err < 0) + return err; + } + goto skip_it; + } + + if (h->nlmsg_type == NLMSG_DONE) + return 0; + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("RTNETLINK answers"); + } + return -1; + } + err = filter(&nladdr, h, arg1); + if (err < 0) + return err; + +skip_it: + h = NLMSG_NEXT(h, status); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + rtnl_filter_t junk, + void *jarg) +{ + int status; + unsigned seq; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov = { (void*)n, n->nlmsg_len }; + char buf[16384]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + n->nlmsg_seq = seq = ++rtnl->seq; + + if (answer == NULL) + n->nlmsg_flags |= NLM_F_ACK; + + status = sendmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + perror("Cannot talk to rtnetlink"); + return -1; + } + + memset(buf,0,sizeof(buf)); + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + if (nladdr.nl_pid != peer || + h->nlmsg_pid != rtnl->local.nl_pid || + h->nlmsg_seq != seq) { + if (junk) { + err = junk(&nladdr, h, jarg); + if (err < 0) + return err; + } + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + if (errno == 0) { + if (answer) + memcpy(answer, h, h->nlmsg_len); + return 0; + } + perror("RTNETLINK answers"); + } + return -1; + } + if (answer) { + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + fprintf(stderr, "Unexpected reply!!!\n"); + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_listen(struct rtnl_handle *rtnl, + rtnl_filter_t handler, + void *jarg) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + char buf[8192]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler, + void *jarg) +{ + int status; + struct sockaddr_nl nladdr; + char buf[8192]; + struct nlmsghdr *h = (void*)buf; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + while (1) { + int err, len, type; + int l; + + status = fread(&buf, 1, sizeof(*h), rtnl); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("rtnl_from_file: fread"); + return -1; + } + if (status == 0) + return 0; + + len = h->nlmsg_len; + type= h->nlmsg_type; + l = len - sizeof(*h); + + if (l<0 || len>sizeof(buf)) { + fprintf(stderr, "!!!malformed message: len=%d @%lu\n", + len, ftell(rtnl)); + return -1; + } + + status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); + + if (status < 0) { + perror("rtnl_from_file: fread"); + return -1; + } + if (status < l) { + fprintf(stderr, "rtnl-from_file: truncated message\n"); + return -1; + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + } +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *rta; + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) +{ + if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { + fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + + memcpy(NLMSG_TAIL(n), data, len); + memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len); + return 0; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, + const void *data, int alen) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return 0; +} + +int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + int i = 0; + + memset(tb, 0, sizeof(struct rtattr *) * max); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max && i < max) + tb[i++] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return i; +} diff --git a/lib/libnetlink.o b/lib/libnetlink.o new file mode 100644 index 0000000..01f8068 Binary files /dev/null and b/lib/libnetlink.o differ diff --git a/lib/libutil.a b/lib/libutil.a new file mode 100644 index 0000000..effedc9 Binary files /dev/null and b/lib/libutil.a differ diff --git a/lib/ll_addr.c b/lib/ll_addr.c new file mode 100644 index 0000000..ea3d660 --- /dev/null +++ b/lib/ll_addr.c @@ -0,0 +1,93 @@ +/* + * ll_addr.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" + + +const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen) +{ + int i; + int l; + + if (alen == 4 && + (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) { + return inet_ntop(AF_INET, addr, buf, blen); + } + l = 0; + for (i=0; i<alen; i++) { + if (i==0) { + snprintf(buf+l, blen, "%02x", addr[i]); + blen -= 2; + l += 2; + } else { + snprintf(buf+l, blen, ":%02x", addr[i]); + blen -= 3; + l += 3; + } + } + return buf; +} + +int ll_addr_a2n(unsigned char *lladdr, int len, char *arg) +{ + if (strchr(arg, '.')) { + inet_prefix pfx; + if (get_addr_1(&pfx, arg, AF_INET)) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); + return -1; + } + if (len < 4) + return -1; + memcpy(lladdr, pfx.data, 4); + return 4; + } else { + int i; + + for (i=0; i<len; i++) { + int temp; + char *cp = strchr(arg, ':'); + if (cp) { + *cp = 0; + cp++; + } + if (sscanf(arg, "%x", &temp) != 1) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); + return -1; + } + if (temp < 0 || temp > 255) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); + return -1; + } + lladdr[i] = temp; + if (!cp) + break; + arg = cp; + } + return i+1; + } +} diff --git a/lib/ll_addr.o b/lib/ll_addr.o new file mode 100644 index 0000000..12a2dea Binary files /dev/null and b/lib/ll_addr.o differ diff --git a/lib/ll_map.c b/lib/ll_map.c new file mode 100644 index 0000000..89c0d20 --- /dev/null +++ b/lib/ll_map.c @@ -0,0 +1,170 @@ +/* + * ll_map.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> + +#include "libnetlink.h" +#include "ll_map.h" + +struct idxmap +{ + struct idxmap * next; + int index; + int type; + int alen; + unsigned flags; + unsigned char addr[8]; + char name[16]; +}; + +static struct idxmap *idxmap[16]; + +int ll_remember_index(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + int h; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct idxmap *im, **imp; + struct rtattr *tb[IFLA_MAX+1]; + + if (n->nlmsg_type != RTM_NEWLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) + return -1; + + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + if (tb[IFLA_IFNAME] == NULL) + return 0; + + h = ifi->ifi_index&0xF; + + for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next) + if (im->index == ifi->ifi_index) + break; + + if (im == NULL) { + im = malloc(sizeof(*im)); + if (im == NULL) + return 0; + im->next = *imp; + im->index = ifi->ifi_index; + *imp = im; + } + + im->type = ifi->ifi_type; + im->flags = ifi->ifi_flags; + if (tb[IFLA_ADDRESS]) { + int alen; + im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]); + if (alen > sizeof(im->addr)) + alen = sizeof(im->addr); + memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen); + } else { + im->alen = 0; + memset(im->addr, 0, sizeof(im->addr)); + } + strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME])); + return 0; +} + +const char *ll_idx_n2a(int idx, char *buf) +{ + struct idxmap *im; + + if (idx == 0) + return "*"; + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->name; + snprintf(buf, 16, "if%d", idx); + return buf; +} + + +const char *ll_index_to_name(int idx) +{ + static char nbuf[16]; + + return ll_idx_n2a(idx, nbuf); +} + +int ll_index_to_type(int idx) +{ + struct idxmap *im; + + if (idx == 0) + return -1; + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->type; + return -1; +} + +unsigned ll_index_to_flags(int idx) +{ + struct idxmap *im; + + if (idx == 0) + return 0; + + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->flags; + return 0; +} + +int ll_name_to_index(const char *name) +{ + static char ncache[16]; + static int icache; + struct idxmap *im; + int i; + + if (name == NULL) + return 0; + if (icache && strcmp(name, ncache) == 0) + return icache; + for (i=0; i<16; i++) { + for (im = idxmap[i]; im; im = im->next) { + if (strcmp(im->name, name) == 0) { + icache = im->index; + strcpy(ncache, name); + return im->index; + } + } + } + return 0; +} + +int ll_init_map(struct rtnl_handle *rth) +{ + if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + return 0; +} diff --git a/lib/ll_map.o b/lib/ll_map.o new file mode 100644 index 0000000..62facd9 Binary files /dev/null and b/lib/ll_map.o differ diff --git a/lib/ll_proto.c b/lib/ll_proto.c new file mode 100644 index 0000000..98c67fe --- /dev/null +++ b/lib/ll_proto.c @@ -0,0 +1,129 @@ +/* + * ll_proto.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "utils.h" +#include "rt_names.h" + + +#define __PF(f,n) { ETH_P_##f, #n }, +static struct { + int id; + const char *name; +} llproto_names[] = { +__PF(LOOP,loop) +__PF(PUP,pup) +#ifdef ETH_P_PUPAT +__PF(PUPAT,pupat) +#endif +__PF(IP,ip) +__PF(X25,x25) +__PF(ARP,arp) +__PF(BPQ,bpq) +#ifdef ETH_P_IEEEPUP +__PF(IEEEPUP,ieeepup) +#endif +#ifdef ETH_P_IEEEPUPAT +__PF(IEEEPUPAT,ieeepupat) +#endif +__PF(DEC,dec) +__PF(DNA_DL,dna_dl) +__PF(DNA_RC,dna_rc) +__PF(DNA_RT,dna_rt) +__PF(LAT,lat) +__PF(DIAG,diag) +__PF(CUST,cust) +__PF(SCA,sca) +__PF(RARP,rarp) +__PF(ATALK,atalk) +__PF(AARP,aarp) +__PF(IPX,ipx) +__PF(IPV6,ipv6) +#ifdef ETH_P_PPP_DISC +__PF(PPP_DISC,ppp_disc) +#endif +#ifdef ETH_P_PPP_SES +__PF(PPP_SES,ppp_ses) +#endif +#ifdef ETH_P_ATMMPOA +__PF(ATMMPOA,atmmpoa) +#endif +#ifdef ETH_P_ATMFATE +__PF(ATMFATE,atmfate) +#endif + +__PF(802_3,802_3) +__PF(AX25,ax25) +__PF(ALL,all) +__PF(802_2,802_2) +__PF(SNAP,snap) +__PF(DDCMP,ddcmp) +__PF(WAN_PPP,wan_ppp) +__PF(PPP_MP,ppp_mp) +__PF(LOCALTALK,localtalk) +__PF(PPPTALK,ppptalk) +__PF(TR_802_2,tr_802_2) +__PF(MOBITEX,mobitex) +__PF(CONTROL,control) +__PF(IRDA,irda) +#ifdef ETH_P_ECONET +__PF(ECONET,econet) +#endif + +{ 0x8100, "802.1Q" }, +{ ETH_P_IP, "ipv4" }, +}; +#undef __PF + + +const char * ll_proto_n2a(unsigned short id, char *buf, int len) +{ + int i; + + id = ntohs(id); + + for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { + if (llproto_names[i].id == id) + return llproto_names[i].name; + } + snprintf(buf, len, "[%d]", id); + return buf; +} + +int ll_proto_a2n(unsigned short *id, char *buf) +{ + int i; + for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { + if (strcasecmp(llproto_names[i].name, buf) == 0) { + *id = htons(llproto_names[i].id); + return 0; + } + } + if (get_u16(id, buf, 0)) + return -1; + *id = htons(*id); + return 0; +} diff --git a/lib/ll_proto.o b/lib/ll_proto.o new file mode 100644 index 0000000..491146c Binary files /dev/null and b/lib/ll_proto.o differ diff --git a/lib/ll_types.c b/lib/ll_types.c new file mode 100644 index 0000000..5b0f106 --- /dev/null +++ b/lib/ll_types.c @@ -0,0 +1,134 @@ +/* + * ll_types.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" + +const char * ll_type_n2a(int type, char *buf, int len) +{ +#define __PF(f,n) { ARPHRD_##f, #n }, +static struct { + int type; + const char *name; +} arphrd_names[] = { +{ 0, "generic" }, +__PF(ETHER,ether) +__PF(EETHER,eether) +__PF(AX25,ax25) +__PF(PRONET,pronet) +__PF(CHAOS,chaos) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802,ieee802) +#else +__PF(IEEE802,tr) +#endif +__PF(ARCNET,arcnet) +__PF(APPLETLK,atalk) +__PF(DLCI,dlci) +#ifdef ARPHRD_ATM +__PF(ATM,atm) +#endif +__PF(METRICOM,metricom) +#ifdef ARPHRD_IEEE1394 +__PF(IEEE1394,ieee1394) +#endif +#ifdef ARPHRD_INFINIBAND +__PF(INFINIBAND,infiniband) +#endif + +__PF(SLIP,slip) +__PF(CSLIP,cslip) +__PF(SLIP6,slip6) +__PF(CSLIP6,cslip6) +__PF(RSRVD,rsrvd) +__PF(ADAPT,adapt) +__PF(ROSE,rose) +__PF(X25,x25) +#ifdef ARPHRD_HWX25 +__PF(HWX25,hwx25) +#endif +__PF(PPP,ppp) +__PF(HDLC,hdlc) +__PF(LAPB,lapb) +#ifdef ARPHRD_DDCMP +__PF(DDCMP,ddcmp) +#endif +#ifdef ARPHRD_RAWHDLC +__PF(RAWHDLC,rawhdlc) +#endif + +__PF(TUNNEL,ipip) +__PF(TUNNEL6,tunnel6) +__PF(FRAD,frad) +__PF(SKIP,skip) +__PF(LOOPBACK,loopback) +__PF(LOCALTLK,ltalk) +__PF(FDDI,fddi) +__PF(BIF,bif) +__PF(SIT,sit) +__PF(IPDDP,ip/ddp) +__PF(IPGRE,gre) +__PF(PIMREG,pimreg) +__PF(HIPPI,hippi) +__PF(ASH,ash) +__PF(ECONET,econet) +__PF(IRDA,irda) +__PF(FCPP,fcpp) +__PF(FCAL,fcal) +__PF(FCPL,fcpl) +__PF(FCFABRIC,fcfb0) +__PF(FCFABRIC+1,fcfb1) +__PF(FCFABRIC+2,fcfb2) +__PF(FCFABRIC+3,fcfb3) +__PF(FCFABRIC+4,fcfb4) +__PF(FCFABRIC+5,fcfb5) +__PF(FCFABRIC+6,fcfb6) +__PF(FCFABRIC+7,fcfb7) +__PF(FCFABRIC+8,fcfb8) +__PF(FCFABRIC+9,fcfb9) +__PF(FCFABRIC+10,fcfb10) +__PF(FCFABRIC+11,fcfb11) +__PF(FCFABRIC+12,fcfb12) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802_TR,tr) +#endif +#ifdef ARPHRD_IEEE80211 +__PF(IEEE80211,ieee802.11) +#endif +#ifdef ARPHRD_VOID +__PF(VOID,void) +#endif +}; +#undef __PF + + int i; + for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) { + if (arphrd_names[i].type == type) + return arphrd_names[i].name; + } + snprintf(buf, len, "[%d]", type); + return buf; +} diff --git a/lib/ll_types.o b/lib/ll_types.o new file mode 100644 index 0000000..11e3790 Binary files /dev/null and b/lib/ll_types.o differ diff --git a/lib/rt_names.c b/lib/rt_names.c new file mode 100644 index 0000000..03df086 --- /dev/null +++ b/lib/rt_names.c @@ -0,0 +1,397 @@ +/* + * rt_names.c rtnetlink names DB. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <asm/types.h> +#include <linux/rtnetlink.h> + +#include "rt_names.h" + +static void rtnl_tab_initialize(char *file, char **tab, int size) +{ + char buf[512]; + FILE *fp; + + fp = fopen(file, "r"); + if (!fp) + return; + while (fgets(buf, sizeof(buf), fp)) { + char *p = buf; + int id; + char namebuf[512]; + + while (*p == ' ' || *p == '\t') + p++; + if (*p == '#' || *p == '\n' || *p == 0) + continue; + if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 && + sscanf(p, "0x%x %s #", &id, namebuf) != 2 && + sscanf(p, "%d %s\n", &id, namebuf) != 2 && + sscanf(p, "%d %s #", &id, namebuf) != 2) { + fprintf(stderr, "Database %s is corrupted at %s\n", + file, p); + return; + } + + if (id<0 || id>size) + continue; + + tab[id] = strdup(namebuf); + } + fclose(fp); +} + + +static char * rtnl_rtprot_tab[256] = { + [RTPROT_UNSPEC] = "none", + [RTPROT_REDIRECT] ="redirect", + [RTPROT_KERNEL] = "kernel", + [RTPROT_BOOT] = "boot", + [RTPROT_STATIC] = "static", + + [RTPROT_GATED] = "gated", + [RTPROT_RA] = "ra", + [RTPROT_MRT] = "mrt", + [RTPROT_ZEBRA] ="zebra", + [RTPROT_BIRD] = "bird", + [RTPROT_DNROUTED] = "dnrouted", +#ifdef RTPROT_XORP + [RTPROT_XORP] = "xorp", +#endif +}; + + + +static int rtnl_rtprot_init; + +static void rtnl_rtprot_initialize(void) +{ + rtnl_rtprot_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_protos", + rtnl_rtprot_tab, 256); +} + +char * rtnl_rtprot_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtprot_tab[id]) { + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + } + if (rtnl_rtprot_tab[id]) + return rtnl_rtprot_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtprot_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtprot_tab[i] && + strcmp(rtnl_rtprot_tab[i], arg) == 0) { + cache = rtnl_rtprot_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtscope_tab[256] = { + "global", +}; + +static int rtnl_rtscope_init; + +static void rtnl_rtscope_initialize(void) +{ + rtnl_rtscope_init = 1; + rtnl_rtscope_tab[255] = "nowhere"; + rtnl_rtscope_tab[254] = "host"; + rtnl_rtscope_tab[253] = "link"; + rtnl_rtscope_tab[200] = "site"; + rtnl_tab_initialize("/etc/iproute2/rt_scopes", + rtnl_rtscope_tab, 256); +} + +char * rtnl_rtscope_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtscope_tab[id]) { + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + } + if (rtnl_rtscope_tab[id]) + return rtnl_rtscope_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtscope_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtscope_tab[i] && + strcmp(rtnl_rtscope_tab[i], arg) == 0) { + cache = rtnl_rtscope_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtrealm_tab[256] = { + "unknown", +}; + +static int rtnl_rtrealm_init; + +static void rtnl_rtrealm_initialize(void) +{ + rtnl_rtrealm_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_realms", + rtnl_rtrealm_tab, 256); +} + +char * rtnl_rtrealm_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtrealm_tab[id]) { + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + } + if (rtnl_rtrealm_tab[id]) + return rtnl_rtrealm_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + + +int rtnl_rtrealm_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtrealm_tab[i] && + strcmp(rtnl_rtrealm_tab[i], arg) == 0) { + cache = rtnl_rtrealm_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rttable_tab[256] = { + "unspec", +}; + +static int rtnl_rttable_init; + +static void rtnl_rttable_initialize(void) +{ + rtnl_rttable_init = 1; + rtnl_rttable_tab[255] = "local"; + rtnl_rttable_tab[254] = "main"; + rtnl_rttable_tab[253] = "default"; + rtnl_tab_initialize("/etc/iproute2/rt_tables", + rtnl_rttable_tab, 256); +} + +char * rtnl_rttable_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rttable_tab[id]) { + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + } + if (rtnl_rttable_tab[id]) + return rtnl_rttable_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rttable_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rttable_tab[i] && + strcmp(rtnl_rttable_tab[i], arg) == 0) { + cache = rtnl_rttable_tab[i]; + res = i; + *id = res; + return 0; + } + } + + i = strtoul(arg, &end, 0); + if (!end || end == arg || *end || i > 255) + return -1; + *id = i; + return 0; +} + + +static char * rtnl_rtdsfield_tab[256] = { + "0", +}; + +static int rtnl_rtdsfield_init; + +static void rtnl_rtdsfield_initialize(void) +{ + rtnl_rtdsfield_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_dsfield", + rtnl_rtdsfield_tab, 256); +} + +char * rtnl_dsfield_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtdsfield_tab[id]) { + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + } + if (rtnl_rtdsfield_tab[id]) + return rtnl_rtdsfield_tab[id]; + snprintf(buf, len, "0x%02x", id); + return buf; +} + + +int rtnl_dsfield_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtdsfield_tab[i] && + strcmp(rtnl_rtdsfield_tab[i], arg) == 0) { + cache = rtnl_rtdsfield_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 16); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + diff --git a/lib/rt_names.o b/lib/rt_names.o new file mode 100644 index 0000000..acab8cc Binary files /dev/null and b/lib/rt_names.o differ diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..73ce865 --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,558 @@ +/* + * utils.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <resolv.h> +#include <asm/types.h> +#include <linux/pkt_sched.h> + +#include "utils.h" + +int get_integer(int *val, const char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN) + return -1; + *val = res; + return 0; +} + +int get_unsigned(unsigned *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > UINT_MAX) + return -1; + *val = res; + return 0; +} + +int get_u64(__u64 *val, const char *arg, int base) +{ + unsigned long long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoull(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res == 0xFFFFFFFFULL) + return -1; + *val = res; + return 0; +} + +int get_u32(__u32 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) + return -1; + *val = res; + return 0; +} + +int get_u16(__u16 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFF) + return -1; + *val = res; + return 0; +} + +int get_u8(__u8 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFF) + return -1; + *val = res; + return 0; +} + +int get_s16(__s16 *val, const char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000) + return -1; + *val = res; + return 0; +} + +int get_s8(__s8 *val, const char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80) + return -1; + *val = res; + return 0; +} + +int get_addr_1(inet_prefix *addr, const char *name, int family) +{ + const char *cp; + unsigned char *ap = (unsigned char*)addr->data; + int i; + + memset(addr, 0, sizeof(*addr)); + + if (strcmp(name, "default") == 0 || + strcmp(name, "all") == 0 || + strcmp(name, "any") == 0) { + if (family == AF_DECnet) + return -1; + addr->family = family; + addr->bytelen = (family == AF_INET6 ? 16 : 4); + addr->bitlen = -1; + return 0; + } + + if (strchr(name, ':')) { + addr->family = AF_INET6; + if (family != AF_UNSPEC && family != AF_INET6) + return -1; + if (inet_pton(AF_INET6, name, addr->data) <= 0) + return -1; + addr->bytelen = 16; + addr->bitlen = -1; + return 0; + } + + if (family == AF_DECnet) { + struct dn_naddr dna; + addr->family = AF_DECnet; + if (dnet_pton(AF_DECnet, name, &dna) <= 0) + return -1; + memcpy(addr->data, dna.a_addr, 2); + addr->bytelen = 2; + addr->bitlen = -1; + return 0; + } + + addr->family = AF_INET; + if (family != AF_UNSPEC && family != AF_INET) + return -1; + addr->bytelen = 4; + addr->bitlen = -1; + for (cp=name, i=0; *cp; cp++) { + if (*cp <= '9' && *cp >= '0') { + ap[i] = 10*ap[i] + (*cp-'0'); + continue; + } + if (*cp == '.' && ++i <= 3) + continue; + return -1; + } + return 0; +} + +int get_prefix_1(inet_prefix *dst, char *arg, int family) +{ + int err; + unsigned plen; + char *slash; + + memset(dst, 0, sizeof(*dst)); + + if (strcmp(arg, "default") == 0 || + strcmp(arg, "any") == 0 || + strcmp(arg, "all") == 0) { + if (family == AF_DECnet) + return -1; + dst->family = family; + dst->bytelen = 0; + dst->bitlen = 0; + return 0; + } + + slash = strchr(arg, '/'); + if (slash) + *slash = 0; + + err = get_addr_1(dst, arg, family); + if (err == 0) { + switch(dst->family) { + case AF_INET6: + dst->bitlen = 128; + break; + case AF_DECnet: + dst->bitlen = 16; + break; + default: + case AF_INET: + dst->bitlen = 32; + } + if (slash) { + if (get_integer(&plen, slash+1, 0) || plen > dst->bitlen) { + err = -1; + goto done; + } + dst->bitlen = plen; + } + } +done: + if (slash) + *slash = '/'; + return err; +} + +int get_addr(inet_prefix *dst, const char *arg, int family) +{ + if (family == AF_PACKET) { + fprintf(stderr, "Error: \"%s\" may be inet address, but it is not allowed in this context.\n", arg); + exit(1); + } + if (get_addr_1(dst, arg, family)) { + fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", arg); + exit(1); + } + return 0; +} + +int get_prefix(inet_prefix *dst, char *arg, int family) +{ + if (family == AF_PACKET) { + fprintf(stderr, "Error: \"%s\" may be inet prefix, but it is not allowed in this context.\n", arg); + exit(1); + } + if (get_prefix_1(dst, arg, family)) { + fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", arg); + exit(1); + } + return 0; +} + +__u32 get_addr32(const char *name) +{ + inet_prefix addr; + if (get_addr_1(&addr, name, AF_INET)) { + fprintf(stderr, "Error: an IP address is expected rather than \"%s\"\n", name); + exit(1); + } + return addr.data[0]; +} + +void incomplete_command(void) +{ + fprintf(stderr, "Command line is not complete. Try option \"help\"\n"); + exit(-1); +} + +void missarg(const char *key) +{ + fprintf(stderr, "Error: argument \"%s\" is required\n", key); + exit(-1); +} + +void invarg(const char *msg, const char *arg) +{ + fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg); + exit(-1); +} + +void duparg(const char *key, const char *arg) +{ + fprintf(stderr, "Error: duplicate \"%s\": \"%s\" is the second value.\n", key, arg); + exit(-1); +} + +void duparg2(const char *key, const char *arg) +{ + fprintf(stderr, "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n", key, arg); + exit(-1); +} + +int matches(const char *cmd, const char *pattern) +{ + int len = strlen(cmd); + if (len > strlen(pattern)) + return -1; + return memcmp(pattern, cmd, len); +} + +int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits) +{ + __u32 *a1 = a->data; + __u32 *a2 = b->data; + int words = bits >> 0x05; + + bits &= 0x1f; + + if (words) + if (memcmp(a1, a2, words << 2)) + return -1; + + if (bits) { + __u32 w1, w2; + __u32 mask; + + w1 = a1[words]; + w2 = a2[words]; + + mask = htonl((0xffffffff) << (0x20 - bits)); + + if ((w1 ^ w2) & mask) + return 1; + } + + return 0; +} + +int __iproute2_hz_internal; + +int __get_hz(void) +{ + char name[1024]; + int hz = 0; + FILE *fp; + + if (getenv("HZ")) + return atoi(getenv("HZ")) ? : HZ; + + if (getenv("PROC_NET_PSCHED")) { + snprintf(name, sizeof(name)-1, "%s", getenv("PROC_NET_PSCHED")); + } else if (getenv("PROC_ROOT")) { + snprintf(name, sizeof(name)-1, "%s/net/psched", getenv("PROC_ROOT")); + } else { + strcpy(name, "/proc/net/psched"); + } + fp = fopen(name, "r"); + + if (fp) { + unsigned nom, denom; + if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) + if (nom == 1000000) + hz = denom; + fclose(fp); + } + if (hz) + return hz; + return HZ; +} + +int __iproute2_user_hz_internal; + +int __get_user_hz(void) +{ + return sysconf(_SC_CLK_TCK); +} + +const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen) +{ + switch (af) { + case AF_INET: + case AF_INET6: + return inet_ntop(af, addr, buf, buflen); + case AF_IPX: + return ipx_ntop(af, addr, buf, buflen); + case AF_DECnet: + { + struct dn_naddr dna = { 2, { 0, 0, }}; + memcpy(dna.a_addr, addr, 2); + return dnet_ntop(af, &dna, buf, buflen); + } + default: + return "???"; + } +} + +#ifdef RESOLVE_HOSTNAMES +struct namerec +{ + struct namerec *next; + inet_prefix addr; + char *name; +}; + +static struct namerec *nht[256]; + +char *resolve_address(const char *addr, int len, int af) +{ + struct namerec *n; + struct hostent *h_ent; + unsigned hash; + static int notfirst; + + + if (af == AF_INET6 && ((__u32*)addr)[0] == 0 && + ((__u32*)addr)[1] == 0 && ((__u32*)addr)[2] == htonl(0xffff)) { + af = AF_INET; + addr += 12; + len = 4; + } + + hash = addr[len-1] ^ addr[len-2] ^ addr[len-3] ^ addr[len-4]; + + for (n = nht[hash]; n; n = n->next) { + if (n->addr.family == af && + n->addr.bytelen == len && + memcmp(n->addr.data, addr, len) == 0) + return n->name; + } + if ((n = malloc(sizeof(*n))) == NULL) + return NULL; + n->addr.family = af; + n->addr.bytelen = len; + n->name = NULL; + memcpy(n->addr.data, addr, len); + n->next = nht[hash]; + nht[hash] = n; + if (++notfirst == 1) + sethostent(1); + fflush(stdout); + + if ((h_ent = gethostbyaddr(addr, len, af)) != NULL) + n->name = strdup(h_ent->h_name); + + /* Even if we fail, "negative" entry is remembered. */ + return n->name; +} +#endif + + +const char *format_host(int af, int len, const void *addr, + char *buf, int buflen) +{ +#ifdef RESOLVE_HOSTNAMES + if (resolve_hosts) { + char *n; + if (len <= 0) { + switch (af) { + case AF_INET: + len = 4; + break; + case AF_INET6: + len = 16; + break; + case AF_IPX: + len = 10; + break; +#ifdef AF_DECnet + /* I see no reasons why gethostbyname + may not work for DECnet */ + case AF_DECnet: + len = 2; + break; +#endif + default: ; + } + } + if (len > 0 && + (n = resolve_address(addr, len, af)) != NULL) + return n; + } +#endif + return rt_addr_n2a(af, len, addr, buf, buflen); +} + + +__u8* hexstring_n2a(const __u8 *str, int len, __u8 *buf, int blen) +{ + __u8 *ptr = buf; + int i; + + for (i=0; i<len; i++) { + if (blen < 3) + break; + sprintf(ptr, "%02x", str[i]); + ptr += 2; + blen -= 2; + if (i != len-1 && blen > 1) { + *ptr++ = ':'; + blen--; + } + } + return buf; +} + +__u8* hexstring_a2n(const __u8 *str, __u8 *buf, int blen) +{ + int cnt = 0; + + for (;;) { + unsigned acc; + char ch; + + acc = 0; + + while ((ch = *str) != ':' && ch != 0) { + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'a' && ch <= 'f') + ch -= 'a'-10; + else if (ch >= 'A' && ch <= 'F') + ch -= 'A'-10; + else + return NULL; + acc = (acc<<4) + ch; + str++; + } + + if (acc > 255) + return NULL; + if (cnt < blen) { + buf[cnt] = acc; + cnt++; + } + if (ch == 0) + break; + ++str; + } + if (cnt < blen) + memset(buf+cnt, 0, blen-cnt); + return buf; +} diff --git a/lib/utils.o b/lib/utils.o new file mode 100644 index 0000000..3fd2741 Binary files /dev/null and b/lib/utils.o differ diff --git a/man/man3/libnetlink.3 b/man/man3/libnetlink.3 new file mode 100644 index 0000000..145f38d --- /dev/null +++ b/man/man3/libnetlink.3 @@ -0,0 +1,197 @@ +.TH libnetlink 3 +.SH NAME +libnetlink \- A library for accessing the netlink service +.SH SYNOPSIS +.nf +#include <asm/types.h> +.br +#include <libnetlink.h> +.br +#include <linux/netlink.h> +.br +#include <linux/rtnetlink.h> +.sp +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +.sp +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +.sp +int rtnl_send(struct rtnl_handle *rth, char *buf, int len) +.sp +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +.sp +int rtnl_dump_filter(struct rtnl_handle *rth, + int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), + void *arg1, + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *arg2) +.sp +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, +.br + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), +.br + void *jarg) +.sp +int rtnl_listen(struct rtnl_handle *rtnl, + int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg) +.sp +int rtnl_from_file(FILE *rtnl, + int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg) +.sp +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +.sp +int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) +.sp +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +.sp +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) +.SH DESCRIPTION +libnetlink provides a higher level interface to +.BR rtnetlink(7). +The read functions return 0 on success and a negative errno on failure. +The send functions return the amount of data sent, or -1 on error. +.TP +rtnl_open +Open a rtnetlink socket and save the state into the +.B rth +handle. This handle is passed to all subsequent calls. +.B subscriptions +is a bitmap of the rtnetlink multicast groups the socket will be +a member of. + +.TP +rtnl_wilddump_request +Request a full dump of the +.B type +database for +.B family +addresses. +.B type +is a rtnetlink message type. +.\" XXX + +.TP +rtnl_dump_request +Request a full dump of the +.B type +data buffer into +.B buf +with maximum length of +.B len. +.B type +is a rtnetlink message type. + +.TP +rtnl_dump_filter +Receive netlink data after a request and filter it. +The +.B filter +callback checks if the received message is wanted. It gets the source +address of the message, the message itself and +.B arg1 +as arguments. 0 as return means that the filter passed, a negative +value is returned +by +.I rtnl_dump_filter +in case of error. NULL for +.I filter +means to not use a filter. +.B junk +is used to filter messages not destined to the local socket. +Only one message bundle is received. Unless there is no message +pending, this function does not block. + +.TP +rtnl_listen +Receive netlink data after a request and pass it to +.I handler. +.B handler +is a callback that gets the message source address, the message itself, +and the +.B jarg +cookie as arguments. It will get called for all received messages. +Only one message bundle is received. Unless there is no message +pending this function does not block. + +.TP +rtnl_from_file +Works like +.I rtnl_listen, +but reads a netlink message bundle from the file +.B file +and passes the messages to +.B handler +for parsing. The file contains raw data as received from a rtnetlink socket. +.PP +The following functions are useful to construct custom rtnetlink messages. For +simple database dumping with filtering it is better to use the higher level +functions above. See +.BR rtnetlink(3) +and +.BR netlink(3) +on how to generate a rtnetlink message. The following utility functions +require a continuous buffer that already contains a netlink message header +and a rtnetlink request. + +.TP +rtnl_send +Send the rtnetlink message in +.B buf +of length +.B len +to handle +.B rth. + +.TP +addattr32 +Add a __u32 attribute of type +.B type +and with value +.B data +to netlink message +.B n, +which is part of a buffer of length +.B maxlen. + +.TP +addattr_l +Add a variable length attribute of type +.B type +and with value +.B data +and +.B alen +length to netlink message +.B n, +which is part of a buffer of length +.B maxlen. +.B data +is copied. + +.TP +rta_addattr32 +Initialize the rtnetlink attribute +.B rta +with a __u32 data value. + +.TP +rta_addattr32 +Initialize the rtnetlink attribute +.B rta +with a variable length data value. + +.SH BUGS +The functions sometimes use fprintf and exit when a fatal error occurs. +This library should be named librtnetlink. + +.SH AUTHORS +netlink/rtnetlink was designed and writen by Alexey Kuznetsov. +Andi Kleen wrote the man page. + +.SH SEE ALSO +.BR netlink(7), +.BR rtnetlink(7) +.br +/usr/include/linux/rtnetlink.h diff --git a/man/man8/ip.8 b/man/man8/ip.8 new file mode 100644 index 0000000..cca6d1c --- /dev/null +++ b/man/man8/ip.8 @@ -0,0 +1,1810 @@ +.TH IP 8 "17 January 2002" "iproute2" "Linux" +.SH NAME +ip \- show / manipulate routing, devices, policy routing and tunnels +.SH SYNOPSIS + +.ad l +.in +8 +.ti -8 +.B ip +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OBJECT " := { " +.BR link " | " addr " | " route " | " rule " | " neigh " | " tunnel " | "\ +maddr " | " mroute " | " monitor " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-s\fR[\fItatistics\fR] | +\fB\-r\fR[\fIesolve\fR] | +\fB\-f\fR[\fIamily\fR] { +.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | " +\fB\-o\fR[\fIneline\fR] } + +.ti -8 +.BI "ip link set " DEVICE +.RB "{ " up " | " down " | " arp " { " on " | " off " } |" +.br +.BR promisc " { " on " | " off " } |" +.br +.BR allmulti " { " on " | " off " } |" +.br +.BR dynamic " { " on " | " off " } |" +.br +.BR multicast " { " on " | " off " } |" +.br +.B txqueuelen +.IR PACKETS " |" +.br +.B name +.IR NEWNAME " |" +.br +.B address +.IR LLADDR " |" +.B broadcast +.IR LLADDR " |" +.br +.B mtu +.IR MTU " }" + +.ti -8 +.B ip link show +.RI "[ " DEVICE " ]" + +.ti -8 +.BR "ip addr" " { " add " | " del " } " +.IB IFADDR " dev " STRING + +.ti -8 +.BR "ip addr" " { " show " | " flush " } [ " dev +.IR STRING " ] [ " +.B scope +.IR SCOPE-ID " ] [ " +.B to +.IR PREFIX " ] [ " FLAG-LIST " ] [ " +.B label +.IR PATTERN " ]" + +.ti -8 +.IR IFADDR " := " PREFIX " | " ADDR +.B peer +.IR PREFIX " [ " +.B broadcast +.IR ADDR " ] [ " +.B anycast +.IR ADDR " ] [ " +.B label +.IR STRING " ] [ " +.B scope +.IR SCOPE-ID " ]" + +.ti -8 +.IR SCOPE-ID " := " +.RB "[ " host " | " link " | " global " | " +.IR NUMBER " ]" + +.ti -8 +.IR FLAG-LIST " := [ " FLAG-LIST " ] " FLAG + +.ti -8 +.IR FLAG " := " +.RB "[ " permanent " | " dynamic " | " secondary " | " primary " | "\ +tentative " | " deprecated " ]" + +.ti -8 +.BR "ip route" " { " +.BR list " | " flush " } " +.I SELECTOR + +.ti -8 +.B ip route get +.IR ADDRESS " [ " +.BI from " ADDRESS " iif " STRING" +.RB " ] [ " oif +.IR STRING " ] [ " +.B tos +.IR TOS " ]" + +.ti -8 +.BR "ip route" " { " add " | " del " | " change " | " append " | "\ +replace " | " monitor " } " +.I ROUTE + +.ti -8 +.IR SELECTOR " := " +.RB "[ " root +.IR PREFIX " ] [ " +.B match +.IR PREFIX " ] [ " +.B exact +.IR PREFIX " ] [ " +.B table +.IR TABLE_ID " ] [ " +.B proto +.IR RTPROTO " ] [ " +.B type +.IR TYPE " ] [ " +.B scope +.IR SCOPE " ]" + +.ti -8 +.IR ROUTE " := " NODE_SPEC " [ " INFO_SPEC " ]" + +.ti -8 +.IR NODE_SPEC " := [ " TYPE " ] " PREFIX " [" +.B tos +.IR TOS " ] [ " +.B table +.IR TABLE_ID " ] [ " +.B proto +.IR RTPROTO " ] [ " +.B scope +.IR SCOPE " ] [ " +.B metric +.IR METRIC " ]" + +.ti -8 +.IR INFO_SPEC " := " "NH OPTIONS FLAGS" " [" +.B nexthop +.IR NH " ] ..." + +.ti -8 +.IR NH " := [ " +.B via +.IR ADDRESS " ] [ " +.B dev +.IR STRING " ] [ " +.B weight +.IR NUMBER " ] " NHFLAGS + +.ti -8 +.IR OPTIONS " := " FLAGS " [ " +.B mtu +.IR NUMBER " ] [ " +.B advmss +.IR NUMBER " ] [ " +.B rtt +.IR NUMBER " ] [ " +.B rttvar +.IR NUMBER " ] [ " +.B window +.IR NUMBER " ] [ " +.B cwnd +.IR NUMBER " ] [ " +.B ssthresh +.IR REALM " ] [ " +.B realms +.IR REALM " ]" + +.ti -8 +.IR TYPE " := [ " +.BR unicast " | " local " | " broadcast " | " multicast " | "\ +throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]" + +.ti -8 +.IR TABLE_ID " := [ " +.BR local "| " main " | " default " | " all " |" +.IR NUMBER " ]" + +.ti -8 +.IR SCOPE " := [ " +.BR host " | " link " | " global " |" +.IR NUMBER " ]" + +.ti -8 +.IR FLAGS " := [ " +.BR equalize " ]" + +.ti -8 +.IR NHFLAGS " := [ " +.BR onlink " | " pervasive " ]" + +.ti -8 +.IR RTPROTO " := [ " +.BR kernel " | " boot " | " static " |" +.IR NUMBER " ]" + +.ti -8 +.B ip rule +.RB " [ " list " | " add " | " del " ]" +.I SELECTOR ACTION + +.ti -8 +.IR SELECTOR " := [ " +.B from +.IR PREFIX " ] [ " +.B to +.IR PREFIX " ] [ " +.B tos +.IR TOS " ] [ " +.B fwmark +.IR FWMARK " ] [ " +.B dev +.IR STRING " ] [ " +.B pref +.IR NUMBER " ]" + +.ti -8 +.IR ACTION " := [ " +.B table +.IR TABLE_ID " ] [ " +.B nat +.IR ADDRESS " ] [ " +.BR prohibit " | " reject " | " unreachable " ] [ " realms +.RI "[" SRCREALM "/]" DSTREALM " ]" + +.ti -8 +.IR TABLE_ID " := [ " +.BR local " | " main " | " default " |" +.IR NUMBER " ]" + +.ti -8 +.BR "ip neigh" " { " add " | " del " | " change " | " replace " } { " +.IR ADDR " [ " +.B lladdr +.IR LLADDR " ] [ " +.BR nud " { " permanent " | " noarp " | " stale " | " reachable " } ] | " proxy +.IR ADDR " } [ " +.B dev +.IR DEV " ]" + +.ti -8 +.BR "ip neigh" " { " show " | " flush " } [ " to +.IR PREFIX " ] [ " +.B dev +.IR DEV " ] [ " +.B nud +.IR STATE " ]" + +.ti -8 +.BR "ip tunnel" " { " add " | " change " | " del " | " show " }" +.RI "[ " NAME " ]" +.br +.RB "[ " mode " { " ipip " | " gre " | " sit " } ]" +.br +.RB "[ " remote +.IR ADDR " ] [ " +.B local +.IR ADDR " ]" +.br +.RB "[ [" i "|" o "]" seq " ] [ [" i "|" o "]" key +.IR KEY " ] [ " +.RB "[" i "|" o "]" csum " ] ]" +.br +.RB "[ " ttl +.IR TTL " ] [ " +.B tos +.IR TOS " ] [ " +.RB "[" no "]" pmtudisc " ]" +.br +.RB "[ " dev +.IR PHYS_DEV " ]" + +.ti -8 +.IR ADDR " := { " IP_ADDRESS " |" +.BR any " }" + +.ti -8 +.IR TOS " := { " NUMBER " |" +.BR inherit " }" + +.ti -8 +.IR TTL " := { " 1 ".." 255 " | " +.BR inherit " }" + +.ti -8 +.IR KEY " := { " DOTTED_QUAD " | " NUMBER " }" + +.ti -8 +.BR "ip maddr" " [ " add " | " del " ]" +.IB MULTIADDR " dev " STRING + +.ti -8 +.BR "ip maddr show" " [ " dev +.IR STRING " ]" + +.ti -8 +.BR "ip mroute show" " [" +.IR PREFIX " ] [ " +.B from +.IR PREFIX " ] [ " +.B iif +.IR DEVICE " ]" + +.ti -8 +.BR "ip monitor" " [ " all " |" +.IR LISTofOBJECTS " ]" +.in -8 +.ad b + +.SH OPTIONS + +.TP +.BR "\-V" , " -Version" +print the version of the +.B ip +utility and exit. + +.TP +.BR "\-s" , " \-stats", " \-statistics" +output more information. If the option +appears twice or more, the amount of information increases. +As a rule, the information is statistics or some time values. + +.TP +.BR "\-f" , " \-family" +followed by protocol family identifier: +.BR "inet" , " inet6" +or +.B link +,enforce the protocol family to use. If the option is not present, +the protocol family is guessed from other arguments. If the rest +of the command line does not give enough information to guess the +family, +.B ip +falls back to the default one, usually +.B inet +or +.BR "any" . +.B link +is a special family identifier meaning that no networking protocol +is involved. + +.TP +.B \-4 +shortcut for +.BR "-family inet" . + +.TP +.B \-6 +shortcut for +.BR "\-family inet6" . + +.TP +.B \-0 +shortcut for +.BR "\-family link" . + +.TP +.BR "\-o" , " \-oneline" +output each record on a single line, replacing line feeds +with the +.B '\' +character. This is convenient when you want to count records +with +.BR wc (1) + or to +.BR grep (1) +the output. + +.TP +.BR "\-r" , " \-resolve" +use the system's name resolver to print DNS names instead of +host addresses. + +.SH IP - COMMAND SYNTAX + +.SS +.I OBJECT + +.TP +.B link +- network device. + +.TP +.B address +- protocol (IP or IPv6) address on a device. +.TP +.B neighbour +- ARP or NDISC cache entry. + +.TP +.B route +- routing table entry. + +.TP +.B rule +- rule in routing policy database. + +.TP +.B maddress +- multicast address. + +.TP +.B mroute +- multicast routing cache entry. + +.TP +.B tunnel +- tunnel over IP. + +.PP +The names of all objects may be written in full or +abbreviated form, f.e. +.B address +is abbreviated as +.B addr +or just +.B a. + +.SS +.I COMMAND + +Specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to +.BR "add" , " delete" +and +.B show +(or +.B list +) objects, but some objects do not allow all of these operations +or have some additional commands. The +.B help +command is available for all objects. It prints +out a list of available commands and argument syntax conventions. +.sp +If no command is given, some default command is assumed. +Usually it is +.B list +or, if the objects of this class cannot be listed, +.BR "help" . + +.SH ip link - network device configuration + +.B link +is a network device and the corresponding commands +display and change the state of devices. + +.SS ip link set - change device attributes + +.TP +.BI dev " NAME " (default) +.I NAME +specifies network device to operate on. + +.TP +.BR up " and " down +change the state of the device to +.B UP +or +.BR "DOWN" . + +.TP +.BR "arp on " or " arp off" +change the +.B NOARP +flag on the device. + +.TP +.BR "multicast on " or " multicast off" +change the +.B MULTICAST +flag on the device. + +.TP +.BR "dynamic on " or " dynamic off" +change the +.B DYNAMIC +flag on the device. + +.TP +.BI name " NAME" +change the name of the device. This operation is not +recommended if the device is running or has some addresses +already configured. + +.TP +.BI txqueuelen " NUMBER" +.TP +.BI txqlen " NUMBER" +change the transmit queue length of the device. + +.TP +.BI mtu " NUMBER" +change the +.I MTU +of the device. + +.TP +.BI address " LLADDRESS" +change the station address of the interface. + +.TP +.BI broadcast " LLADDRESS" +.TP +.BI brd " LLADDRESS" +.TP +.BI peer " LLADDRESS" +change the link layer broadcast address or the peer address when +the interface is +.IR "POINTOPOINT" . + +.PP +.B Warning: +If multiple parameter changes are requested, +.B ip +aborts immediately after any of the changes have failed. +This is the only case when +.B ip +can move the system to an unpredictable state. The solution +is to avoid changing several parameters with one +.B ip link set +call. + +.SS ip link show - display device attributes + +.TP +.BI dev " NAME " (default) +.I NAME +specifies the network device to show. +If this argument is omitted all devices are listed. + +.TP +.B up +only display running interfaces. + +.SH ip address - protocol address management. + +The +.B address +is a protocol (IP or IPv6) address attached +to a network device. Each device must have at least one address +to use the corresponding protocol. It is possible to have several +different addresses attached to one device. These addresses are not +discriminated, so that the term +.B alias +is not quite appropriate for them and we do not use it in this document. +.sp +The +.B ip addr +command displays addresses and their properties, adds new addresses +and deletes old ones. + +.SS ip address add - add new protocol address. + +.TP +.BI dev " NAME" +the name of the device to add the address to. + +.TP +.BI local " ADDRESS " (default) +the address of the interface. The format of the address depends +on the protocol. It is a dotted quad for IP and a sequence of +hexadecimal halfwords separated by colons for IPv6. The +.I ADDRESS +may be followed by a slash and a decimal number which encodes +the network prefix length. + +.TP +.BI peer " ADDRESS" +the address of the remote endpoint for pointopoint interfaces. +Again, the +.I ADDRESS +may be followed by a slash and a decimal number, encoding the network +prefix length. If a peer address is specified, the local address +cannot have a prefix length. The network prefix is associated +with the peer rather than with the local address. + +.TP +.BI broadcast " ADDRESS" +the broadcast address on the interface. +.sp +It is possible to use the special symbols +.B '+' +and +.B '-' +instead of the broadcast address. In this case, the broadcast address +is derived by setting/resetting the host bits of the interface prefix. + +.TP +.BI label " NAME" +Each address may be tagged with a label string. +In order to preserve compatibility with Linux-2.0 net aliases, +this string must coincide with the name of the device or must be prefixed +with the device name followed by colon. + +.TP +.BI scope " SCOPE_VALUE" +the scope of the area where this address is valid. +The available scopes are listed in file +.BR "/etc/iproute2/rt_scopes" . +Predefined scope values are: + +.in +8 +.B global +- the address is globally valid. +.sp +.B site +- (IPv6 only) the address is site local, i.e. it is +valid inside this site. +.sp +.B link +- the address is link local, i.e. it is valid only on this device. +.sp +.B host +- the address is valid only inside this host. +.in -8 + +.SS ip address delete - delete protocol address +.B Arguments: +coincide with the arguments of +.B ip addr add. +The device name is a required argument. The rest are optional. +If no arguments are given, the first address is deleted. + +.SS ip address show - look at protocol addresses + +.TP +.BI dev " NAME " (default) +name of device. + +.TP +.BI scope " SCOPE_VAL" +only list addresses with this scope. + +.TP +.BI to " PREFIX" +only list addresses matching this prefix. + +.TP +.BI label " PATTERN" +only list addresses with labels matching the +.IR "PATTERN" . +.I PATTERN +is a usual shell style pattern. + +.TP +.BR dynamic " and " permanent +(IPv6 only) only list addresses installed due to stateless +address configuration or only list permanent (not dynamic) +addresses. + +.TP +.B tentative +(IPv6 only) only list addresses which did not pass duplicate +address detection. + +.TP +.B deprecated +(IPv6 only) only list deprecated addresses. + +.TP +.BR primary " and " secondary +only list primary (or secondary) addresses. + +.SS ip address flush - flush protocol addresses +This command flushes the protocol addresses selected by some criteria. + +.PP +This command has the same arguments as +.B show. +The difference is that it does not run when no arguments are given. + +.PP +.B Warning: +This command (and other +.B flush +commands described below) is pretty dangerous. If you make a mistake, +it will not forgive it, but will cruelly purge all the addresses. + +.PP +With the +.B -statistics +option, the command becomes verbose. It prints out the number of deleted +addresses and the number of rounds made to flush the address list. If +this option is given twice, +.B ip addr flush +also dumps all the deleted addresses in the format described in the +previous subsection. + +.SH ip neighbour - neighbour/arp tables management. + +.B neighbour +objects establish bindings between protocol addresses and +link layer addresses for hosts sharing the same link. +Neighbour entries are organized into tables. The IPv4 neighbour table +is known by another name - the ARP table. + +.P +The corresponding commands display neighbour bindings +and their properties, add new neighbour entries and delete old ones. + +.SS ip neighbour add - add a new neighbour entry +.SS ip neighbour change - change an existing entry +.SS ip neighbour replace - add a new entry or change an existing one + +These commands create new neighbour records or update existing ones. + +.TP +.BI to " ADDRESS " (default) +the protocol address of the neighbour. It is either an IPv4 or IPv6 address. + +.TP +.BI dev " NAME" +the interface to which this neighbour is attached. + +.TP +.BI lladdr " LLADDRESS" +the link layer address of the neighbour. +.I LLADDRESS +can also be +.BR "null" . + +.TP +.BI nud " NUD_STATE" +the state of the neighbour entry. +.B nud +is an abbreviation for 'Neigh bour Unreachability Detection'. +The state can take one of the following values: + +.in +8 +.B permanent +- the neighbour entry is valid forever and can be only +be removed administratively. +.sp + +.B noarp +- the neighbour entry is valid. No attempts to validate +this entry will be made but it can be removed when its lifetime expires. +.sp + +.B reachable +- the neighbour entry is valid until the reachability +timeout expires. +.sp + +.B stale +- the neighbour entry is valid but suspicious. +This option to +.B ip neigh +does not change the neighbour state if it was valid and the address +is not changed by this command. +.in -8 + +.SS ip neighbour delete - delete a neighbour entry +This command invalidates a neighbour entry. + +.PP +The arguments are the same as with +.BR "ip neigh add" , +except that +.B lladdr +and +.B nud +are ignored. + +.PP +.B Warning: +Attempts to delete or manually change a +.B noarp +entry created by the kernel may result in unpredictable behaviour. +Particularly, the kernel may try to resolve this address even +on a +.B NOARP +interface or if the address is multicast or broadcast. + +.SS ip neighbour show - list neighbour entries + +This commands displays neighbour tables. + +.TP +.BI to " ADDRESS " (default) +the prefix selecting the neighbours to list. + +.TP +.BI dev " NAME" +only list the neighbours attached to this device. + +.TP +.B unused +only list neighbours which are not currently in use. + +.TP +.BI nud " NUD_STATE" +only list neighbour entries in this state. +.I NUD_STATE +takes values listed below or the special value +.B all +which means all states. This option may occur more than once. +If this option is absent, +.B ip +lists all entries except for +.B none +and +.BR "noarp" . + +.SS ip neighbour flush - flush neighbour entries +This command flushes neighbour tables, selecting +entries to flush by some criteria. + +.PP +This command has the same arguments as +.B show. +The differences are that it does not run when no arguments are given, +and that the default neighbour states to be flushed do not include +.B permanent +and +.BR "noarp" . + +.PP +With the +.B -statistics +option, the command becomes verbose. It prints out the number of +deleted neighbours and the number of rounds made to flush the +neighbour table. If the option is given +twice, +.B ip neigh flush +also dumps all the deleted neighbours. + +.SH ip route - routing table management +Manipulate route entries in the kernel routing tables keep +information about paths to other networked nodes. +.sp +.B Route types: + +.in +8 +.B unicast +- the route entry describes real paths to the destinations covered +by the route prefix. + +.sp +.B unreachable +- these destinations are unreachable. Packets are discarded and the +ICMP message +.I host unreachable +is generated. +The local senders get an +.I EHOSTUNREACH +error. + +.sp +.B blackhole +- these destinations are unreachable. Packets are discarded silently. +The local senders get an +.I EINVAL +error. + +.sp +.B prohibit +- these destinations are unreachable. Packets are discarded and the +ICMP message +.I communication administratively prohibited +is generated. The local senders get an +.I EACCES +error. + +.sp +.B local +- the destinations are assigned to this host. The packets are looped +back and delivered locally. + +.sp +.B broadcast +- the destinations are broadcast addresses. The packets are sent as +link broadcasts. + +.sp +.B throw +- a special control route used together with policy rules. If such a +route is selected, lookup in this table is terminated pretending that +no route was found. Without policy routing it is equivalent to the +absence of the route in the routing table. The packets are dropped +and the ICMP message +.I net unreachable +is generated. The local senders get an +.I ENETUNREACH +error. + +.sp +.B nat +- a special NAT route. Destinations covered by the prefix +are considered to be dummy (or external) addresses which require translation +to real (or internal) ones before forwarding. The addresses to translate to +are selected with the attribute +.B Warning: +Route NAT is no longer supported in Linux 2.6. + + +.BR "via" . +.sp +.B anycast +.RI "- " "not implemented" +the destinations are +.I anycast +addresses assigned to this host. They are mainly equivalent +to +.B local +with one difference: such addresses are invalid when used +as the source address of any packet. + +.sp +.B multicast +- a special type used for multicast routing. It is not present in +normal routing tables. +.in -8 + +.P +.B Route tables: +Linux-2.x can pack routes into several routing +tables identified by a number in the range from 1 to 255 or by +name from the file +.B /etc/iproute2/rt_tables +. By default all normal routes are inserted into the +.B main +table (ID 254) and the kernel only uses this table when calculating routes. + +.sp +Actually, one other table always exists, which is invisible but +even more important. It is the +.B local +table (ID 255). This table +consists of routes for local and broadcast addresses. The kernel maintains +this table automatically and the administrator usually need not modify it +or even look at it. + +The multiple routing tables enter the game when +.I policy routing +is used. + +.SS ip route add - add new route +.SS ip route change - change route +.SS ip route replace - change or add new one + +.TP +.BI to " TYPE PREFIX " (default) +the destination prefix of the route. If +.I TYPE +is omitted, +.B ip +assumes type +.BR "unicast" . +Other values of +.I TYPE +are listed above. +.I PREFIX +is an IP or IPv6 address optionally followed by a slash and the +prefix length. If the length of the prefix is missing, +.B ip +assumes a full-length host route. There is also a special +.I PREFIX +.B default +- which is equivalent to IP +.B 0/0 +or to IPv6 +.BR "::/0" . + +.TP +.BI tos " TOS" +.TP +.BI dsfield " TOS" +the Type Of Service (TOS) key. This key has no associated mask and +the longest match is understood as: First, compare the TOS +of the route and of the packet. If they are not equal, then the packet +may still match a route with a zero TOS. +.I TOS +is either an 8 bit hexadecimal number or an identifier +from +.BR "/etc/iproute2/rt_dsfield" . + +.TP +.BI metric " NUMBER" +.TP +.BI preference " NUMBER" +the preference value of the route. +.I NUMBER +is an arbitrary 32bit number. + +.TP +.BI table " TABLEID" +the table to add this route to. +.I TABLEID +may be a number or a string from the file +.BR "/etc/iproute2/rt_tables" . +If this parameter is omitted, +.B ip +assumes the +.B main +table, with the exception of +.BR local " , " broadcast " and " nat +routes, which are put into the +.B local +table by default. + +.TP +.BI dev " NAME" +the output device name. + +.TP +.BI via " ADDRESS" +the address of the nexthop router. Actually, the sense of this field +depends on the route type. For normal +.B unicast +routes it is either the true next hop router or, if it is a direct +route installed in BSD compatibility mode, it can be a local address +of the interface. For NAT routes it is the first address of the block +of translated IP destinations. + +.TP +.BI src " ADDRESS" +the source address to prefer when sending to the destinations +covered by the route prefix. + +.TP +.BI realm " REALMID" +the realm to which this route is assigned. +.I REALMID +may be a number or a string from the file +.BR "/etc/iproute2/rt_realms" . + +.TP +.BI mtu " MTU" +.TP +.BI "mtu lock" " MTU" +the MTU along the path to the destination. If the modifier +.B lock +is not used, the MTU may be updated by the kernel due to +Path MTU Discovery. If the modifier +.B lock +is used, no path MTU discovery will be tried, all packets +will be sent without the DF bit in IPv4 case or fragmented +to MTU for IPv6. + +.TP +.BI window " NUMBER" +the maximal window for TCP to advertise to these destinations, +measured in bytes. It limits maximal data bursts that our TCP +peers are allowed to send to us. + +.TP +.BI rtt " NUMBER" +the initial RTT ('Round Trip Time') estimate. + +.TP +.BI rttvar " NUMBER " "(2.3.15+ only)" +the initial RTT variance estimate. + +.TP +.BI ssthresh " NUMBER " "(2.3.15+ only)" +an estimate for the initial slow start threshold. + +.TP +.BI cwnd " NUMBER " "(2.3.15+ only)" +the clamp for congestion window. It is ignored if the +.B lock +flag is not used. + +.TP +.BI advmss " NUMBER " "(2.3.15+ only)" +the MSS ('Maximal Segment Size') to advertise to these +destinations when establishing TCP connections. If it is not given, +Linux uses a default value calculated from the first hop device MTU. +(If the path to these destination is asymmetric, this guess may be wrong.) + +.TP +.BI reordering " NUMBER " "(2.3.15+ only)" +Maximal reordering on the path to this destination. +If it is not given, Linux uses the value selected with +.B sysctl +variable +.BR "net/ipv4/tcp_reordering" . + +.TP +.BI nexthop " NEXTHOP" +the nexthop of a multipath route. +.I NEXTHOP +is a complex value with its own syntax similar to the top level +argument lists: + +.in +8 +.BI via " ADDRESS" +- is the nexthop router. +.sp + +.BI dev " NAME" +- is the output device. +.sp + +.BI weight " NUMBER" +- is a weight for this element of a multipath +route reflecting its relative bandwidth or quality. +.in -8 + +.TP +.BI scope " SCOPE_VAL" +the scope of the destinations covered by the route prefix. +.I SCOPE_VAL +may be a number or a string from the file +.BR "/etc/iproute2/rt_scopes" . +If this parameter is omitted, +.B ip +assumes scope +.B global +for all gatewayed +.B unicast +routes, scope +.B link +for direct +.BR unicast " and " broadcast +routes and scope +.BR host " for " local +routes. + +.TP +.BI protocol " RTPROTO" +the routing protocol identifier of this route. +.I RTPROTO +may be a number or a string from the file +.BR "/etc/iproute2/rt_protos" . +If the routing protocol ID is not given, +.B ip assumes protocol +.B boot +(i.e. it assumes the route was added by someone who doesn't +understand what they are doing). Several protocol values have +a fixed interpretation. +Namely: + +.in +8 +.B redirect +- the route was installed due to an ICMP redirect. +.sp + +.B kernel +- the route was installed by the kernel during autoconfiguration. +.sp + +.B boot +- the route was installed during the bootup sequence. +If a routing daemon starts, it will purge all of them. +.sp + +.B static +- the route was installed by the administrator +to override dynamic routing. Routing daemon will respect them +and, probably, even advertise them to its peers. +.sp + +.B ra +- the route was installed by Router Discovery protocol. +.in -8 + +.sp +The rest of the values are not reserved and the administrator is free +to assign (or not to assign) protocol tags. + +.TP +.B onlink +pretend that the nexthop is directly attached to this link, +even if it does not match any interface prefix. + +.TP +.B equalize +allow packet by packet randomization on multipath routes. +Without this modifier, the route will be frozen to one selected +nexthop, so that load splitting will only occur on per-flow base. +.B equalize +only works if the kernel is patched. + +.SS ip route delete - delete route + +.B ip route del +has the same arguments as +.BR "ip route add" , +but their semantics are a bit different. + +Key values +.RB "(" to ", " tos ", " preference " and " table ")" +select the route to delete. If optional attributes are present, +.B ip +verifies that they coincide with the attributes of the route to delete. +If no route with the given key and attributes was found, +.B ip route del +fails. + +.SS ip route show - list routes +the command displays the contents of the routing tables or the route(s) +selected by some criteria. + +.TP +.BI to " SELECTOR " (default) +only select routes from the given range of destinations. +.I SELECTOR +consists of an optional modifier +.RB "(" root ", " match " or " exact ")" +and a prefix. +.BI root " PREFIX" +selects routes with prefixes not shorter than +.IR PREFIX "." +F.e. +.BI root " 0/0" +selects the entire routing table. +.BI match " PREFIX" +selects routes with prefixes not longer than +.IR PREFIX "." +F.e. +.BI match " 10.0/16" +selects +.IR 10.0/16 "," +.IR 10/8 " and " 0/0 , +but it does not select +.IR 10.1/16 " and " 10.0.0/24 . +And +.BI exact " PREFIX" +(or just +.IR PREFIX ")" +selects routes with this exact prefix. If neither of these options +are present, +.B ip +assumes +.BI root " 0/0" +i.e. it lists the entire table. + +.TP +.BI tos " TOS" +.BI dsfield " TOS" +only select routes with the given TOS. + +.TP +.BI table " TABLEID" +show the routes from this table(s). The default setting is to show +.BR table main "." +.I TABLEID +may either be the ID of a real table or one of the special values: +.sp +.in +8 +.B all +- list all of the tables. +.sp +.B cache +- dump the routing cache. +.in -8 + +.TP +.B cloned +.TP +.B cached +list cloned routes i.e. routes which were dynamically forked from +other routes because some route attribute (f.e. MTU) was updated. +Actually, it is equivalent to +.BR "table cache" "." + +.TP +.BI from " SELECTOR" +the same syntax as for +.BR to "," +but it binds the source address range rather than destinations. +Note that the +.B from +option only works with cloned routes. + +.TP +.BI protocol " RTPROTO" +only list routes of this protocol. + +.TP +.BI scope " SCOPE_VAL" +only list routes with this scope. + +.TP +.BI type " TYPE" +only list routes of this type. + +.TP +.BI dev " NAME" +only list routes going via this device. + +.TP +.BI via " PREFIX" +only list routes going via the nexthop routers selected by +.IR PREFIX "." + +.TP +.BI src " PREFIX" +only list routes with preferred source addresses selected +by +.IR PREFIX "." + +.TP +.BI realm " REALMID" +.TP +.BI realms " FROMREALM/TOREALM" +only list routes with these realms. + +.SS ip route flush - flush routing tables +this command flushes routes selected by some criteria. + +.sp +The arguments have the same syntax and semantics as the arguments of +.BR "ip route show" , +but routing tables are not listed but purged. The only difference is +the default action: +.B show +dumps all the IP main routing table but +.B flush +prints the helper page. + +.sp +With the +.B -statistics +option, the command becomes verbose. It prints out the number of +deleted routes and the number of rounds made to flush the routing +table. If the option is given +twice, +.B ip route flush +also dumps all the deleted routes in the format described in the +previous subsection. + +.SS ip route get - get a single route +this command gets a single route to a destination and prints its +contents exactly as the kernel sees it. + +.TP +.BI to " ADDRESS " (default) +the destination address. + +.TP +.BI from " ADDRESS" +the source address. + +.TP +.BI tos " TOS" +.TP +.BI dsfield " TOS" +the Type Of Service. + +.TP +.BI iif " NAME" +the device from which this packet is expected to arrive. + +.TP +.BI oif " NAME" +force the output device on which this packet will be routed. + +.TP +.B connected +if no source address +.RB "(option " from ")" +was given, relookup the route with the source set to the preferred +address received from the first lookup. +If policy routing is used, it may be a different route. + +.P +Note that this operation is not equivalent to +.BR "ip route show" . +.B show +shows existing routes. +.B get +resolves them and creates new clones if necessary. Essentially, +.B get +is equivalent to sending a packet along this path. +If the +.B iif +argument is not given, the kernel creates a route +to output packets towards the requested destination. +This is equivalent to pinging the destination +with a subsequent +.BR "ip route ls cache" , +however, no packets are actually sent. With the +.B iif +argument, the kernel pretends that a packet arrived from this interface +and searches for a path to forward the packet. + +.SH ip rule - routing policy database management + +.BR "Rule" s +in the routing policy database control the route selection algorithm. + +.P +Classic routing algorithms used in the Internet make routing decisions +based only on the destination address of packets (and in theory, +but not in practice, on the TOS field). + +.P +In some circumstances we want to route packets differently depending not only +on destination addresses, but also on other packet fields: source address, +IP protocol, transport protocol ports or even packet payload. +This task is called 'policy routing'. + +.P +To solve this task, the conventional destination based routing table, ordered +according to the longest match rule, is replaced with a 'routing policy +database' (or RPDB), which selects routes by executing some set of rules. + +.P +Each policy routing rule consists of a +.B selector +and an +.B action predicate. +The RPDB is scanned in the order of increasing priority. The selector +of each rule is applied to {source address, destination address, incoming +interface, tos, fwmark} and, if the selector matches the packet, +the action is performed. The action predicate may return with success. +In this case, it will either give a route or failure indication +and the RPDB lookup is terminated. Otherwise, the RPDB program +continues on the next rule. + +.P +Semantically, natural action is to select the nexthop and the output device. + +.P +At startup time the kernel configures the default RPDB consisting of three +rules: + +.TP +1. +Priority: 0, Selector: match anything, Action: lookup routing +table +.B local +(ID 255). +The +.B local +table is a special routing table containing +high priority control routes for local and broadcast addresses. +.sp +Rule 0 is special. It cannot be deleted or overridden. + +.TP +2. +Priority: 32766, Selector: match anything, Action: lookup routing +table +.B main +(ID 254). +The +.B main +table is the normal routing table containing all non-policy +routes. This rule may be deleted and/or overridden with other +ones by the administrator. + +.TP +3. +Priority: 32767, Selector: match anything, Action: lookup routing +table +.B default +(ID 253). +The +.B default +table is empty. It is reserved for some post-processing if no previous +default rules selected the packet. +This rule may also be deleted. + +.P +Each RPDB entry has additional +attributes. F.e. each rule has a pointer to some routing +table. NAT and masquerading rules have an attribute to select new IP +address to translate/masquerade. Besides that, rules have some +optional attributes, which routes have, namely +.BR "realms" . +These values do not override those contained in the routing tables. They +are only used if the route did not select any attributes. + +.sp +The RPDB may contain rules of the following types: + +.in +8 +.B unicast +- the rule prescribes to return the route found +in the routing table referenced by the rule. + +.B blackhole +- the rule prescribes to silently drop the packet. + +.B unreachable +- the rule prescribes to generate a 'Network is unreachable' error. + +.B prohibit +- the rule prescribes to generate 'Communication is administratively +prohibited' error. + +.B nat +- the rule prescribes to translate the source address +of the IP packet into some other value. +.in -8 + +.SS ip rule add - insert a new rule +.SS ip rule delete - delete a rule + +.TP +.BI type " TYPE " (default) +the type of this rule. The list of valid types was given in the previous +subsection. + +.TP +.BI from " PREFIX" +select the source prefix to match. + +.TP +.BI to " PREFIX" +select the destination prefix to match. + +.TP +.BI iif " NAME" +select the incoming device to match. If the interface is loopback, +the rule only matches packets originating from this host. This means +that you may create separate routing tables for forwarded and local +packets and, hence, completely segregate them. + +.TP +.BI tos " TOS" +.TP +.BI dsfield " TOS" +select the TOS value to match. + +.TP +.BI fwmark " MARK" +select the +.B fwmark +value to match. + +.TP +.BI priority " PREFERENCE" +the priority of this rule. Each rule should have an explicitly +set +.I unique +priority value. + +.TP +.BI table " TABLEID" +the routing table identifier to lookup if the rule selector matches. + +.TP +.BI realms " FROM/TO" +Realms to select if the rule matched and the routing table lookup +succeeded. Realm +.I TO +is only used if the route did not select any realm. + +.TP +.BI nat " ADDRESS" +The base of the IP address block to translate (for source addresses). +The +.I ADDRESS +may be either the start of the block of NAT addresses (selected by NAT +routes) or a local host address (or even zero). +In the last case the router does not translate the packets, but +masquerades them to this address. + +.B Warning: +Changes to the RPDB made with these commands do not become active +immediately. It is assumed that after a script finishes a batch of +updates, it flushes the routing cache with +.BR "ip route flush cache" . + +.SS ip rule show - list rules +This command has no arguments. + +.SH ip maddress - multicast addresses management + +.B maddress +objects are multicast addresses. + +.SS ip maddress show - list multicast addresses + +.TP +.BI dev " NAME " (default) +the device name. + +.SS ip maddress add - add a multicast address +.SS ip maddress delete - delete a multicast address +these commands attach/detach a static link layer multicast address +to listen on the interface. +Note that it is impossible to join protocol multicast groups +statically. This command only manages link layer addresses. + +.TP +.BI address " LLADDRESS " (default) +the link layer multicast address. + +.TP +.BI dev " NAME" +the device to join/leave this multicast address. + +.SH ip mroute - multicast routing cache management +.B mroute +objects are multicast routing cache entries created by a user level +mrouting daemon (f.e. +.B pimd +or +.B mrouted +). + +Due to the limitations of the current interface to the multicast routing +engine, it is impossible to change +.B mroute +objects administratively, so we may only display them. This limitation +will be removed in the future. + +.SS ip mroute show - list mroute cache entries + +.TP +.BI to " PREFIX " (default) +the prefix selecting the destination multicast addresses to list. + +.TP +.BI iif " NAME" +the interface on which multicast packets are received. + +.TP +.BI from " PREFIX" +the prefix selecting the IP source addresses of the multicast route. + +.SH ip tunnel - tunnel configuration +.B tunnel +objects are tunnels, encapsulating packets in IPv4 packets and then +sending them over the IP infrastructure. + +.SS ip tunnel add - add a new tunnel +.SS ip tunnel change - change an existing tunnel +.SS ip tunnel delete - destroy a tunnel + +.TP +.BI name " NAME " (default) +select the tunnel device name. + +.TP +.BI mode " MODE" +set the tunnel mode. Three modes are currently available: +.BR ipip ", " sit " and " gre "." + +.TP +.BI remote " ADDRESS" +set the remote endpoint of the tunnel. + +.TP +.BI local " ADDRESS" +set the fixed local address for tunneled packets. +It must be an address on another interface of this host. + +.TP +.BI ttl " N" +set a fixed TTL +.I N +on tunneled packets. +.I N +is a number in the range 1--255. 0 is a special value +meaning that packets inherit the TTL value. +The default value is: +.BR "inherit" . + +.TP +.BI tos " T" +.TP +.BI dsfield " T" +set a fixed TOS +.I T +on tunneled packets. +The default value is: +.BR "inherit" . + +.TP +.BI dev " NAME" +bind the tunnel to the device +.I NAME +so that tunneled packets will only be routed via this device and will +not be able to escape to another device when the route to endpoint +changes. + +.TP +.B nopmtudisc +disable Path MTU Discovery on this tunnel. +It is enabled by default. Note that a fixed ttl is incompatible +with this option: tunnelling with a fixed ttl always makes pmtu +discovery. + +.TP +.BI key " K" +.TP +.BI ikey " K" +.TP +.BI okey " K" +.RB ( " only GRE tunnels " ) +use keyed GRE with key +.IR K ". " K +is either a number or an IP address-like dotted quad. +The +.B key +parameter sets the key to use in both directions. +The +.BR ikey " and " okey +parameters set different keys for input and output. + +.TP +.BR csum ", " icsum ", " ocsum +.RB ( " only GRE tunnels " ) +generate/require checksums for tunneled packets. +The +.B ocsum +flag calculates checksums for outgoing packets. +The +.B icsum +flag requires that all input packets have the correct +checksum. The +.B csum +flag is equivalent to the combination +.BR "icsum ocsum" . + +.TP +.BR seq ", " iseq ", " oseq +.RB ( " only GRE tunnels " ) +serialize packets. +The +.B oseq +flag enables sequencing of outgoing packets. +The +.B iseq +flag requires that all input packets are serialized. +The +.B seq +flag is equivalent to the combination +.BR "iseq oseq" . +.B It isn't work. Don't use it. + +.SS ip tunnel show - list tunnels +This command has no arguments. + +.SH ip monitor and rtmon - state monitoring + +The +.B ip +utility can monitor the state of devices, addresses +and routes continuously. This option has a slightly different format. +Namely, the +.B monitor +command is the first in the command line and then the object list follows: + +.BR "ip monitor" " [ " all " |" +.IR LISTofOBJECTS " ]" + +.I OBJECT-LIST +is the list of object types that we want to monitor. +It may contain +.BR link ", " address " and " route "." +If no +.B file +argument is given, +.B ip +opens RTNETLINK, listens on it and dumps state changes in the format +described in previous sections. + +.P +If a file name is given, it does not listen on RTNETLINK, +but opens the file containing RTNETLINK messages saved in binary format +and dumps them. Such a history file can be generated with the +.B rtmon +utility. This utility has a command line syntax similar to +.BR "ip monitor" . +Ideally, +.B rtmon +should be started before the first network configuration command +is issued. F.e. if you insert: +.sp +.in +8 +rtmon file /var/log/rtmon.log +.in -8 +.sp +in a startup script, you will be able to view the full history +later. + +.P +Certainly, it is possible to start +.B rtmon +at any time. +It prepends the history with the state snapshot dumped at the moment +of starting. + +.SH HISTORY +.B ip +was written by Alexey N. Kuznetsov and added in Linux 2.2. +.SH SEE ALSO +.BR tc (8) +.br +.RB "IP Command reference " ip-cref.ps +.br +.RB "IP tunnels " ip-cref.ps + +.SH AUTHOR +Original Manpage by Michail Litvak <mci@owl.openwall.com> diff --git a/man/man8/tc-cbq-details.8 b/man/man8/tc-cbq-details.8 new file mode 100644 index 0000000..e47da62 --- /dev/null +++ b/man/man8/tc-cbq-details.8 @@ -0,0 +1,425 @@ +.TH CBQ 8 "8 December 2001" "iproute2" "Linux" +.SH NAME +CBQ \- Class Based Queueing +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] cbq avpkt +bytes +.B bandwidth +rate +.B [ cell +bytes +.B ] [ ewma +log +.B ] [ mpu +bytes +.B ] + +.B tc class ... dev +dev +.B parent +major:[minor] +.B [ classid +major:minor +.B ] cbq allot +bytes +.B [ bandwidth +rate +.B ] [ rate +rate +.B ] prio +priority +.B [ weight +weight +.B ] [ minburst +packets +.B ] [ maxburst +packets +.B ] [ ewma +log +.B ] [ cell +bytes +.B ] avpkt +bytes +.B [ mpu +bytes +.B ] [ bounded isolated ] [ split +handle +.B & defmap +defmap +.B ] [ estimator +interval timeconstant +.B ] + +.SH DESCRIPTION +Class Based Queueing is a classful qdisc that implements a rich +linksharing hierarchy of classes. It contains shaping elements as +well as prioritizing capabilities. Shaping is performed using link +idle time calculations based on the timing of dequeue events and +underlying link bandwidth. + +.SH SHAPING ALGORITHM +Shaping is done using link idle time calculations, and actions taken if +these calculations deviate from set limits. + +When shaping a 10mbit/s connection to 1mbit/s, the link will +be idle 90% of the time. If it isn't, it needs to be throttled so that it +IS idle 90% of the time. + +From the kernel's perspective, this is hard to measure, so CBQ instead +derives the idle time from the number of microseconds (in fact, jiffies) +that elapse between requests from the device driver for more data. Combined +with the knowledge of packet sizes, this is used to approximate how full or +empty the link is. + +This is rather circumspect and doesn't always arrive at proper +results. For example, what is the actual link speed of an interface +that is not really able to transmit the full 100mbit/s of data, +perhaps because of a badly implemented driver? A PCMCIA network card +will also never achieve 100mbit/s because of the way the bus is +designed - again, how do we calculate the idle time? + +The physical link bandwidth may be ill defined in case of not-quite-real +network devices like PPP over Ethernet or PPTP over TCP/IP. The effective +bandwidth in that case is probably determined by the efficiency of pipes +to userspace - which not defined. + +During operations, the effective idletime is measured using an +exponential weighted moving average (EWMA), which considers recent +packets to be exponentially more important than past ones. The Unix +loadaverage is calculated in the same way. + +The calculated idle time is subtracted from the EWMA measured one, +the resulting number is called 'avgidle'. A perfectly loaded link has +an avgidle of zero: packets arrive exactly at the calculated +interval. + +An overloaded link has a negative avgidle and if it gets too negative, +CBQ throttles and is then 'overlimit'. + +Conversely, an idle link might amass a huge avgidle, which would then +allow infinite bandwidths after a few hours of silence. To prevent +this, avgidle is capped at +.B maxidle. + +If overlimit, in theory, the CBQ could throttle itself for exactly the +amount of time that was calculated to pass between packets, and then +pass one packet, and throttle again. Due to timer resolution constraints, +this may not be feasible, see the +.B minburst +parameter below. + +.SH CLASSIFICATION +Within the one CBQ instance many classes may exist. Each of these classes +contains another qdisc, by default +.BR tc-pfifo (8). + +When enqueueing a packet, CBQ starts at the root and uses various methods to +determine which class should receive the data. If a verdict is reached, this +process is repeated for the recipient class which might have further +means of classifying traffic to its children, if any. + +CBQ has the following methods available to classify a packet to any child +classes. +.TP +(i) +.B skb->priority class encoding. +Can be set from userspace by an application with the +.B SO_PRIORITY +setsockopt. +The +.B skb->priority class encoding +only applies if the skb->priority holds a major:minor handle of an existing +class within this qdisc. +.TP +(ii) +tc filters attached to the class. +.TP +(iii) +The defmap of a class, as set with the +.B split & defmap +parameters. The defmap may contain instructions for each possible Linux packet +priority. + +.P +Each class also has a +.B level. +Leaf nodes, attached to the bottom of the class hierarchy, have a level of 0. +.SH CLASSIFICATION ALGORITHM + +Classification is a loop, which terminates when a leaf class is found. At any +point the loop may jump to the fallback algorithm. + +The loop consists of the following steps: +.TP +(i) +If the packet is generated locally and has a valid classid encoded within its +.B skb->priority, +choose it and terminate. + +.TP +(ii) +Consult the tc filters, if any, attached to this child. If these return +a class which is not a leaf class, restart loop from the class returned. +If it is a leaf, choose it and terminate. +.TP +(iii) +If the tc filters did not return a class, but did return a classid, +try to find a class with that id within this qdisc. +Check if the found class is of a lower +.B level +than the current class. If so, and the returned class is not a leaf node, +restart the loop at the found class. If it is a leaf node, terminate. +If we found an upward reference to a higher level, enter the fallback +algorithm. +.TP +(iv) +If the tc filters did not return a class, nor a valid reference to one, +consider the minor number of the reference to be the priority. Retrieve +a class from the defmap of this class for the priority. If this did not +contain a class, consult the defmap of this class for the +.B BEST_EFFORT +class. If this is an upward reference, or no +.B BEST_EFFORT +class was defined, +enter the fallback algorithm. If a valid class was found, and it is not a +leaf node, restart the loop at this class. If it is a leaf, choose it and +terminate. If +neither the priority distilled from the classid, nor the +.B BEST_EFFORT +priority yielded a class, enter the fallback algorithm. +.P +The fallback algorithm resides outside of the loop and is as follows. +.TP +(i) +Consult the defmap of the class at which the jump to fallback occured. If +the defmap contains a class for the +.B +priority +of the class (which is related to the TOS field), choose this class and +terminate. +.TP +(ii) +Consult the map for a class for the +.B BEST_EFFORT +priority. If found, choose it, and terminate. +.TP +(iii) +Choose the class at which break out to the fallback algorithm occured. Terminate. +.P +The packet is enqueued to the class which was chosen when either algorithm +terminated. It is therefore possible for a packet to be enqueued *not* at a +leaf node, but in the middle of the hierarchy. + +.SH LINK SHARING ALGORITHM +When dequeuing for sending to the network device, CBQ decides which of its +classes will be allowed to send. It does so with a Weighted Round Robin process +in which each class with packets gets a chance to send in turn. The WRR process +starts by asking the highest priority classes (lowest numerically - +highest semantically) for packets, and will continue to do so until they +have no more data to offer, in which case the process repeats for lower +priorities. + +.B CERTAINTY ENDS HERE, ANK PLEASE HELP + +Each class is not allowed to send at length though - they can only dequeue a +configurable amount of data during each round. + +If a class is about to go overlimit, and it is not +.B bounded +it will try to borrow avgidle from siblings that are not +.B isolated. +This process is repeated from the bottom upwards. If a class is unable +to borrow enough avgidle to send a packet, it is throttled and not asked +for a packet for enough time for the avgidle to increase above zero. + +.B I REALLY NEED HELP FIGURING THIS OUT. REST OF DOCUMENT IS PRETTY CERTAIN +.B AGAIN. + +.SH QDISC +The root qdisc of a CBQ class tree has the following parameters: + +.TP +parent major:minor | root +This mandatory parameter determines the place of the CBQ instance, either at the +.B root +of an interface or within an existing class. +.TP +handle major: +Like all other qdiscs, the CBQ can be assigned a handle. Should consist only +of a major number, followed by a colon. Optional. +.TP +avpkt bytes +For calculations, the average packet size must be known. It is silently capped +at a minimum of 2/3 of the interface MTU. Mandatory. +.TP +bandwidth rate +To determine the idle time, CBQ must know the bandwidth of your underlying +physical interface, or parent qdisc. This is a vital parameter, more about it +later. Mandatory. +.TP +cell +The cell size determines he granularity of packet transmission time calculations. Has a sensible default. +.TP +mpu +A zero sized packet may still take time to transmit. This value is the lower +cap for packet transmission time calculations - packets smaller than this value +are still deemed to have this size. Defaults to zero. +.TP +ewma log +When CBQ needs to measure the average idle time, it does so using an +Exponentially Weighted Moving Average which smoothes out measurements into +a moving average. The EWMA LOG determines how much smoothing occurs. Defaults +to 5. Lower values imply greater sensitivity. Must be between 0 and 31. +.P +A CBQ qdisc does not shape out of its own accord. It only needs to know certain +parameters about the underlying link. Actual shaping is done in classes. + +.SH CLASSES +Classes have a host of parameters to configure their operation. + +.TP +parent major:minor +Place of this class within the hierarchy. If attached directly to a qdisc +and not to another class, minor can be omitted. Mandatory. +.TP +classid major:minor +Like qdiscs, classes can be named. The major number must be equal to the +major number of the qdisc to which it belongs. Optional, but needed if this +class is going to have children. +.TP +weight weight +When dequeuing to the interface, classes are tried for traffic in a +round-robin fashion. Classes with a higher configured qdisc will generally +have more traffic to offer during each round, so it makes sense to allow +it to dequeue more traffic. All weights under a class are normalized, so +only the ratios matter. Defaults to the configured rate, unless the priority +of this class is maximal, in which case it is set to 1. +.TP +allot bytes +Allot specifies how many bytes a qdisc can dequeue +during each round of the process. This parameter is weighted using the +renormalized class weight described above. + +.TP +priority priority +In the round-robin process, classes with the lowest priority field are tried +for packets first. Mandatory. + +.TP +rate rate +Maximum rate this class and all its children combined can send at. Mandatory. + +.TP +bandwidth rate +This is different from the bandwidth specified when creating a CBQ disc. Only +used to determine maxidle and offtime, which are only calculated when +specifying maxburst or minburst. Mandatory if specifying maxburst or minburst. + +.TP +maxburst +This number of packets is used to calculate maxidle so that when +avgidle is at maxidle, this number of average packets can be burst +before avgidle drops to 0. Set it higher to be more tolerant of +bursts. You can't set maxidle directly, only via this parameter. + +.TP +minburst +As mentioned before, CBQ needs to throttle in case of +overlimit. The ideal solution is to do so for exactly the calculated +idle time, and pass 1 packet. However, Unix kernels generally have a +hard time scheduling events shorter than 10ms, so it is better to +throttle for a longer period, and then pass minburst packets in one +go, and then sleep minburst times longer. + +The time to wait is called the offtime. Higher values of minburst lead +to more accurate shaping in the long term, but to bigger bursts at +millisecond timescales. + +.TP +minidle +If avgidle is below 0, we are overlimits and need to wait until +avgidle will be big enough to send one packet. To prevent a sudden +burst from shutting down the link for a prolonged period of time, +avgidle is reset to minidle if it gets too low. + +Minidle is specified in negative microseconds, so 10 means that +avgidle is capped at -10us. + +.TP +bounded +Signifies that this class will not borrow bandwidth from its siblings. +.TP +isolated +Means that this class will not borrow bandwidth to its siblings + +.TP +split major:minor & defmap bitmap[/bitmap] +If consulting filters attached to a class did not give a verdict, +CBQ can also classify based on the packet's priority. There are 16 +priorities available, numbered from 0 to 15. + +The defmap specifies which priorities this class wants to receive, +specified as a bitmap. The Least Significant Bit corresponds to priority +zero. The +.B split +parameter tells CBQ at which class the decision must be made, which should +be a (grand)parent of the class you are adding. + +As an example, 'tc class add ... classid 10:1 cbq .. split 10:0 defmap c0' +configures class 10:0 to send packets with priorities 6 and 7 to 10:1. + +The complimentary configuration would then +be: 'tc class add ... classid 10:2 cbq ... split 10:0 defmap 3f' +Which would send all packets 0, 1, 2, 3, 4 and 5 to 10:1. +.TP +estimator interval timeconstant +CBQ can measure how much bandwidth each class is using, which tc filters +can use to classify packets with. In order to determine the bandwidth +it uses a very simple estimator that measures once every +.B interval +microseconds how much traffic has passed. This again is a EWMA, for which +the time constant can be specified, also in microseconds. The +.B time constant +corresponds to the sluggishness of the measurement or, conversely, to the +sensitivity of the average to short bursts. Higher values mean less +sensitivity. + + + +.SH SOURCES +.TP +o +Sally Floyd and Van Jacobson, "Link-sharing and Resource +Management Models for Packet Networks", +IEEE/ACM Transactions on Networking, Vol.3, No.4, 1995 + +.TP +o +Sally Floyd, "Notes on CBQ and Guarantee Service", 1995 + +.TP +o +Sally Floyd, "Notes on Class-Based Queueing: Setting +Parameters", 1996 + +.TP +o +Sally Floyd and Michael Speer, "Experimental Results +for Class-Based Queueing", 1998, not published. + + + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-cbq.8 b/man/man8/tc-cbq.8 new file mode 100644 index 0000000..79fb93b --- /dev/null +++ b/man/man8/tc-cbq.8 @@ -0,0 +1,353 @@ +.TH CBQ 8 "16 December 2001" "iproute2" "Linux" +.SH NAME +CBQ \- Class Based Queueing +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] cbq [ allot +bytes +.B ] avpkt +bytes +.B bandwidth +rate +.B [ cell +bytes +.B ] [ ewma +log +.B ] [ mpu +bytes +.B ] + +.B tc class ... dev +dev +.B parent +major:[minor] +.B [ classid +major:minor +.B ] cbq allot +bytes +.B [ bandwidth +rate +.B ] [ rate +rate +.B ] prio +priority +.B [ weight +weight +.B ] [ minburst +packets +.B ] [ maxburst +packets +.B ] [ ewma +log +.B ] [ cell +bytes +.B ] avpkt +bytes +.B [ mpu +bytes +.B ] [ bounded isolated ] [ split +handle +.B & defmap +defmap +.B ] [ estimator +interval timeconstant +.B ] + +.SH DESCRIPTION +Class Based Queueing is a classful qdisc that implements a rich +linksharing hierarchy of classes. It contains shaping elements as +well as prioritizing capabilities. Shaping is performed using link +idle time calculations based on the timing of dequeue events and +underlying link bandwidth. + +.SH SHAPING ALGORITHM +When shaping a 10mbit/s connection to 1mbit/s, the link will +be idle 90% of the time. If it isn't, it needs to be throttled so that it +IS idle 90% of the time. + +During operations, the effective idletime is measured using an +exponential weighted moving average (EWMA), which considers recent +packets to be exponentially more important than past ones. The Unix +loadaverage is calculated in the same way. + +The calculated idle time is subtracted from the EWMA measured one, +the resulting number is called 'avgidle'. A perfectly loaded link has +an avgidle of zero: packets arrive exactly at the calculated +interval. + +An overloaded link has a negative avgidle and if it gets too negative, +CBQ throttles and is then 'overlimit'. + +Conversely, an idle link might amass a huge avgidle, which would then +allow infinite bandwidths after a few hours of silence. To prevent +this, avgidle is capped at +.B maxidle. + +If overlimit, in theory, the CBQ could throttle itself for exactly the +amount of time that was calculated to pass between packets, and then +pass one packet, and throttle again. Due to timer resolution constraints, +this may not be feasible, see the +.B minburst +parameter below. + +.SH CLASSIFICATION +Within the one CBQ instance many classes may exist. Each of these classes +contains another qdisc, by default +.BR tc-pfifo (8). + +When enqueueing a packet, CBQ starts at the root and uses various methods to +determine which class should receive the data. + +In the absence of uncommon configuration options, the process is rather easy. +At each node we look for an instruction, and then go to the class the +instruction refers us to. If the class found is a barren leaf-node (without +children), we enqueue the packet there. If it is not yet a leaf node, we do +the whole thing over again starting from that node. + +The following actions are performed, in order at each node we visit, until one +sends us to another node, or terminates the process. +.TP +(i) +Consult filters attached to the class. If sent to a leafnode, we are done. +Otherwise, restart. +.TP +(ii) +Consult the defmap for the priority assigned to this packet, which depends +on the TOS bits. Check if the referral is leafless, otherwise restart. +.TP +(iii) +Ask the defmap for instructions for the 'best effort' priority. Check the +answer for leafness, otherwise restart. +.TP +(iv) +If none of the above returned with an instruction, enqueue at this node. +.P +This algorithm makes sure that a packet always ends up somewhere, even while +you are busy building your configuration. + +For more details, see +.BR tc-cbq-details(8). + +.SH LINK SHARING ALGORITHM +When dequeuing for sending to the network device, CBQ decides which of its +classes will be allowed to send. It does so with a Weighted Round Robin process +in which each class with packets gets a chance to send in turn. The WRR process +starts by asking the highest priority classes (lowest numerically - +highest semantically) for packets, and will continue to do so until they +have no more data to offer, in which case the process repeats for lower +priorities. + +Classes by default borrow bandwidth from their siblings. A class can be +prevented from doing so by declaring it 'bounded'. A class can also indicate +its unwillingness to lend out bandwidth by being 'isolated'. + +.SH QDISC +The root of a CBQ qdisc class tree has the following parameters: + +.TP +parent major:minor | root +This mandatory parameter determines the place of the CBQ instance, either at the +.B root +of an interface or within an existing class. +.TP +handle major: +Like all other qdiscs, the CBQ can be assigned a handle. Should consist only +of a major number, followed by a colon. Optional, but very useful if classes +will be generated within this qdisc. +.TP +allot bytes +This allotment is the 'chunkiness' of link sharing and is used for determining packet +transmission time tables. The qdisc allot differs slightly from the class allot discussed +below. Optional. Defaults to a reasonable value, related to avpkt. +.TP +avpkt bytes +The average size of a packet is needed for calculating maxidle, and is also used +for making sure 'allot' has a safe value. Mandatory. +.TP +bandwidth rate +To determine the idle time, CBQ must know the bandwidth of your underlying +physical interface, or parent qdisc. This is a vital parameter, more about it +later. Mandatory. +.TP +cell +The cell size determines he granularity of packet transmission time calculations. Has a sensible default. +.TP +mpu +A zero sized packet may still take time to transmit. This value is the lower +cap for packet transmission time calculations - packets smaller than this value +are still deemed to have this size. Defaults to zero. +.TP +ewma log +When CBQ needs to measure the average idle time, it does so using an +Exponentially Weighted Moving Average which smoothes out measurements into +a moving average. The EWMA LOG determines how much smoothing occurs. Lower +values imply greater sensitivity. Must be between 0 and 31. Defaults +to 5. +.P +A CBQ qdisc does not shape out of its own accord. It only needs to know certain +parameters about the underlying link. Actual shaping is done in classes. + +.SH CLASSES +Classes have a host of parameters to configure their operation. + +.TP +parent major:minor +Place of this class within the hierarchy. If attached directly to a qdisc +and not to another class, minor can be omitted. Mandatory. +.TP +classid major:minor +Like qdiscs, classes can be named. The major number must be equal to the +major number of the qdisc to which it belongs. Optional, but needed if this +class is going to have children. +.TP +weight weight +When dequeuing to the interface, classes are tried for traffic in a +round-robin fashion. Classes with a higher configured qdisc will generally +have more traffic to offer during each round, so it makes sense to allow +it to dequeue more traffic. All weights under a class are normalized, so +only the ratios matter. Defaults to the configured rate, unless the priority +of this class is maximal, in which case it is set to 1. +.TP +allot bytes +Allot specifies how many bytes a qdisc can dequeue +during each round of the process. This parameter is weighted using the +renormalized class weight described above. Silently capped at a minimum of +3/2 avpkt. Mandatory. + +.TP +prio priority +In the round-robin process, classes with the lowest priority field are tried +for packets first. Mandatory. + +.TP +avpkt +See the QDISC section. + +.TP +rate rate +Maximum rate this class and all its children combined can send at. Mandatory. + +.TP +bandwidth rate +This is different from the bandwidth specified when creating a CBQ disc! Only +used to determine maxidle and offtime, which are only calculated when +specifying maxburst or minburst. Mandatory if specifying maxburst or minburst. + +.TP +maxburst +This number of packets is used to calculate maxidle so that when +avgidle is at maxidle, this number of average packets can be burst +before avgidle drops to 0. Set it higher to be more tolerant of +bursts. You can't set maxidle directly, only via this parameter. + +.TP +minburst +As mentioned before, CBQ needs to throttle in case of +overlimit. The ideal solution is to do so for exactly the calculated +idle time, and pass 1 packet. However, Unix kernels generally have a +hard time scheduling events shorter than 10ms, so it is better to +throttle for a longer period, and then pass minburst packets in one +go, and then sleep minburst times longer. + +The time to wait is called the offtime. Higher values of minburst lead +to more accurate shaping in the long term, but to bigger bursts at +millisecond timescales. Optional. + +.TP +minidle +If avgidle is below 0, we are overlimits and need to wait until +avgidle will be big enough to send one packet. To prevent a sudden +burst from shutting down the link for a prolonged period of time, +avgidle is reset to minidle if it gets too low. + +Minidle is specified in negative microseconds, so 10 means that +avgidle is capped at -10us. Optional. + +.TP +bounded +Signifies that this class will not borrow bandwidth from its siblings. +.TP +isolated +Means that this class will not borrow bandwidth to its siblings + +.TP +split major:minor & defmap bitmap[/bitmap] +If consulting filters attached to a class did not give a verdict, +CBQ can also classify based on the packet's priority. There are 16 +priorities available, numbered from 0 to 15. + +The defmap specifies which priorities this class wants to receive, +specified as a bitmap. The Least Significant Bit corresponds to priority +zero. The +.B split +parameter tells CBQ at which class the decision must be made, which should +be a (grand)parent of the class you are adding. + +As an example, 'tc class add ... classid 10:1 cbq .. split 10:0 defmap c0' +configures class 10:0 to send packets with priorities 6 and 7 to 10:1. + +The complimentary configuration would then +be: 'tc class add ... classid 10:2 cbq ... split 10:0 defmap 3f' +Which would send all packets 0, 1, 2, 3, 4 and 5 to 10:1. +.TP +estimator interval timeconstant +CBQ can measure how much bandwidth each class is using, which tc filters +can use to classify packets with. In order to determine the bandwidth +it uses a very simple estimator that measures once every +.B interval +microseconds how much traffic has passed. This again is a EWMA, for which +the time constant can be specified, also in microseconds. The +.B time constant +corresponds to the sluggishness of the measurement or, conversely, to the +sensitivity of the average to short bursts. Higher values mean less +sensitivity. + +.SH BUGS +The actual bandwidth of the underlying link may not be known, for example +in the case of PPoE or PPTP connections which in fact may send over a +pipe, instead of over a physical device. CBQ is quite resilient to major +errors in the configured bandwidth, probably a the cost of coarser shaping. + +Default kernels rely on coarse timing information for making decisions. These +may make shaping precise in the long term, but inaccurate on second long scales. + +See +.BR tc-cbq-details(8) +for hints on how to improve this. + +.SH SOURCES +.TP +o +Sally Floyd and Van Jacobson, "Link-sharing and Resource +Management Models for Packet Networks", +IEEE/ACM Transactions on Networking, Vol.3, No.4, 1995 + +.TP +o +Sally Floyd, "Notes on CBQ and Guaranteed Service", 1995 + +.TP +o +Sally Floyd, "Notes on Class-Based Queueing: Setting +Parameters", 1996 + +.TP +o +Sally Floyd and Michael Speer, "Experimental Results +for Class-Based Queueing", 1998, not published. + + + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-htb.8 b/man/man8/tc-htb.8 new file mode 100644 index 0000000..f61b818 --- /dev/null +++ b/man/man8/tc-htb.8 @@ -0,0 +1,150 @@ +.TH HTB 8 "10 January 2002" "iproute2" "Linux" +.SH NAME +HTB \- Hierarchy Token Bucket +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] htb [ default +minor-id +.B ] + +.B tc class ... dev +dev +.B parent +major:[minor] +.B [ classid +major:minor +.B ] htb rate +rate +.B [ ceil +rate +.B ] burst +bytes +.B [ cburst +bytes +.B ] [ prio +priority +.B ] + +.SH DESCRIPTION +HTB is meant as a more understandable and intuitive replacement for +the CBQ qdisc in Linux. Both CBQ and HTB help you to control the use +of the outbound bandwidth on a given link. Both allow you to use one +physical link to simulate several slower links and to send different +kinds of traffic on different simulated links. In both cases, you have +to specify how to divide the physical link into simulated links and +how to decide which simulated link to use for a given packet to be sent. + +Unlike CBQ, HTB shapes traffic based on the Token Bucket Filter algorithm +which does not depend on interface characteristics and so does not need to +know the underlying bandwidth of the outgoing interface. + +.SH SHAPING ALGORITHM +Shaping works as documented in +.B tc-tbf (8). + +.SH CLASSIFICATION +Within the one HRB instance many classes may exist. Each of these classes +contains another qdisc, by default +.BR tc-pfifo (8). + +When enqueueing a packet, HTB starts at the root and uses various methods to +determine which class should receive the data. + +In the absence of uncommon configuration options, the process is rather easy. +At each node we look for an instruction, and then go to the class the +instruction refers us to. If the class found is a barren leaf-node (without +children), we enqueue the packet there. If it is not yet a leaf node, we do +the whole thing over again starting from that node. + +The following actions are performed, in order at each node we visit, until one +sends us to another node, or terminates the process. +.TP +(i) +Consult filters attached to the class. If sent to a leafnode, we are done. +Otherwise, restart. +.TP +(ii) +If none of the above returned with an instruction, enqueue at this node. +.P +This algorithm makes sure that a packet always ends up somewhere, even while +you are busy building your configuration. + +.SH LINK SHARING ALGORITHM +FIXME + +.SH QDISC +The root of a HTB qdisc class tree has the following parameters: + +.TP +parent major:minor | root +This mandatory parameter determines the place of the HTB instance, either at the +.B root +of an interface or within an existing class. +.TP +handle major: +Like all other qdiscs, the HTB can be assigned a handle. Should consist only +of a major number, followed by a colon. Optional, but very useful if classes +will be generated within this qdisc. +.TP +default minor-id +Unclassified traffic gets sent to the class with this minor-id. + +.SH CLASSES +Classes have a host of parameters to configure their operation. + +.TP +parent major:minor +Place of this class within the hierarchy. If attached directly to a qdisc +and not to another class, minor can be omitted. Mandatory. +.TP +classid major:minor +Like qdiscs, classes can be named. The major number must be equal to the +major number of the qdisc to which it belongs. Optional, but needed if this +class is going to have children. +.TP +prio priority +In the round-robin process, classes with the lowest priority field are tried +for packets first. Mandatory. + +.TP +rate rate +Maximum rate this class and all its children are guaranteed. Mandatory. + +.TP +ceil rate +Maximum rate at which a class can send, if its parent has bandwidth to spare. +Defaults to the configured rate, which implies no borrowing + +.TP +burst bytes +Amount of bytes that can be burst at +.B ceil +speed, in excess of the configured +.B rate. +Should be at least as high as the highest burst of all children. + +.TP +cburst bytes +Amount of bytes that can be burst at 'infinite' speed, in other words, as fast +as the interface can transmit them. For perfect evening out, should be equal to at most one average +packet. Should be at least as high as the highest cburst of all children. + +.SH NOTES +Due to Unix timing constraints, the maximum ceil rate is not infinite and may in fact be quite low. On Intel, +there are 100 timer events per second, the maximum rate is that rate at which 'burst' bytes are sent each timer tick. +From this, the mininum burst size for a specified rate can be calculated. For i386, a 10mbit rate requires a 12 kilobyte +burst as 100*12kb*8 equals 10mbit. + +.SH SEE ALSO +.BR tc (8) +.P +HTB website: http://luxik.cdi.cz/~devik/qos/htb/ +.SH AUTHOR +Martin Devera <devik@cdi.cz>. This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-pbfifo.8 b/man/man8/tc-pbfifo.8 new file mode 100644 index 0000000..8dda4bb --- /dev/null +++ b/man/man8/tc-pbfifo.8 @@ -0,0 +1,72 @@ +.TH PBFIFO 8 "10 January 2002" "iproute2" "Linux" +.SH NAME +pfifo \- Packet limited First In, First Out queue +.P +bfifo \- Byte limited First In, First Out queue + +.SH SYNOPSIS +.B tc qdisc ... add pfifo +.B [ limit +packets +.B ] +.P +.B tc qdisc ... add bfifo +.B [ limit +bytes +.B ] + +.SH DESCRIPTION +The pfifo and bfifo qdiscs are unadorned First In, First Out queues. They are the +simplest queues possible and therefore have no overhead. +.B pfifo +constrains the queue size as measured in packets. +.B bfifo +does so as measured in bytes. + +Like all non-default qdiscs, they maintain statistics. This might be a reason to prefer +pfifo or bfifo over the default. + +.SH ALGORITHM +A list of packets is maintained, when a packet is enqueued it gets inserted at the tail of +a list. When a packet needs to be sent out to the network, it is taken from the head of the list. + +If the list is too long, no further packets are allowed on. This is called 'tail drop'. + +.SH PARAMETERS +.TP +limit +Maximum queue size. Specified in bytes for bfifo, in packets for pfifo. For pfifo, defaults +to the interface txqueuelen, as specified with +.BR ifconfig (8) +or +.BR ip (8). + +For bfifo, it defaults to the txqueuelen multiplied by the interface MTU. + +.SH OUTPUT +The output of +.B tc -s qdisc ls +contains the limit, either in packets or in bytes, and the number of bytes +and packets actually sent. An unsent and dropped packet only appears between braces +and is not counted as 'Sent'. + +In this example, the queue length is 100 packets, 45894 bytes were sent over 681 packets. +No packets were dropped, and as the pfifo queue does not slow down packets, there were also no +overlimits: +.P +.nf +# tc -s qdisc ls dev eth0 +qdisc pfifo 8001: dev eth0 limit 100p + Sent 45894 bytes 681 pkts (dropped 0, overlimits 0) +.fi + +If a backlog occurs, this is displayed as well. +.SH SEE ALSO +.BR tc (8) + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru> + +This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-pfifo_fast.8 b/man/man8/tc-pfifo_fast.8 new file mode 100644 index 0000000..43ab166 --- /dev/null +++ b/man/man8/tc-pfifo_fast.8 @@ -0,0 +1,59 @@ +.TH PFIFO_FAST 8 "10 January 2002" "iproute2" "Linux" +.SH NAME +pfifo_fast \- three-band first in, first out queue + +.SH DESCRIPTION +pfifo_fast is the default qdisc of each interface. + +Whenever an interface is created, the pfifo_fast qdisc is automatically used +as a queue. If another qdisc is attached, it preempts the default +pfifo_fast, which automatically returns to function when an existing qdisc +is detached. + +In this sense this qdisc is magic, and unlike other qdiscs. + +.SH ALGORITHM +The algorithm is very similar to that of the classful +.BR tc-prio (8) +qdisc. +.B pfifo_fast +is like three +.BR tc-pfifo (8) +queues side by side, where packets can be enqueued in any of the three bands +based on their Type of Service bits or assigned priority. + +Not all three bands are dequeued simultaneously - as long as lower bands +have traffic, higher bands are never dequeued. This can be used to +prioritize interactive traffic or penalize 'lowest cost' traffic. + +Each band can be txqueuelen packets long, as configured with +.BR ifconfig (8) +or +.BR ip (8). +Additional packets coming in are not enqueued but are instead dropped. + +See +.BR tc-prio (8) +for complete details on how TOS bits are translated into bands. +.SH PARAMETERS +.TP +txqueuelen +The length of the three bands depends on the interface txqueuelen, as +specified with +.BR ifconfig (8) +or +.BR ip (8). + +.SH BUGS +Does not maintain statistics and does not show up in tc qdisc ls. This is because +it is the automatic default in the absence of a configured qdisc. + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru> + +This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-prio.8 b/man/man8/tc-prio.8 new file mode 100644 index 0000000..e942e62 --- /dev/null +++ b/man/man8/tc-prio.8 @@ -0,0 +1,187 @@ +.TH PRIO 8 "16 December 2001" "iproute2" "Linux" +.SH NAME +PRIO \- Priority qdisc +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] prio [ bands +bands +.B ] [ priomap +band,band,band... +.B ] [ estimator +interval timeconstant +.B ] + +.SH DESCRIPTION +The PRIO qdisc is a simple classful queueing discipline that contains +an arbitrary number of classes of differing priority. The classes are +dequeued in numerical descending order of priority. PRIO is a scheduler +and never delays packets - it is a work-conserving qdisc, though the qdiscs +contained in the classes may not be. + +Very useful for lowering latency when there is no need for slowing down +traffic. + +.SH ALGORITHM +On creation with 'tc qdisc add', a fixed number of bands is created. Each +band is a class, although is not possible to add classes with 'tc qdisc +add', the number of bands to be created must instead be specified on the +commandline attaching PRIO to its root. + +When dequeueing, band 0 is tried first and only if it did not deliver a +packet does PRIO try band 1, and so onwards. Maximum reliability packets +should therefore go to band 0, minimum delay to band 1 and the rest to band +2. + +As the PRIO qdisc itself will have minor number 0, band 0 is actually +major:1, band 1 is major:2, etc. For major, substitute the major number +assigned to the qdisc on 'tc qdisc add' with the +.B handle +parameter. + +.SH CLASSIFICATION +Three methods are available to PRIO to determine in which band a packet will +be enqueued. +.TP +From userspace +A process with sufficient privileges can encode the destination class +directly with SO_PRIORITY, see +.BR tc(7). +.TP +with a tc filter +A tc filter attached to the root qdisc can point traffic directly to a class +.TP +with the priomap +Based on the packet priority, which in turn is derived from the Type of +Service assigned to the packet. +.P +Only the priomap is specific to this qdisc. +.SH QDISC PARAMETERS +.TP +bands +Number of bands. If changed from the default of 3, +.B priomap +must be updated as well. +.TP +priomap +The priomap maps the priority of +a packet to a class. The priority can either be set directly from userspace, +or be derived from the Type of Service of the packet. + +Determines how packet priorities, as assigned by the kernel, map to +bands. Mapping occurs based on the TOS octet of the packet, which looks like +this: + +.nf +0 1 2 3 4 5 6 7 ++---+---+---+---+---+---+---+---+ +| | | | +|PRECEDENCE | TOS |MBZ| +| | | | ++---+---+---+---+---+---+---+---+ +.fi + +The four TOS bits (the 'TOS field') are defined as: + +.nf +Binary Decimcal Meaning +----------------------------------------- +1000 8 Minimize delay (md) +0100 4 Maximize throughput (mt) +0010 2 Maximize reliability (mr) +0001 1 Minimize monetary cost (mmc) +0000 0 Normal Service +.fi + +As there is 1 bit to the right of these four bits, the actual value of the +TOS field is double the value of the TOS bits. Tcpdump -v -v shows you the +value of the entire TOS field, not just the four bits. It is the value you +see in the first column of this table: + +.nf +TOS Bits Means Linux Priority Band +------------------------------------------------------------ +0x0 0 Normal Service 0 Best Effort 1 +0x2 1 Minimize Monetary Cost 1 Filler 2 +0x4 2 Maximize Reliability 0 Best Effort 1 +0x6 3 mmc+mr 0 Best Effort 1 +0x8 4 Maximize Throughput 2 Bulk 2 +0xa 5 mmc+mt 2 Bulk 2 +0xc 6 mr+mt 2 Bulk 2 +0xe 7 mmc+mr+mt 2 Bulk 2 +0x10 8 Minimize Delay 6 Interactive 0 +0x12 9 mmc+md 6 Interactive 0 +0x14 10 mr+md 6 Interactive 0 +0x16 11 mmc+mr+md 6 Interactive 0 +0x18 12 mt+md 4 Int. Bulk 1 +0x1a 13 mmc+mt+md 4 Int. Bulk 1 +0x1c 14 mr+mt+md 4 Int. Bulk 1 +0x1e 15 mmc+mr+mt+md 4 Int. Bulk 1 +.fi + +The second column contains the value of the relevant +four TOS bits, followed by their translated meaning. For example, 15 stands +for a packet wanting Minimal Montetary Cost, Maximum Reliability, Maximum +Throughput AND Minimum Delay. + +The fourth column lists the way the Linux kernel interprets the TOS bits, by +showing to which Priority they are mapped. + +The last column shows the result of the default priomap. On the commandline, +the default priomap looks like this: + + 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 + +This means that priority 4, for example, gets mapped to band number 1. +The priomap also allows you to list higher priorities (> 7) which do not +correspond to TOS mappings, but which are set by other means. + +This table from RFC 1349 (read it for more details) explains how +applications might very well set their TOS bits: + +.nf +TELNET 1000 (minimize delay) +FTP + Control 1000 (minimize delay) + Data 0100 (maximize throughput) + +TFTP 1000 (minimize delay) + +SMTP + Command phase 1000 (minimize delay) + DATA phase 0100 (maximize throughput) + +Domain Name Service + UDP Query 1000 (minimize delay) + TCP Query 0000 + Zone Transfer 0100 (maximize throughput) + +NNTP 0001 (minimize monetary cost) + +ICMP + Errors 0000 + Requests 0000 (mostly) + Responses <same as request> (mostly) +.fi + + +.SH CLASSES +PRIO classes cannot be configured further - they are automatically created +when the PRIO qdisc is attached. Each class however can contain yet a +further qdisc. + +.SH BUGS +Large amounts of traffic in the lower bands can cause starvation of higher +bands. Can be prevented by attaching a shaper (for example, +.BR tc-tbf(8) +to these bands to make sure they cannot dominate the link. + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>, J Hadi Salim +<hadi@cyberus.ca>. This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-red.8 b/man/man8/tc-red.8 new file mode 100644 index 0000000..d02b411 --- /dev/null +++ b/man/man8/tc-red.8 @@ -0,0 +1,131 @@ +.TH RED 8 "13 December 2001" "iproute2" "Linux" +.SH NAME +red \- Random Early Detection +.SH SYNOPSIS +.B tc qdisc ... red +.B limit +bytes +.B min +bytes +.B max +bytes +.B avpkt +bytes +.B burst +packets +.B [ ecn ] [ bandwidth +rate +.B ] probability +chance + +.SH DESCRIPTION +Random Early Detection is a classless qdisc which manages its queue size +smartly. Regular queues simply drop packets from the tail when they are +full, which may not be the optimal behaviour. RED also performs tail drop, +but does so in a more gradual way. + +Once the queue hits a certain average length, packets enqueued have a +configurable chance of being marked (which may mean dropped). This chance +increases linearly up to a point called the +.B max +average queue length, although the queue might get bigger. + +This has a host of benefits over simple taildrop, while not being processor +intensive. It prevents synchronous retransmits after a burst in traffic, +which cause further retransmits, etc. + +The goal is the have a small queue size, which is good for interactivity +while not disturbing TCP/IP traffic with too many sudden drops after a burst +of traffic. + +Depending on if ECN is configured, marking either means dropping or +purely marking a packet as overlimit. +.SH ALGORITHM +The average queue size is used for determining the marking +probability. This is calculated using an Exponential Weighted Moving +Average, which can be more or less sensitive to bursts. + +When the average queue size is below +.B min +bytes, no packet will ever be marked. When it exceeds +.B min, +the probability of doing so climbs linearly up +to +.B probability, +until the average queue size hits +.B max +bytes. Because +.B probability +is normally not set to 100%, the queue size might +conceivably rise above +.B max +bytes, so the +.B limit +parameter is provided to set a hard maximum for the size of the queue. + +.SH PARAMETERS +.TP +min +Average queue size at which marking becomes a possibility. +.TP +max +At this average queue size, the marking probability is maximal. Should be at +least twice +.B min +to prevent synchronous retransmits, higher for low +.B min. +.TP +probability +Maximum probability for marking, specified as a floating point +number from 0.0 to 1.0. Suggested values are 0.01 or 0.02 (1 or 2%, +respectively). +.TP +limit +Hard limit on the real (not average) queue size in bytes. Further packets +are dropped. Should be set higher than max+burst. It is advised to set this +a few times higher than +.B max. +.TP +burst +Used for determining how fast the average queue size is influenced by the +real queue size. Larger values make the calculation more sluggish, allowing +longer bursts of traffic before marking starts. Real life experiments +support the following guideline: (min+min+max)/(3*avpkt). +.TP +avpkt +Specified in bytes. Used with burst to determine the time constant for +average queue size calculations. 1000 is a good value. +.TP +bandwidth +This rate is used for calculating the average queue size after some +idle time. Should be set to the bandwidth of your interface. Does not mean +that RED will shape for you! Optional. +.TP +ecn +As mentioned before, RED can either 'mark' or 'drop'. Explicit Congestion +Notification allows RED to notify remote hosts that their rate exceeds the +amount of bandwidth available. Non-ECN capable hosts can only be notified by +dropping a packet. If this parameter is specified, packets which indicate +that their hosts honor ECN will only be marked and not dropped, unless the +queue size hits +.B limit +bytes. Needs a tc binary with RED support compiled in. Recommended. + +.SH SEE ALSO +.BR tc (8) + +.SH SOURCES +.TP +o +Floyd, S., and Jacobson, V., Random Early Detection gateways for +Congestion Avoidance. http://www.aciri.org/floyd/papers/red/red.html +.TP +o +Some changes to the algorithm by Alexey N. Kuznetsov. + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>, Alexey Makarenko +<makar@phoenix.kharkov.ua>, J Hadi Salim <hadi@nortelnetworks.com>. +This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-sfq.8 b/man/man8/tc-sfq.8 new file mode 100644 index 0000000..337c795 --- /dev/null +++ b/man/man8/tc-sfq.8 @@ -0,0 +1,107 @@ +.TH TC 8 "8 December 2001" "iproute2" "Linux" +.SH NAME +sfq \- Stochastic Fairness Queueing +.SH SYNOPSIS +.B tc qdisc ... perturb +seconds +.B quantum +bytes + +.SH DESCRIPTION + +Stochastic Fairness Queueing is a classless queueing discipline available for +traffic control with the +.BR tc (8) +command. + +SFQ does not shape traffic but only schedules the transmission of packets, based on 'flows'. +The goal is to ensure fairness so that each flow is able to send data in turn, thus preventing +any single flow from drowning out the rest. + +This may in fact have some effect in mitigating a Denial of Service attempt. + +SFQ is work-conserving and therefore always delivers a packet if it has one available. +.SH ALGORITHM +On enqueueing, each packet is assigned to a hash bucket, based on +.TP +(i) +Source address +.TP +(ii) +Destination address +.TP +(iii) +Source port +.P +If these are available. SFQ knows about ipv4 and ipv6 and also UDP, TCP and ESP. +Packets with other protocols are hashed based on the 32bits representation of their +destination and the socket they belong to. A flow corresponds mostly to a TCP/IP +connection. + +Each of these buckets should represent a unique flow. Because multiple flows may +get hashed to the same bucket, the hashing algorithm is perturbed at configurable +intervals so that the unfairness lasts only for a short while. Perturbation may +however cause some inadvertent packet reordering to occur. + +When dequeuing, each hashbucket with data is queried in a round robin fashion. + +The compile time maximum length of the SFQ is 128 packets, which can be spread over +at most 128 buckets of 1024 available. In case of overflow, tail-drop is performed +on the fullest bucket, thus maintaining fairness. + +.SH PARAMETERS +.TP +perturb +Interval in seconds for queue algorithm perturbation. Defaults to 0, which means that +no perturbation occurs. Do not set too low for each perturbation may cause some packet +reordering. Advised value: 10 +.TP +quantum +Amount of bytes a flow is allowed to dequeue during a round of the round robin process. +Defaults to the MTU of the interface which is also the advised value and the minimum value. + +.SH EXAMPLE & USAGE + +To attach to device ppp0: +.P +# tc qdisc add dev ppp0 root sfq perturb 10 +.P +Please note that SFQ, like all non-shaping (work-conserving) qdiscs, is only useful +if it owns the queue. +This is the case when the link speed equals the actually available bandwidth. This holds +for regular phone modems, ISDN connections and direct non-switched ethernet links. +.P +Most often, cable modems and DSL devices do not fall into this category. The same holds +for when connected to a switch and trying to send data to a congested segment also +connected to the switch. +.P +In this case, the effective queue does not reside within Linux and is therefore not +available for scheduling. +.P +Embed SFQ in a classful qdisc to make sure it owns the queue. + +.SH SOURCE +.TP +o +Paul E. McKenney "Stochastic Fairness Queuing", +IEEE INFOCOMM'90 Proceedings, San Francisco, 1990. + +.TP +o +Paul E. McKenney "Stochastic Fairness Queuing", +"Interworking: Research and Experience", v.2, 1991, p.113-131. + +.TP +o +See also: +M. Shreedhar and George Varghese "Efficient Fair +Queuing using Deficit Round Robin", Proc. SIGCOMM 95. + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-tbf.8 b/man/man8/tc-tbf.8 new file mode 100644 index 0000000..3abb238 --- /dev/null +++ b/man/man8/tc-tbf.8 @@ -0,0 +1,138 @@ +.TH TC 8 "13 December 2001" "iproute2" "Linux" +.SH NAME +tbf \- Token Bucket Filter +.SH SYNOPSIS +.B tc qdisc ... tbf rate +rate +.B burst +bytes/cell +.B ( latency +ms +.B | limit +bytes +.B ) [ mpu +bytes +.B [ peakrate +rate +.B mtu +bytes/cell +.B ] ] +.P +burst is also known as buffer and maxburst. mtu is also known as minburst. +.SH DESCRIPTION + +The Token Bucket Filter is a classless queueing discipline available for +traffic control with the +.BR tc (8) +command. + +TBF is a pure shaper and never schedules traffic. It is non-work-conserving and may throttle +itself, although packets are available, to ensure that the configured rate is not exceeded. +On all platforms except for Alpha, +it is able to shape up to 1mbit/s of normal traffic with ideal minimal burstiness, +sending out data exactly at the configured rates. + +Much higher rates are possible but at the cost of losing the minimal burstiness. In that +case, data is on average dequeued at the configured rate but may be sent much faster at millisecond +timescales. Because of further queues living in network adaptors, this is often not a problem. + +Kernels with a higher 'HZ' can achieve higher rates with perfect burstiness. On Alpha, HZ is ten +times higher, leading to a 10mbit/s limit to perfection. These calculations hold for packets of on +average 1000 bytes. + +.SH ALGORITHM +As the name implies, traffic is filtered based on the expenditure of +.B tokens. +Tokens roughly correspond to bytes, with the additional constraint that each packet consumes +some tokens, no matter how small it is. This reflects the fact that even a zero-sized packet occupies +the link for some time. + +On creation, the TBF is stocked with tokens which correspond to the amount of traffic that can be burst +in one go. Tokens arrive at a steady rate, until the bucket is full. + +If no tokens are available, packets are queued, up to a configured limit. The TBF now +calculates the token deficit, and throttles until the first packet in the queue can be sent. + +If it is not acceptable to burst out packets at maximum speed, a peakrate can be configured +to limit the speed at which the bucket empties. This peakrate is implemented as a second TBF +with a very small bucket, so that it doesn't burst. + +To achieve perfection, the second bucket may contain only a single packet, which leads to +the earlier mentioned 1mbit/s limit. + +This limit is caused by the fact that the kernel can only throttle for at minimum 1 'jiffy', which depends +on HZ as 1/HZ. For perfect shaping, only a single packet can get sent per jiffy - for HZ=100, this means 100 +packets of on average 1000 bytes each, which roughly corresponds to 1mbit/s. + +.SH PARAMETERS +See +.BR tc (8) +for how to specify the units of these values. +.TP +limit or latency +Limit is the number of bytes that can be queued waiting for tokens to become +available. You can also specify this the other way around by setting the +latency parameter, which specifies the maximum amount of time a packet can +sit in the TBF. The latter calculation takes into account the size of the +bucket, the rate and possibly the peakrate (if set). These two parameters +are mutually exclusive. +.TP +burst +Also known as buffer or maxburst. +Size of the bucket, in bytes. This is the maximum amount of bytes that tokens can be available for instantaneously. +In general, larger shaping rates require a larger buffer. For 10mbit/s on Intel, you need at least 10kbyte buffer +if you want to reach your configured rate! + +If your buffer is too small, packets may be dropped because more tokens arrive per timer tick than fit in your bucket. +The minimum buffer size can be calculated by dividing the rate by HZ. + +Token usage calculations are performed using a table which by default has a resolution of 8 packets. +This resolution can be changed by specifying the +.B cell +size with the burst. For example, to specify a 6000 byte buffer with a 16 +byte cell size, set a burst of 6000/16. You will probably never have to set +this. Must be an integral power of 2. +.TP +mpu +A zero-sized packet does not use zero bandwidth. For ethernet, no packet uses less than 64 bytes. The Minimum Packet Unit +determines the minimal token usage (specified in bytes) for a packet. Defaults to zero. +.TP +rate +The speed knob. See remarks above about limits! See +.BR tc (8) +for units. +.PP +Furthermore, if a peakrate is desired, the following parameters are available: + +.TP +peakrate +Maximum depletion rate of the bucket. Limited to 1mbit/s on Intel, 10mbit/s on Alpha. The peakrate does +not need to be set, it is only necessary if perfect millisecond timescale shaping is required. + +.TP +mtu/minburst +Specifies the size of the peakrate bucket. For perfect accuracy, should be set to the MTU of the interface. +If a peakrate is needed, but some burstiness is acceptable, this size can be raised. A 3000 byte minburst +allows around 3mbit/s of peakrate, given 1000 byte packets. + +Like the regular burstsize you can also specify a +.B cell +size. +.SH EXAMPLE & USAGE + +To attach a TBF with a sustained maximum rate of 0.5mbit/s, a peakrate of 1.0mbit/s, +a 5kilobyte buffer, with a pre-bucket queue size limit calculated so the TBF causes +at most 70ms of latency, with perfect peakrate behaviour, issue: +.P +# tc qdisc add dev eth0 root tbf rate 0.5mbit \\ + burst 5kb latency 70ms peakrate 1mbit \\ + minburst 1540 + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc.8 b/man/man8/tc.8 new file mode 100644 index 0000000..b9b8039 --- /dev/null +++ b/man/man8/tc.8 @@ -0,0 +1,348 @@ +.TH TC 8 "16 December 2001" "iproute2" "Linux" +.SH NAME +tc \- show / manipulate traffic control settings +.SH SYNOPSIS +.B tc qdisc [ add | change | replace | link ] dev +DEV +.B +[ parent +qdisc-id +.B | root ] +.B [ handle +qdisc-id ] qdisc +[ qdisc specific parameters ] +.P + +.B tc class [ add | change | replace ] dev +DEV +.B parent +qdisc-id +.B [ classid +class-id ] qdisc +[ qdisc specific parameters ] +.P + +.B tc filter [ add | change | replace ] dev +DEV +.B [ parent +qdisc-id +.B | root ] protocol +protocol +.B prio +priority filtertype +[ filtertype specific parameters ] +.B flowid +flow-id + +.B tc [-s | -d ] qdisc show [ dev +DEV +.B ] +.P +.B tc [-s | -d ] class show dev +DEV +.P +.B tc filter show dev +DEV + +.SH DESCRIPTION +.B Tc +is used to configure Traffic Control in the Linux kernel. Traffic Control consists +of the following: + +.TP +SHAPING +When traffic is shaped, its rate of transmission is under control. Shaping may +be more than lowering the available bandwidth - it is also used to smooth out +bursts in traffic for better network behaviour. Shaping occurs on egress. + +.TP +SCHEDULING +By scheduling the transmission of packets it is possible to improve interactivity +for traffic that needs it while still guaranteeing bandwidth to bulk transfers. Reordering +is also called prioritizing, and happens only on egress. + +.TP +POLICING +Where shaping deals with transmission of traffic, policing pertains to traffic +arriving. Policing thus occurs on ingress. + +.TP +DROPPING +Traffic exceeding a set bandwidth may also be dropped forthwith, both on +ingress and on egress. + +.P +Processing of traffic is controlled by three kinds of objects: qdiscs, +classes and filters. + +.SH QDISCS +.B qdisc +is short for 'queueing discipline' and it is elementary to +understanding traffic control. Whenever the kernel needs to send a +packet to an interface, it is +.B enqueued +to the qdisc configured for that interface. Immediately afterwards, the kernel +tries to get as many packets as possible from the qdisc, for giving them +to the network adaptor driver. + +A simple QDISC is the 'pfifo' one, which does no processing at all and is a pure +First In, First Out queue. It does however store traffic when the network interface +can't handle it momentarily. + +.SH CLASSES +Some qdiscs can contain classes, which contain further qdiscs - traffic may +then be enqueued in any of the inner qdiscs, which are within the +.B classes. +When the kernel tries to dequeue a packet from such a +.B classful qdisc +it can come from any of the classes. A qdisc may for example prioritize +certain kinds of traffic by trying to dequeue from certain classes +before others. + +.SH FILTERS +A +.B filter +is used by a classful qdisc to determine in which class a packet will +be enqueued. Whenever traffic arrives at a class with subclasses, it needs +to be classified. Various methods may be employed to do so, one of these +are the filters. All filters attached to the class are called, until one of +them returns with a verdict. If no verdict was made, other criteria may be +available. This differs per qdisc. + +It is important to notice that filters reside +.B within +qdiscs - they are not masters of what happens. + +.SH CLASSLESS QDISCS +The classless qdiscs are: +.TP +[p|b]fifo +Simplest usable qdisc, pure First In, First Out behaviour. Limited in +packets or in bytes. +.TP +pfifo_fast +Standard qdisc for 'Advanced Router' enabled kernels. Consists of a three-band +queue which honors Type of Service flags, as well as the priority that may be +assigned to a packet. +.TP +red +Random Early Detection simulates physical congestion by randomly dropping +packets when nearing configured bandwidth allocation. Well suited to very +large bandwidth applications. +.TP +sfq +Stochastic Fairness Queueing reorders queued traffic so each 'session' +gets to send a packet in turn. +.TP +tbf +The Token Bucket Filter is suited for slowing traffic down to a precisely +configured rate. Scales well to large bandwidths. +.SH CONFIGURING CLASSLESS QDISCS +In the absence of classful qdiscs, classless qdiscs can only be attached at +the root of a device. Full syntax: +.P +.B tc qdisc add dev +DEV +.B root +QDISC QDISC-PARAMETERS + +To remove, issue +.P +.B tc qdisc del dev +DEV +.B root + +The +.B pfifo_fast +qdisc is the automatic default in the absence of a configured qdisc. + +.SH CLASSFUL QDISCS +The classful qdiscs are: +.TP +CBQ +Class Based Queueing implements a rich linksharing hierarchy of classes. +It contains shaping elements as well as prioritizing capabilities. Shaping is +performed using link idle time calculations based on average packet size and +underlying link bandwidth. The latter may be ill-defined for some interfaces. +.TP +HTB +The Hierarchy Token Bucket implements a rich linksharing hierarchy of +classes with an emphasis on conforming to existing practices. HTB facilitates +guaranteeing bandwidth to classes, while also allowing specification of upper +limits to inter-class sharing. It contains shaping elements, based on TBF and +can prioritize classes. +.TP +PRIO +The PRIO qdisc is a non-shaping container for a configurable number of +classes which are dequeued in order. This allows for easy prioritization +of traffic, where lower classes are only able to send if higher ones have +no packets available. To facilitate configuration, Type Of Service bits are +honored by default. +.SH THEORY OF OPERATION +Classes form a tree, where each class has a single parent. +A class may have multiple children. Some qdiscs allow for runtime addition +of classes (CBQ, HTB) while others (PRIO) are created with a static number of +children. + +Qdiscs which allow dynamic addition of classes can have zero or more +subclasses to which traffic may be enqueued. + +Furthermore, each class contains a +.B leaf qdisc +which by default has +.B pfifo +behaviour though another qdisc can be attached in place. This qdisc may again +contain classes, but each class can have only one leaf qdisc. + +When a packet enters a classful qdisc it can be +.B classified +to one of the classes within. Three criteria are available, although not all +qdiscs will use all three: +.TP +tc filters +If tc filters are attached to a class, they are consulted first +for relevant instructions. Filters can match on all fields of a packet header, +as well as on the firewall mark applied by ipchains or iptables. See +.BR tc-filters (8). +.TP +Type of Service +Some qdiscs have built in rules for classifying packets based on the TOS field. +.TP +skb->priority +Userspace programs can encode a class-id in the 'skb->priority' field using +the SO_PRIORITY option. +.P +Each node within the tree can have its own filters but higher level filters +may also point directly to lower classes. + +If classification did not succeed, packets are enqueued to the leaf qdisc +attached to that class. Check qdisc specific manpages for details, however. + +.SH NAMING +All qdiscs, classes and filters have IDs, which can either be specified +or be automatically assigned. + +IDs consist of a major number and a minor number, separated by a colon. + +.TP +QDISCS +A qdisc, which potentially can have children, +gets assigned a major number, called a 'handle', leaving the minor +number namespace available for classes. The handle is expressed as '10:'. +It is customary to explicitly assign a handle to qdiscs expected to have +children. + +.TP +CLASSES +Classes residing under a qdisc share their qdisc major number, but each have +a separate minor number called a 'classid' that has no relation to their +parent classes, only to their parent qdisc. The same naming custom as for +qdiscs applies. + +.TP +FILTERS +Filters have a three part ID, which is only needed when using a hashed +filter hierarchy, for which see +.BR tc-filters (8). +.SH UNITS +All parameters accept a floating point number, possibly followed by a unit. +.P +Bandwidths or rates can be specified in: +.TP +kbps +Kilobytes per second +.TP +mbps +Megabytes per second +.TP +kbit +Kilobits per second +.TP +mbit +Megabits per second +.TP +bps or a bare number +Bytes per second +.P +Amounts of data can be specified in: +.TP +kb or k +Kilobytes +.TP +mb or m +Megabytes +.TP +mbit +Megabits +.TP +kbit +Kilobits +.TP +b or a bare number +Bytes. +.P +Lengths of time can be specified in: +.TP +s, sec or secs +Whole seconds +.TP +ms, msec or msecs +Milliseconds +.TP +us, usec, usecs or a bare number +Microseconds. + +.SH TC COMMANDS +The following commands are available for qdiscs, classes and filter: +.TP +add +Add a qdisc, class or filter to a node. For all entities, a +.B parent +must be passed, either by passing its ID or by attaching directly to the root of a device. +When creating a qdisc or a filter, it can be named with the +.B handle +parameter. A class is named with the +.B classid +parameter. + +.TP +remove +A qdisc can be removed by specifying its handle, which may also be 'root'. All subclasses and their leaf qdiscs +are automatically deleted, as well as any filters attached to them. + +.TP +change +Some entities can be modified 'in place'. Shares the syntax of 'add', with the exception +that the handle cannot be changed and neither can the parent. In other words, +.B +change +cannot move a node. + +.TP +replace +Performs a nearly atomic remove/add on an existing node id. If the node does not exist yet +it is created. + +.TP +link +Only available for qdiscs and performs a replace where the node +must exist already. + + +.SH HISTORY +.B tc +was written by Alexey N. Kuznetsov and added in Linux 2.2. +.SH SEE ALSO +.BR tc-cbq (8), +.BR tc-htb (8), +.BR tc-sfq (8), +.BR tc-red (8), +.BR tc-tbf (8), +.BR tc-pfifo (8), +.BR tc-bfifo (8), +.BR tc-pfifo_fast (8), +.BR tc-filters (8) + +.SH AUTHOR +Manpage maintained by bert hubert (ahu@ds9a.nl) + diff --git a/misc/Makefile b/misc/Makefile new file mode 100644 index 0000000..2ddf950 --- /dev/null +++ b/misc/Makefile @@ -0,0 +1,35 @@ +SSOBJ=ss.o ssfilter.o +LNSTATOBJ=lnstat.o lnstat_util.o + +TARGETS=ss nstat ifstat rtacct arpd lnstat + +include ../Config + +all: $(TARGETS) + +ss: $(SSOBJ) $(LIBUTIL) + +nstat: nstat.c + $(CC) $(CFLAGS) $(LDFLAGS) -o nstat nstat.c -lm + +ifstat: ifstat.c + $(CC) $(CFLAGS) $(LDFLAGS) -o ifstat ifstat.c $(LIBNETLINK) -lm + +rtacct: rtacct.c + $(CC) $(CFLAGS) $(LDFLAGS) -o rtacct rtacct.c $(LIBNETLINK) -lm + +arpd: arpd.c + $(CC) $(CFLAGS) -I$(DBM_INCLUDE) $(LDFLAGS) -o arpd arpd.c $(LIBNETLINK) -ldb -lpthread + +ssfilter.c: ssfilter.y + bison ssfilter.y -o ssfilter.c + +lnstat: $(LNSTATOBJ) + +install: all + install -m 0755 -s $(TARGETS) $(DESTDIR)$(SBINDIR) + ln -sf $(SBINDIR)/lnstat $(DESTDIR)$(SBINDIR)/rtstat + ln -sf $(SBINDIR)/lnstat $(DESTDIR)$(SBINDIR)/ctstat + +clean: + rm -f *.o $(TARGETS) ssfilter.c diff --git a/misc/arpd b/misc/arpd new file mode 100755 index 0000000..cb898a9 Binary files /dev/null and b/misc/arpd differ diff --git a/misc/arpd.c b/misc/arpd.c new file mode 100644 index 0000000..85b2a1c --- /dev/null +++ b/misc/arpd.c @@ -0,0 +1,846 @@ +/* + * arpd.c ARP helper daemon. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <syslog.h> +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <netdb.h> +#include <db_185.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <linux/if.h> +#include <linux/if_ether.h> +#include <linux/if_arp.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <linux/if_packet.h> +#include <linux/filter.h> + +#include "libnetlink.h" +#include "utils.h" + +int resolve_hosts; + +DB *dbase; +char *dbname = "/var/lib/arpd/arpd.db"; + +int ifnum; +int *ifvec; +char **ifnames; + +struct dbkey +{ + __u32 iface; + __u32 addr; +}; + +#define IS_NEG(x) (((__u8*)(x))[0] == 0xFF) +#define NEG_TIME(x) (((x)[2]<<24)|((x)[3]<<16)|((x)[4]<<8)|(x)[5]) +#define NEG_AGE(x) ((__u32)time(NULL) - NEG_TIME((__u8*)x)) +#define NEG_VALID(x) (NEG_AGE(x) < negative_timeout) +#define NEG_CNT(x) (((__u8*)(x))[1]) + +struct rtnl_handle rth; + +struct pollfd pset[2]; +int udp_sock = -1; + +volatile int do_exit; +volatile int do_sync; +volatile int do_stats; + +struct { + unsigned long arp_new; + unsigned long arp_change; + + unsigned long app_recv; + unsigned long app_success; + unsigned long app_bad; + unsigned long app_neg; + unsigned long app_suppressed; + + unsigned long kern_neg; + unsigned long kern_new; + unsigned long kern_change; + + unsigned long probes_sent; + unsigned long probes_suppressed; +} stats; + +int active_probing; +int negative_timeout = 60; +int no_kernel_broadcasts; +int broadcast_rate = 1000; +int broadcast_burst = 3000; + +void usage(void) +{ + fprintf(stderr, +"Usage: arpd [ -lk ] [ -a N ] [ -b dbase ] [ -f file ] [ interfaces ]\n"); + exit(1); +} + +int handle_if(int ifindex) +{ + int i; + + if (ifnum == 0) + return 1; + + for (i=0; i<ifnum; i++) + if (ifvec[i] == ifindex) + return 1; + return 0; +} + +int sysctl_adjusted; + +void do_sysctl_adjustments(void) +{ + int i; + + if (!ifnum) + return; + + for (i=0; i<ifnum; i++) { + char buf[128]; + FILE *fp; + + if (active_probing) { + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/mcast_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + if (no_kernel_broadcasts) + strcpy(buf, "0\n"); + else + sprintf(buf, "%d\n", active_probing>=2 ? 1 : 3-active_probing); + fputs(buf, fp); + fclose(fp); + } + } + + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + sprintf(buf, "%d\n", active_probing<=1 ? 1 : active_probing); + fputs(buf, fp); + fclose(fp); + } + } + sysctl_adjusted = 1; +} + +void undo_sysctl_adjustments(void) +{ + int i; + + if (!sysctl_adjusted) + return; + + for (i=0; i<ifnum; i++) { + char buf[128]; + FILE *fp; + + if (active_probing) { + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/mcast_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + strcpy(buf, "3\n"); + fputs(buf, fp); + fclose(fp); + } + } + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + strcpy(buf, "0\n"); + fputs(buf, fp); + fclose(fp); + } + } + sysctl_adjusted = 0; +} + + +int send_probe(int ifindex, __u32 addr) +{ + struct ifreq ifr; + struct sockaddr_in dst; + int len; + unsigned char buf[256]; + struct arphdr *ah = (struct arphdr*)buf; + unsigned char *p = (unsigned char *)(ah+1); + struct sockaddr_ll sll; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = ifindex; + if (ioctl(udp_sock, SIOCGIFNAME, &ifr)) + return -1; + if (ioctl(udp_sock, SIOCGIFHWADDR, &ifr)) + return -1; + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) + return -1; + if (setsockopt(udp_sock, SOL_SOCKET, SO_BINDTODEVICE, ifr.ifr_name, strlen(ifr.ifr_name)+1) < 0) + return -1; + + dst.sin_family = AF_INET; + dst.sin_port = htons(1025); + dst.sin_addr.s_addr = addr; + if (connect(udp_sock, (struct sockaddr*)&dst, sizeof(dst)) < 0) + return -1; + len = sizeof(dst); + if (getsockname(udp_sock, (struct sockaddr*)&dst, &len) < 0) + return -1; + + ah->ar_hrd = htons(ifr.ifr_hwaddr.sa_family); + ah->ar_pro = htons(ETH_P_IP); + ah->ar_hln = 6; + ah->ar_pln = 4; + ah->ar_op = htons(ARPOP_REQUEST); + + memcpy(p, ifr.ifr_hwaddr.sa_data, ah->ar_hln); + p += ah->ar_hln; + + memcpy(p, &dst.sin_addr, 4); + p+=4; + + sll.sll_family = AF_PACKET; + memset(sll.sll_addr, 0xFF, sizeof(sll.sll_addr)); + sll.sll_ifindex = ifindex; + sll.sll_protocol = htons(ETH_P_ARP); + memcpy(p, &sll.sll_addr, ah->ar_hln); + p+=ah->ar_hln; + + memcpy(p, &addr, 4); + p+=4; + + len = sendto(pset[0].fd, buf, p-buf, 0, (struct sockaddr*)&sll, sizeof(sll)); + if (len < 0) + return -1; + stats.probes_sent++; + return 0; +} + +/* Be very tough on sending probes: 1 per second with burst of 3. */ + +int queue_active_probe(int ifindex, __u32 addr) +{ + static struct timeval prev; + static int buckets; + struct timeval now; + + gettimeofday(&now, NULL); + if (prev.tv_sec) { + int diff = (now.tv_sec-prev.tv_sec)*1000+(now.tv_usec-prev.tv_usec)/1000; + buckets += diff; + } else { + buckets = broadcast_burst; + } + if (buckets > broadcast_burst) + buckets = broadcast_burst; + if (buckets >= broadcast_rate && !send_probe(ifindex, addr)) { + buckets -= broadcast_rate; + prev = now; + return 0; + } + stats.probes_suppressed++; + return -1; +} + +int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + + memset(&req.n, 0, sizeof(req.n)); + memset(&req.ndm, 0, sizeof(req.ndm)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWNEIGH; + req.ndm.ndm_family = AF_INET; + req.ndm.ndm_state = NUD_STALE; + req.ndm.ndm_ifindex = ifindex; + req.ndm.ndm_type = RTN_UNICAST; + + addattr_l(&req.n, sizeof(req), NDA_DST, &addr, 4); + addattr_l(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); + return rtnl_send(&rth, (char*)&req, req.n.nlmsg_len) <= 0; +} + +void prepare_neg_entry(__u8 *ndata, __u32 stamp) +{ + ndata[0] = 0xFF; + ndata[1] = 0; + ndata[2] = stamp>>24; + ndata[3] = stamp>>16; + ndata[4] = stamp>>8; + ndata[5] = stamp; +} + + +int do_one_request(struct nlmsghdr *n) +{ + struct ndmsg *ndm = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[NDA_MAX+1]; + struct dbkey key; + DBT dbkey, dbdat; + int do_acct = 0; + + if (n->nlmsg_type == NLMSG_DONE) { + dbase->sync(dbase, 0); + + /* Now we have at least mirror of kernel db, so that + * may start real resolution. + */ + do_sysctl_adjustments(); + return 0; + } + + if (n->nlmsg_type != RTM_GETNEIGH && n->nlmsg_type != RTM_NEWNEIGH) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ndm)); + if (len < 0) + return -1; + + if (ndm->ndm_family != AF_INET || + (ifnum && !handle_if(ndm->ndm_ifindex)) || + ndm->ndm_flags || + ndm->ndm_type != RTN_UNICAST || + !(ndm->ndm_state&~NUD_NOARP)) + return 0; + + parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len); + + if (!tb[NDA_DST]) + return 0; + + key.iface = ndm->ndm_ifindex; + memcpy(&key.addr, RTA_DATA(tb[NDA_DST]), 4); + dbkey.data = &key; + dbkey.size = sizeof(key); + + if (dbase->get(dbase, &dbkey, &dbdat, 0) != 0) { + dbdat.data = 0; + dbdat.size = 0; + } + + if (n->nlmsg_type == RTM_GETNEIGH) { + if (!(n->nlmsg_flags&NLM_F_REQUEST)) + return 0; + + if (!(ndm->ndm_state&(NUD_PROBE|NUD_INCOMPLETE))) { + stats.app_bad++; + return 0; + } + + if (ndm->ndm_state&NUD_PROBE) { + /* If we get this, kernel still has some valid + * address, but unicast probing failed and host + * is either dead or changed its mac address. + * Kernel is going to initiate broadcast resolution. + * OK, we invalidate our information as well. + */ + if (dbdat.data && !IS_NEG(dbdat.data)) + stats.app_neg++; + + dbase->del(dbase, &dbkey, 0); + } else { + /* If we get this kernel does not have any information. + * If we have something tell this to kernel. */ + stats.app_recv++; + if (dbdat.data && !IS_NEG(dbdat.data)) { + stats.app_success++; + respond_to_kernel(key.iface, key.addr, dbdat.data, dbdat.size); + return 0; + } + + /* Sheeit! We have nothing to tell. */ + /* If we have recent negative entry, be silent. */ + if (dbdat.data && NEG_VALID(dbdat.data)) { + if (NEG_CNT(dbdat.data) >= active_probing) { + stats.app_suppressed++; + return 0; + } + do_acct = 1; + } + } + + if (active_probing && + queue_active_probe(ndm->ndm_ifindex, key.addr) == 0 && + do_acct) { + NEG_CNT(dbdat.data)++; + dbase->put(dbase, &dbkey, &dbdat, 0); + } + } else if (n->nlmsg_type == RTM_NEWNEIGH) { + if (n->nlmsg_flags&NLM_F_REQUEST) + return 0; + + if (ndm->ndm_state&NUD_FAILED) { + /* Kernel was not able to resolve. Host is dead. + * Create negative entry if it is not present + * or renew it if it is too old. */ + if (!dbdat.data || + !IS_NEG(dbdat.data) || + !NEG_VALID(dbdat.data)) { + __u8 ndata[6]; + stats.kern_neg++; + prepare_neg_entry(ndata, time(NULL)); + dbdat.data = ndata; + dbdat.size = sizeof(ndata); + dbase->put(dbase, &dbkey, &dbdat, 0); + } + } else if (tb[NDA_LLADDR]) { + if (dbdat.data && !IS_NEG(dbdat.data)) { + if (memcmp(RTA_DATA(tb[NDA_LLADDR]), dbdat.data, dbdat.size) == 0) + return 0; + stats.kern_change++; + } else { + stats.kern_new++; + } + dbdat.data = RTA_DATA(tb[NDA_LLADDR]); + dbdat.size = RTA_PAYLOAD(tb[NDA_LLADDR]); + dbase->put(dbase, &dbkey, &dbdat, 0); + } + } + return 0; +} + +void load_initial_table(void) +{ + rtnl_wilddump_request(&rth, AF_INET, RTM_GETNEIGH); +} + +void get_kern_msg(void) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + char buf[8192]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + status = recvmsg(rth.fd, &msg, MSG_DONTWAIT); + + if (status <= 0) + return; + + if (msg.msg_namelen != sizeof(nladdr)) + return; + + if (nladdr.nl_pid) + return; + + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l < 0 || len > status) + return; + + if (do_one_request(h) < 0) + return; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } +} + +/* Receive gratuitous ARP messages and store them, that's all. */ +void get_arp_pkt(void) +{ + unsigned char buf[1024]; + struct sockaddr_ll sll; + int sll_len = sizeof(sll); + struct arphdr *a = (struct arphdr*)buf; + struct dbkey key; + DBT dbkey, dbdat; + int n; + + n = recvfrom(pset[0].fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sll, &sll_len); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + syslog(LOG_ERR, "recvfrom: %m"); + return; + } + + if (ifnum && !handle_if(sll.sll_ifindex)) + return; + + /* Sanity checks */ + + if (n < sizeof(*a) || + (a->ar_op != htons(ARPOP_REQUEST) && + a->ar_op != htons(ARPOP_REPLY)) || + a->ar_pln != 4 || + a->ar_pro != htons(ETH_P_IP) || + a->ar_hln != sll.sll_halen || + sizeof(*a) + 2*4 + 2*a->ar_hln > n) + return; + + key.iface = sll.sll_ifindex; + memcpy(&key.addr, (char*)(a+1) + a->ar_hln, 4); + + /* DAD message, ignore. */ + if (key.addr == 0) + return; + + dbkey.data = &key; + dbkey.size = sizeof(key); + + if (dbase->get(dbase, &dbkey, &dbdat, 0) == 0 && !IS_NEG(dbdat.data)) { + if (memcmp(dbdat.data, a+1, dbdat.size) == 0) + return; + stats.arp_change++; + } else { + stats.arp_new++; + } + + dbdat.data = a+1; + dbdat.size = a->ar_hln; + dbase->put(dbase, &dbkey, &dbdat, 0); +} + +void catch_signal(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; +#ifdef SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#endif + sigaction(sig, &sa, NULL); +} + +#include <setjmp.h> +sigjmp_buf env; +volatile int in_poll; + +void sig_exit(int signo) +{ + do_exit = 1; + if (in_poll) + siglongjmp(env, 1); +} + +void sig_sync(int signo) +{ + do_sync = 1; + if (in_poll) + siglongjmp(env, 1); +} + +void sig_stats(int signo) +{ + do_sync = 1; + do_stats = 1; + if (in_poll) + siglongjmp(env, 1); +} + +void send_stats(void) +{ + syslog(LOG_INFO, "arp_rcv: n%lu c%lu app_rcv: tot %lu hits %lu bad %lu neg %lu sup %lu", + stats.arp_new, stats.arp_change, + + stats.app_recv, stats.app_success, + stats.app_bad, stats.app_neg, stats.app_suppressed + ); + syslog(LOG_INFO, "kern: n%lu c%lu neg %lu arp_send: %lu rlim %lu", + stats.kern_new, stats.kern_change, stats.kern_neg, + + stats.probes_sent, stats.probes_suppressed + ); + do_stats = 0; +} + + +int main(int argc, char **argv) +{ + int opt; + int do_list = 0; + char *do_load = NULL; + + while ((opt = getopt(argc, argv, "h?b:lf:a:n:kR:B:")) != EOF) { + switch (opt) { + case 'b': + dbname = optarg; + break; + case 'f': + if (do_load) { + fprintf(stderr, "Duplicate option -f\n"); + usage(); + } + do_load = optarg; + break; + case 'l': + do_list = 1; + break; + case 'a': + active_probing = atoi(optarg); + break; + case 'n': + negative_timeout = atoi(optarg); + break; + case 'k': + no_kernel_broadcasts = 1; + break; + case 'R': + if ((broadcast_rate = atoi(optarg)) <= 0 || + (broadcast_rate = 1000/broadcast_rate) <= 0) { + fprintf(stderr, "Invalid ARP rate\n"); + exit(-1); + } + break; + case 'B': + if ((broadcast_burst = atoi(optarg)) <= 0 || + (broadcast_burst = 1000*broadcast_burst) <= 0) { + fprintf(stderr, "Invalid ARP burst\n"); + exit(-1); + } + break; + case 'h': + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc > 0) { + ifnum = argc; + ifnames = argv; + ifvec = malloc(argc*sizeof(int)); + if (!ifvec) { + perror("malloc"); + exit(-1); + } + } + + if ((udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(-1); + } + + if (ifnum) { + int i; + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + for (i=0; i<ifnum; i++) { + strncpy(ifr.ifr_name, ifnames[i], IFNAMSIZ); + if (ioctl(udp_sock, SIOCGIFINDEX, &ifr)) { + perror("ioctl(SIOCGIFINDEX)"); + exit(-1);; + } + ifvec[i] = ifr.ifr_ifindex; + } + } + + dbase = dbopen(dbname, O_CREAT|O_RDWR, 0644, DB_HASH, NULL); + if (dbase == NULL) { + perror("db_open"); + exit(-1); + } + + if (do_load) { + char buf[128]; + FILE *fp; + struct dbkey k; + DBT dbkey, dbdat; + + dbkey.data = &k; + dbkey.size = sizeof(k); + + if (strcmp(do_load, "-") == 0 || strcmp(do_load, "--") == 0) { + fp = stdin; + } else if ((fp = fopen(do_load, "r")) == NULL) { + perror("fopen"); + goto do_abort; + } + + buf[sizeof(buf)-1] = 0; + while (fgets(buf, sizeof(buf)-1, fp)) { + __u8 b1[6]; + char ipbuf[128]; + char macbuf[128]; + + if (buf[0] == '#') + continue; + + if (sscanf(buf, "%u%s%s", &k.iface, ipbuf, macbuf) != 3) { + fprintf(stderr, "Wrong format of input file \"%s\"\n", do_load); + goto do_abort; + } + if (strncmp(macbuf, "FAILED:", 7) == 0) + continue; + if (!inet_aton(ipbuf, (struct in_addr*)&k.addr)) { + fprintf(stderr, "Invalid IP address: \"%s\"\n", ipbuf); + goto do_abort; + } + dbdat.data = hexstring_a2n(macbuf, b1, 6); + if (dbdat.data == NULL) + goto do_abort; + dbdat.size = 6; + + if (dbase->put(dbase, &dbkey, &dbdat, 0)) { + perror("hash->put"); + goto do_abort; + } + } + dbase->sync(dbase, 0); + if (fp != stdin) + fclose(fp); + } + + if (do_list) { + DBT dbkey, dbdat; + printf("%-8s %-15s %s\n", "#Ifindex", "IP", "MAC"); + while (dbase->seq(dbase, &dbkey, &dbdat, R_NEXT) == 0) { + struct dbkey *key = dbkey.data; + if (handle_if(key->iface)) { + if (!IS_NEG(dbdat.data)) { + __u8 b1[18]; + printf("%-8d %-15s %s\n", + key->iface, + inet_ntoa(*(struct in_addr*)&key->addr), + hexstring_n2a(dbdat.data, 6, b1, 18)); + } else { + printf("%-8d %-15s FAILED: %dsec ago\n", + key->iface, + inet_ntoa(*(struct in_addr*)&key->addr), + NEG_AGE(dbdat.data)); + } + } + } + } + + if (do_load || do_list) + goto out; + + pset[0].fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (pset[0].fd < 0) { + perror("socket"); + exit(-1); + } + + if (1) { + struct sockaddr_ll sll; + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_ARP); + sll.sll_ifindex = (ifnum == 1 ? ifvec[0] : 0); + if (bind(pset[0].fd, (struct sockaddr*)&sll, sizeof(sll)) < 0) { + perror("bind"); + goto do_abort; + } + } + + if (rtnl_open(&rth, RTMGRP_NEIGH) < 0) { + perror("rtnl_open"); + goto do_abort; + } + pset[1].fd = rth.fd; + + load_initial_table(); + + if (1) { + int fd; + pid_t pid = fork(); + + if (pid > 0) + _exit(0); + if (pid < 0) { + perror("arpd: fork"); + goto do_abort; + } + + chdir("/"); + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); + } + setsid(); + } + + openlog("arpd", LOG_PID | LOG_CONS, LOG_DAEMON); + catch_signal(SIGINT, sig_exit); + catch_signal(SIGTERM, sig_exit); + catch_signal(SIGHUP, sig_sync); + catch_signal(SIGUSR1, sig_stats); + +#define EVENTS (POLLIN|POLLPRI|POLLERR|POLLHUP) + pset[0].events = EVENTS; + pset[0].revents = 0; + pset[1].events = EVENTS; + pset[1].revents = 0; + + sigsetjmp(env, 1); + + for (;;) { + in_poll = 1; + + if (do_exit) + break; + if (do_sync) { + in_poll = 0; + dbase->sync(dbase, 0); + do_sync = 0; + in_poll = 1; + } + if (do_stats) + send_stats(); + if (poll(pset, 2, 30000) > 0) { + in_poll = 0; + if (pset[0].revents&EVENTS) + get_arp_pkt(); + if (pset[1].revents&EVENTS) + get_kern_msg(); + } else { + do_sync = 1; + } + } + + undo_sysctl_adjustments(); +out: + dbase->close(dbase); + exit(0); + +do_abort: + dbase->close(dbase); + exit(-1); +} diff --git a/misc/ifstat b/misc/ifstat new file mode 100755 index 0000000..5a54b51 Binary files /dev/null and b/misc/ifstat differ diff --git a/misc/ifstat.c b/misc/ifstat.c new file mode 100644 index 0000000..1379a81 --- /dev/null +++ b/misc/ifstat.c @@ -0,0 +1,766 @@ +/* + * ifstat.c handy utility to read net interface statistics + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <fnmatch.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <signal.h> +#include <math.h> +#include <getopt.h> + +#include <libnetlink.h> +#include <linux/netdevice.h> + +#include <SNAPSHOT.h> + +int dump_zeros = 0; +int reset_history = 0; +int ignore_history = 0; +int no_output = 0; +int no_update = 0; +int scan_interval = 0; +int time_constant = 0; +int show_errors = 0; +double W; +char **patterns; +int npatterns; + +char info_source[128]; +int source_mismatch; + +#define MAXS (sizeof(struct net_device_stats)/sizeof(unsigned long)) + +struct ifstat_ent +{ + struct ifstat_ent *next; + char *name; + int ifindex; + unsigned long long val[MAXS]; + double rate[MAXS]; + unsigned long ival[MAXS]; +}; + +struct ifstat_ent *kern_db; +struct ifstat_ent *hist_db; + +static int match(const char *id) +{ + int i; + + if (npatterns == 0) + return 1; + + for (i=0; i<npatterns; i++) { + if (!fnmatch(patterns[i], id, 0)) + return 1; + } + return 0; +} + +static int get_nlmsg(const struct sockaddr_nl *who, + struct nlmsghdr *m, void *arg) +{ + struct ifinfomsg *ifi = NLMSG_DATA(m); + struct rtattr * tb[IFLA_MAX+1]; + int len = m->nlmsg_len; + struct ifstat_ent *n; + int i; + + if (m->nlmsg_type != RTM_NEWLINK) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (!(ifi->ifi_flags&IFF_UP)) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (tb[IFLA_IFNAME] == NULL || tb[IFLA_STATS] == NULL) + return 0; + + n = malloc(sizeof(*n)); + if (!n) + abort(); + n->ifindex = ifi->ifi_index; + n->name = strdup(RTA_DATA(tb[IFLA_IFNAME])); + memcpy(&n->ival, RTA_DATA(tb[IFLA_STATS]), sizeof(n->ival)); + memset(&n->rate, 0, sizeof(n->rate)); + for (i=0; i<MAXS; i++) + n->val[i] = n->ival[i]; + n->next = kern_db; + kern_db = n; + return 0; +} + +void load_info(void) +{ + struct ifstat_ent *db, *n; + struct rtnl_handle rth; + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, AF_INET, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, get_nlmsg, NULL, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + rtnl_close(&rth); + + db = kern_db; + kern_db = NULL; + + while (db) { + n = db; + db = db->next; + n->next = kern_db; + kern_db = n; + } +} + +void load_raw_table(FILE *fp) +{ + char buf[4096]; + struct ifstat_ent *db = NULL; + struct ifstat_ent *n; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *p; + char *next; + int i; + + if (buf[0] == '#') { + buf[strlen(buf)-1] = 0; + if (info_source[0] && strcmp(info_source, buf+1)) + source_mismatch = 1; + strncpy(info_source, buf+1, sizeof(info_source)-1); + continue; + } + if ((n = malloc(sizeof(*n))) == NULL) + abort(); + + if (!(p = strchr(buf, ' '))) + abort(); + *p++ = 0; + + if (sscanf(buf, "%d", &n->ifindex) != 1) + abort(); + if (!(next = strchr(p, ' '))) + abort(); + *next++ = 0; + + n->name = strdup(p); + p = next; + + for (i=0; i<MAXS; i++) { + unsigned rate; + if (!(next = strchr(p, ' '))) + abort(); + *next++ = 0; + if (sscanf(p, "%llu", n->val+i) != 1) + abort(); + n->ival[i] = (unsigned long)n->val[i]; + p = next; + if (!(next = strchr(p, ' '))) + abort(); + *next++ = 0; + if (sscanf(p, "%u", &rate) != 1) + abort(); + n->rate[i] = rate; + p = next; + } + n->next = db; + db = n; + } + + while (db) { + n = db; + db = db->next; + n->next = kern_db; + kern_db = n; + } +} + +void dump_raw_db(FILE *fp, int to_hist) +{ + struct ifstat_ent *n, *h; + h = hist_db; + fprintf(fp, "#%s\n", info_source); + + for (n=kern_db; n; n=n->next) { + int i; + unsigned long long *vals = n->val; + double *rates = n->rate; + if (!match(n->name)) { + struct ifstat_ent *h1; + if (!to_hist) + continue; + for (h1 = h; h1; h1 = h1->next) { + if (h1->ifindex == n->ifindex) { + vals = h1->val; + rates = h1->rate; + h = h1->next; + break; + } + } + } + fprintf(fp, "%d %s ", n->ifindex, n->name); + for (i=0; i<MAXS; i++) + fprintf(fp, "%llu %u ", vals[i], (unsigned)rates[i]); + fprintf(fp, "\n"); + } +} + +/* use communication definitions of meg/kilo etc */ +static const unsigned long long giga = 1000000000ull; +static const unsigned long long mega = 1000000; +static const unsigned long long kilo = 1000; + +void format_rate(FILE *fp, unsigned long long *vals, double *rates, int i) +{ + char temp[64]; + if (vals[i] > giga) + fprintf(fp, "%7lluM ", vals[i]/mega); + else if (vals[i] > mega) + fprintf(fp, "%7lluK ", vals[i]/kilo); + else + fprintf(fp, "%8llu ", vals[i]); + + if (rates[i] > mega) { + sprintf(temp, "%uM", (unsigned)(rates[i]/mega)); + fprintf(fp, "%-6s ", temp); + } else if (rates[i] > kilo) { + sprintf(temp, "%uK", (unsigned)(rates[i]/kilo)); + fprintf(fp, "%-6s ", temp); + } else + fprintf(fp, "%-6u ", (unsigned)rates[i]); +} + +void format_pair(FILE *fp, unsigned long long *vals, int i, int k) +{ + char temp[64]; + if (vals[i] > giga) + fprintf(fp, "%7lluM ", vals[i]/mega); + else if (vals[i] > mega) + fprintf(fp, "%7lluK ", vals[i]/kilo); + else + fprintf(fp, "%8llu ", vals[i]); + + if (vals[k] > giga) { + sprintf(temp, "%uM", (unsigned)(vals[k]/mega)); + fprintf(fp, "%-6s ", temp); + } else if (vals[k] > mega) { + sprintf(temp, "%uK", (unsigned)(vals[k]/kilo)); + fprintf(fp, "%-6s ", temp); + } else + fprintf(fp, "%-6u ", (unsigned)vals[k]); +} + +void print_head(FILE *fp) +{ + fprintf(fp, "#%s\n", info_source); + fprintf(fp, "%-15s ", "Interface"); + + fprintf(fp, "%8s/%-6s ", "RX Pkts", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Pkts", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Data", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Data", "Rate"); + + if (!show_errors) { + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "RX Errs", "Drop"); + fprintf(fp, "%8s/%-6s ", "TX Errs", "Drop"); + fprintf(fp, "%8s/%-6s ", "RX Over", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Coll", "Rate"); + } else { + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "RX Errs", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Drop", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Over", "Rate"); + fprintf(fp, "%8s/%-6s\n","RX Leng", "Rate"); + + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "RX Crc", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Frm", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Fifo", "Rate"); + fprintf(fp, "%8s/%-6s\n","RX Miss", "Rate"); + + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "TX Errs", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Drop", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Coll", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Carr", "Rate"); + + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "TX Abrt", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Fifo", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Hear", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Wind", "Rate"); + } +} + +void print_one_if(FILE *fp, struct ifstat_ent *n, unsigned long long *vals) +{ + int i; + fprintf(fp, "%-15s ", n->name); + for (i=0; i<4; i++) + format_rate(fp, vals, n->rate, i); + fprintf(fp, "\n"); + + if (!show_errors) { + fprintf(fp, "%-15s ", ""); + format_pair(fp, vals, 4, 6); + format_pair(fp, vals, 5, 7); + format_rate(fp, vals, n->rate, 11); + format_rate(fp, vals, n->rate, 9); + fprintf(fp, "\n"); + } else { + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 4); + format_rate(fp, vals, n->rate, 6); + format_rate(fp, vals, n->rate, 11); + format_rate(fp, vals, n->rate, 10); + fprintf(fp, "\n"); + + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 12); + format_rate(fp, vals, n->rate, 13); + format_rate(fp, vals, n->rate, 14); + format_rate(fp, vals, n->rate, 15); + fprintf(fp, "\n"); + + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 5); + format_rate(fp, vals, n->rate, 7); + format_rate(fp, vals, n->rate, 9); + format_rate(fp, vals, n->rate, 17); + fprintf(fp, "\n"); + + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 16); + format_rate(fp, vals, n->rate, 18); + format_rate(fp, vals, n->rate, 19); + format_rate(fp, vals, n->rate, 20); + fprintf(fp, "\n"); + } +} + + +void dump_kern_db(FILE *fp) +{ + struct ifstat_ent *n, *h; + h = hist_db; + + print_head(fp); + + for (n=kern_db; n; n=n->next) { + if (!match(n->name)) + continue; + print_one_if(fp, n, n->val); + } +} + + +void dump_incr_db(FILE *fp) +{ + struct ifstat_ent *n, *h; + h = hist_db; + + print_head(fp); + + for (n=kern_db; n; n=n->next) { + int i; + unsigned long long vals[MAXS]; + struct ifstat_ent *h1; + + memcpy(vals, n->val, sizeof(vals)); + + for (h1 = h; h1; h1 = h1->next) { + if (h1->ifindex == n->ifindex) { + for (i = 0; i < MAXS; i++) + vals[i] -= h1->val[i]; + h = h1->next; + break; + } + } + if (!match(n->name)) + continue; + print_one_if(fp, n, vals); + } +} + + +static int children; + +void sigchild(int signo) +{ +} + +void update_db(int interval) +{ + struct ifstat_ent *n, *h; + + n = kern_db; + kern_db = NULL; + + load_info(); + + h = kern_db; + kern_db = n; + + for (n = kern_db; n; n = n->next) { + struct ifstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { + if (h1->ifindex == n->ifindex) { + int i; + for (i = 0; i < MAXS; i++) { + if ((long)(h1->ival[i] - n->ival[i]) < 0) { + memset(n->ival, 0, sizeof(n->ival)); + break; + } + } + for (i = 0; i < MAXS; i++) { + double sample; + unsigned long incr = h1->ival[i] - n->ival[i]; + n->val[i] += incr; + n->ival[i] = h1->ival[i]; + sample = (double)(incr*1000)/interval; + if (interval >= scan_interval) { + n->rate[i] += W*(sample-n->rate[i]); + } else if (interval >= 1000) { + if (interval >= time_constant) { + n->rate[i] = sample; + } else { + double w = W*(double)interval/scan_interval; + n->rate[i] += w*(sample-n->rate[i]); + } + } + } + + while (h != h1) { + struct ifstat_ent *tmp = h; + h = h->next; + free(tmp->name); + free(tmp); + }; + h = h1->next; + free(h1->name); + free(h1); + break; + } + } + } +} + +#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) + + +void server_loop(int fd) +{ + struct timeval snaptime; + struct pollfd p; + p.fd = fd; + p.events = p.revents = POLLIN; + + sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d", + getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); + + load_info(); + + for (;;) { + int status; + int tdiff; + struct timeval now; + gettimeofday(&now, NULL); + tdiff = T_DIFF(now, snaptime); + if (tdiff >= scan_interval) { + update_db(tdiff); + snaptime = now; + tdiff = 0; + } + if (poll(&p, 1, tdiff + scan_interval) > 0 + && (p.revents&POLLIN)) { + int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { + pid_t pid; + if (children >= 5) { + close(clnt); + } else if ((pid = fork()) != 0) { + if (pid>0) + children++; + close(clnt); + } else { + FILE *fp = fdopen(clnt, "w"); + if (fp) { + if (tdiff > 0) + update_db(tdiff); + dump_raw_db(fp, 0); + } + exit(0); + } + } + } + while (children && waitpid(-1, &status, WNOHANG) > 0) + children--; + } +} + +int verify_forging(int fd) +{ + struct ucred cred; + int olen = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + olen < sizeof(cred)) + return -1; + if (cred.uid == getuid() || cred.uid == 0) + return 0; + return -1; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: ifstat [OPTION] [ PATTERN [ PATTERN ] ]\n" +" -h, --help this message\n" +" -a, --ignore ignore history\n" +" -d, --scan=SECS sample every statistics every SECS\n" +" -e, --errors show errors\n" +" -n, --nooutput do history only\n" +" -r, --reset reset history\n" +" -s, --noupdate don;t update history\n" +" -t, --interval=SECS report average over the last SECS\n" +" -V, --version output version information\n" +" -z, --zeros show entries with zero activity\n" +" -e, --errors show errors\n" +" -z, --zeros show entries with zero activity\n"); + + exit(-1); +} + +static const struct option longopts[] = { + { "help", 0, 0, 'h' }, + { "ignore", 0, 0, 'a' }, + { "scan", 1, 0, 'd'}, + { "errors", 0, 0, 'e' }, + { "nooutput", 0, 0, 'n' }, + { "reset", 0, 0, 'r' }, + { "noupdate", 0, 0, 's' }, + { "interval", 1, 0, 't' }, + { "version", 0, 0, 'V' }, + { "zeros", 0, 0, 'z' }, + { "errors", 0, 0, 'e' }, + { "zeros", 0, 0, 'z' }, + { 0 } +}; + +int main(int argc, char *argv[]) +{ + char hist_name[128]; + struct sockaddr_un sun; + FILE *hist_fp = NULL; + int ch; + int fd; + + while ((ch = getopt_long(argc, argv, "hvVzrnasd:t:eK", + longopts, NULL)) != EOF) { + switch(ch) { + case 'z': + dump_zeros = 1; + break; + case 'r': + reset_history = 1; + break; + case 'a': + ignore_history = 1; + break; + case 's': + no_update = 1; + break; + case 'n': + no_output = 1; + break; + case 'e': + show_errors = 1; + break; + case 'd': + scan_interval = atoi(optarg) * 1000; + if (scan_interval <= 0) { + fprintf(stderr, "ifstat: invalid scan interval\n"); + exit(-1); + } + break; + case 't': + time_constant = atoi(optarg); + if (time_constant <= 0) { + fprintf(stderr, "ifstat: invalid time constant divisor\n"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("ifstat utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + sun.sun_family = AF_UNIX; + sun.sun_path[0] = 0; + sprintf(sun.sun_path+1, "ifstat%d", getuid()); + + if (scan_interval > 0) { + if (time_constant == 0) + time_constant = 60; + time_constant *= 1000; + W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant); + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("ifstat: socket"); + exit(-1); + } + if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + perror("ifstat: bind"); + exit(-1); + } + if (listen(fd, 5) < 0) { + perror("ifstat: listen"); + exit(-1); + } + if (fork()) + exit(0); + chdir("/"); + close(0); close(1); close(2); setsid(); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, sigchild); + server_loop(fd); + exit(0); + } + + patterns = argv; + npatterns = argc; + + if (getenv("IFSTAT_HISTORY")) + snprintf(hist_name, sizeof(hist_name), getenv("IFSTAT_HISTORY")); + else + sprintf(hist_name, "%s/.ifstat.u%d", P_tmpdir, getuid()); + + if (reset_history) + unlink(hist_name); + + if (!ignore_history || !no_update) { + struct stat stb; + + fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600); + if (fd < 0) { + perror("ifstat: open history file"); + exit(-1); + } + if ((hist_fp = fdopen(fd, "r+")) == NULL) { + perror("ifstat: fdopen history file"); + exit(-1); + } + if (flock(fileno(hist_fp), LOCK_EX)) { + perror("ifstat: flock history file"); + exit(-1); + } + if (fstat(fileno(hist_fp), &stb) != 0) { + perror("ifstat: fstat history file"); + exit(-1); + } + if (stb.st_nlink != 1 || stb.st_uid != getuid()) { + fprintf(stderr, "ifstat: something is so wrong with history file, that I prefer not to proceed.\n"); + exit(-1); + } + if (!ignore_history) { + FILE *tfp; + long uptime; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { + if (fscanf(tfp, "%ld", &uptime) != 1) + uptime = -1; + fclose(tfp); + } + if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { + fprintf(stderr, "ifstat: history is aged out, resetting\n"); + ftruncate(fileno(hist_fp), 0); + } + } + + load_raw_table(hist_fp); + + hist_db = kern_db; + kern_db = NULL; + } + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && + (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + || (strcpy(sun.sun_path+1, "ifstat0"), + connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + && verify_forging(fd) == 0) { + FILE *sfp = fdopen(fd, "r"); + load_raw_table(sfp); + if (hist_db && source_mismatch) { + fprintf(stderr, "ifstat: history is stale, ignoring it.\n"); + hist_db = NULL; + } + fclose(sfp); + } else { + if (fd >= 0) + close(fd); + if (hist_db && info_source[0] && strcmp(info_source, "kernel")) { + fprintf(stderr, "ifstat: history is stale, ignoring it.\n"); + hist_db = NULL; + info_source[0] = 0; + } + load_info(); + if (info_source[0] == 0) + strcpy(info_source, "kernel"); + } + + if (!no_output) { + if (ignore_history || hist_db == NULL) + dump_kern_db(stdout); + else + dump_incr_db(stdout); + } + if (!no_update) { + ftruncate(fileno(hist_fp), 0); + rewind(hist_fp); + dump_raw_db(hist_fp, 1); + fflush(hist_fp); + } + exit(0); +} diff --git a/misc/lnstat b/misc/lnstat new file mode 100755 index 0000000..c59931a Binary files /dev/null and b/misc/lnstat differ diff --git a/misc/lnstat.c b/misc/lnstat.c new file mode 100644 index 0000000..03e6f3f --- /dev/null +++ b/misc/lnstat.c @@ -0,0 +1,336 @@ +/* lnstat - Unified linux network statistics + * + * Copyright (C) 2004 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code was funded by Astaro AG, http://www.astaro.com/ + * + * Based on original concept and ideas from predecessor rtstat.c: + * + * Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se> + * Uppsala University, Sweden + * + * 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. + * + */ + +/* Maximum number of fields that can be displayed */ +#define MAX_FIELDS 64 + +/* Maximum number of header lines */ +#define HDR_LINES 10 + +/* default field width if none specified */ +#define FIELD_WIDTH_DEFAULT 8 +#define FIELD_WIDTH_MAX 20 + +#define DEFAULT_INTERVAL 2 + +#define HDR_LINE_LENGTH (MAX_FIELDS*FIELD_WIDTH_MAX) + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> + +#include "lnstat.h" + +static struct option opts[] = { + { "version", 0, NULL, 'V' }, + { "count", 1, NULL, 'c' }, + { "dump", 1, NULL, 'd' }, + { "file", 1, NULL, 'f' }, + { "help", 0, NULL, 'h' }, + { "interval", 1, NULL, 'i' }, + { "key", 1, NULL, 'k' }, + { "subject", 1, NULL, 's' }, + { "width", 1, NULL, 'w' }, +}; + +static int usage(char *name, int exit_code) +{ + fprintf(stderr, "%s Version %s\n", name, LNSTAT_VERSION); + fprintf(stderr, "Copyright (C) 2004 by Harald Welte " + "<laforge@gnumonks.org>\n"); + fprintf(stderr, "This program is free software licensed under GNU GPLv2" + "\nwith ABSOLUTELY NO WARRANTY.\n\n"); + fprintf(stderr, "Parameters:\n"); + fprintf(stderr, "\t-V --version\t\tPrint Version of Program\n"); + fprintf(stderr, "\t-c --count <count>\t" + "Print <count> number of intervals\n"); + fprintf(stderr, "\t-d --dumpt\t\t" + "Dump list of available files/keys\n"); + fprintf(stderr, "\t-f --file <file>\tStatistics file to use\n"); + fprintf(stderr, "\t-h --help\t\tThis help message\n"); + fprintf(stderr, "\t-i --interval <intv>\t" + "Set interval to 'intv' seconds\n"); + fprintf(stderr, "\t-k --keys k,k,k,...\tDisplay only keys specified\n"); + fprintf(stderr, "\t-s --subject [0-2]\t?\n"); + fprintf(stderr, "\t-w --width n,n,n,...\tWidth for each field\n"); + fprintf(stderr, "\n"); + + exit(exit_code); +} + +struct field_param { + const char *name; + struct lnstat_field *lf; + struct { + unsigned int width; + } print; +}; + +struct field_params { + unsigned int num; + struct field_param params[MAX_FIELDS]; +}; + +static void print_line(FILE *of, const struct lnstat_file *lnstat_files, + const struct field_params *fp) +{ + int i; + + for (i = 0; i < fp->num; i++) { + struct lnstat_field *lf = fp->params[i].lf; + char formatbuf[255]; + + snprintf(formatbuf, sizeof(formatbuf)-1, "%%%ulu|", + fp->params[i].print.width); + fprintf(of, formatbuf, lf->result); + } + fputc('\n', of); +} + +/* find lnstat_field according to user specification */ +static int map_field_params(struct lnstat_file *lnstat_files, + struct field_params *fps, int interval) +{ + int i, j = 0; + struct lnstat_file *lf; + + /* no field specification on commandline, need to build default */ + if (!fps->num) { + for (lf = lnstat_files; lf; lf = lf->next) { + for (i = 0; i < lf->num_fields; i++) { + fps->params[j].lf = &lf->fields[i]; + fps->params[j].lf->file->interval.tv_sec = + interval; + if (!fps->params[j].print.width) + fps->params[j].print.width = + FIELD_WIDTH_DEFAULT; + j++; + } + } + fps->num = j; + return 1; + } + + for (i = 0; i < fps->num; i++) { + fps->params[i].lf = lnstat_find_field(lnstat_files, + fps->params[i].name); + if (!fps->params[i].lf) { + fprintf(stderr, "Field `%s' unknown\n", + fps->params[i].name); + return 0; + } + fps->params[i].lf->file->interval.tv_sec = interval; + if (!fps->params[i].print.width) + fps->params[i].print.width = FIELD_WIDTH_DEFAULT; + } + return 1; +} + +struct table_hdr { + int num_lines; + char *hdr[HDR_LINES]; +}; + +static struct table_hdr *build_hdr_string(struct lnstat_file *lnstat_files, + struct field_params *fps, + int linewidth) +{ + int h,i; + static struct table_hdr th; + int ofs = 0; + + for (i = 0; i < HDR_LINES; i++) { + th.hdr[i] = malloc(HDR_LINE_LENGTH); + memset(th.hdr[i], 0, sizeof(th.hdr[i])); + } + + for (i = 0; i < fps->num; i++) { + char *cname, *fname = fps->params[i].lf->name; + char fmt[12]; + unsigned int width = fps->params[i].print.width; + + snprintf(fmt, sizeof(fmt)-1, "%%%u.%us|", width, width); + + snprintf(th.hdr[0]+ofs, width+2, fmt, + fps->params[i].lf->file->basename); + + cname = fname; + for (h = 1; h < HDR_LINES; h++) { + if (cname - fname >= strlen(fname)) + snprintf(th.hdr[h]+ofs, width+2, fmt, ""); + else { + th.num_lines = h+1; + snprintf(th.hdr[h]+ofs, width+2, fmt, cname); + } + cname += width; + } + ofs += width+1; + } + /* fill in spaces */ + for (h = 1; h <= th.num_lines; h++) { + for (i = 0; i < ofs; i++) { + if (th.hdr[h][i] == '\0') + th.hdr[h][i] = ' '; + } + } + + return &th; +} + +static int print_hdr(FILE *of, struct table_hdr *th) +{ + int i; + + for (i = 0; i < th->num_lines; i++) { + fputs(th->hdr[i], of); + fputc('\n', of); + } + return 0; +} + + +int main(int argc, char **argv) +{ + struct lnstat_file *lnstat_files; + const char *basename; + int c; + int interval = DEFAULT_INTERVAL; + int hdr = 2; + enum { + MODE_DUMP, + MODE_NORMAL, + } mode = MODE_NORMAL; + + unsigned long count = 0; + static struct field_params fp; + int num_req_files = 0; + char *req_files[LNSTAT_MAX_FILES]; + + /* backwards compatibility mode for old tools */ + basename = strrchr(argv[0], '/'); + if (basename) + basename += 1; /* name after slash */ + else + basename = argv[0]; /* no slash */ + + if (!strcmp(basename, "rtstat")) { + /* rtstat compatibility mode */ + req_files[0] = "rt_cache"; + num_req_files = 1; + } else if (!strcmp(basename, "ctstat")) { + /* ctstat compatibility mode */ + req_files[0] = "ip_conntrack"; + num_req_files = 1; + } + + while ((c = getopt_long(argc, argv,"Vc:df:h?i:k:s:w:", + opts, NULL)) != -1) { + switch (c) { + int i, len = 0; + char *tmp, *tok; + case 'c': + count = strtoul(optarg, NULL, 0); + break; + case 'd': + mode = MODE_DUMP; + break; + case 'f': + req_files[num_req_files++] = strdup(optarg); + break; + case '?': + case 'h': + usage(argv[0], 0); + break; + case 'i': + sscanf(optarg, "%u", &interval); + break; + case 'k': + tmp = strdup(optarg); + if (!tmp) + break; + for (tok = strtok(tmp, ","); + tok; + tok = strtok(NULL, ",")) { + if (fp.num >= MAX_FIELDS) + break; + fp.params[fp.num++].name = tok; + } + break; + case 's': + sscanf(optarg, "%u", &hdr); + break; + case 'w': + tmp = strdup(optarg); + if (!tmp) + break; + i = 0; + for (tok = strtok(tmp, ","); + tok; + tok = strtok(NULL, ",")) { + len = strtoul(tok, NULL, 0); + if (len > FIELD_WIDTH_MAX) + len = FIELD_WIDTH_MAX; + fp.params[i].print.width = len; + i++; + } + if (i == 1) { + for (i = 0; i < MAX_FIELDS; i++) + fp.params[i].print.width = len; + } + break; + default: + usage(argv[0], 1); + break; + } + } + + lnstat_files = lnstat_scan_dir(PROC_NET_STAT, num_req_files, + (const char **) req_files); + + switch (mode) { + int i; + struct table_hdr *header; + case MODE_DUMP: + lnstat_dump(stderr, lnstat_files); + break; + case MODE_NORMAL: + + if (!map_field_params(lnstat_files, &fp, interval)) + exit(1); + + header = build_hdr_string(lnstat_files, &fp, 80); + if (!header) + exit(1); + + if (interval < 1 ) + interval=1; + + for (i = 0; i < count; i++) { + if ((hdr > 1 && (! (i % 20))) || (hdr == 1 && i == 0)) + print_hdr(stdout, header); + lnstat_update(lnstat_files); + print_line(stdout, lnstat_files, &fp); + sleep(interval); + } + } + + return 1; +} + diff --git a/misc/lnstat.h b/misc/lnstat.h new file mode 100644 index 0000000..06774ab --- /dev/null +++ b/misc/lnstat.h @@ -0,0 +1,43 @@ +#ifndef _LNSTAT_H +#define _LNSTAT_H + +#include <limits.h> + +#define LNSTAT_VERSION "0.02 041002" + +#define PROC_NET_STAT "/proc/net/stat" + +#define LNSTAT_MAX_FILES 32 +#define LNSTAT_MAX_FIELDS_PER_LINE 32 +#define LNSTAT_MAX_FIELD_NAME_LEN 32 + +struct lnstat_file; + +struct lnstat_field { + struct lnstat_file *file; + unsigned int num; /* field number in line */ + char name[LNSTAT_MAX_FIELD_NAME_LEN+1]; + unsigned long values[2]; /* two buffers for values */ + unsigned long result; +}; + +struct lnstat_file { + struct lnstat_file *next; + char path[PATH_MAX+1]; + char basename[NAME_MAX+1]; + struct timeval last_read; /* last time of read */ + struct timeval interval; /* interval */ + int compat; /* 1 == backwards compat mode */ + FILE *fp; + unsigned int num_fields; /* number of fields */ + struct lnstat_field fields[LNSTAT_MAX_FIELDS_PER_LINE]; +}; + + +struct lnstat_file *lnstat_scan_dir(const char *path, const int num_req_files, + const char **req_files); +int lnstat_update(struct lnstat_file *lnstat_files); +int lnstat_dump(FILE *outfd, struct lnstat_file *lnstat_files); +struct lnstat_field *lnstat_find_field(struct lnstat_file *lnstat_files, + const char *name); +#endif /* _LNSTAT_H */ diff --git a/misc/lnstat.o b/misc/lnstat.o new file mode 100644 index 0000000..b272d87 Binary files /dev/null and b/misc/lnstat.o differ diff --git a/misc/lnstat_util.c b/misc/lnstat_util.c new file mode 100644 index 0000000..6ff3779 --- /dev/null +++ b/misc/lnstat_util.c @@ -0,0 +1,324 @@ +/* lnstat.c: Unified linux network statistics + * + * Copyright (C) 2004 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code was funded by Astaro AG, http://www.astaro.com/ + * + * Based on original concept and ideas from predecessor rtstat.c: + * + * Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se> + * Uppsala University, Sweden + * + * 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. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <limits.h> +#include <time.h> + +#include <sys/time.h> +#include <sys/types.h> + +#include "lnstat.h" + +/* size of temp buffer used to read lines from procfiles */ +#define FGETS_BUF_SIZE 1024 + + +#define RTSTAT_COMPAT_LINE "entries in_hit in_slow_tot in_no_route in_brd in_martian_dst in_martian_src out_hit out_slow_tot out_slow_mc gc_total gc_ignored gc_goal_miss gc_dst_overflow in_hlist_search out_hlist_search\n" + +/* Read (and summarize for SMP) the different stats vars. */ +static int scan_lines(struct lnstat_file *lf, int i) +{ + int j, num_lines = 0; + + for (j = 0; j < lf->num_fields; j++) + lf->fields[j].values[i] = 0; + + while(!feof(lf->fp)) { + char buf[FGETS_BUF_SIZE]; + char *ptr = buf; + + num_lines++; + + fgets(buf, sizeof(buf)-1, lf->fp); + gettimeofday(&lf->last_read, NULL); + + for (j = 0; j < lf->num_fields; j++) + lf->fields[j].values[i] = strtoul(ptr, &ptr, 16); + } + return num_lines; +} + +static int time_after(struct timeval *last, + struct timeval *tout, + struct timeval *now) +{ + if (now->tv_sec > last->tv_sec + tout->tv_sec) + return 1; + + if (now->tv_sec == last->tv_sec + tout->tv_sec) { + if (now->tv_usec > last->tv_usec + tout->tv_usec) + return 1; + } + + return 0; +} + +int lnstat_update(struct lnstat_file *lnstat_files) +{ + struct lnstat_file *lf; + char buf[FGETS_BUF_SIZE]; + struct timeval tv; + + gettimeofday(&tv, NULL); + + for (lf = lnstat_files; lf; lf = lf->next) { + if (time_after(&lf->last_read, &lf->interval, &tv)) { + int i; + struct lnstat_field *lfi; + + rewind(lf->fp); + if (!lf->compat) { + /* skip first line */ + fgets(buf, sizeof(buf)-1, lf->fp); + } + scan_lines(lf, 1); + + for (i = 0, lfi = &lf->fields[i]; + i < lf->num_fields; i++, lfi = &lf->fields[i]) { + if (i == 0) + lfi->result = lfi->values[1]; + else + lfi->result = (lfi->values[1]-lfi->values[0]) + / lf->interval.tv_sec; + } + + rewind(lf->fp); + fgets(buf, sizeof(buf)-1, lf->fp); + scan_lines(lf, 0); + } + } + + return 0; +} + +/* scan first template line and fill in per-field data structures */ +static int __lnstat_scan_fields(struct lnstat_file *lf, char *buf) +{ + char *tok; + int i; + + tok = strtok(buf, " \t\n"); + for (i = 0; i < LNSTAT_MAX_FIELDS_PER_LINE; i++) { + lf->fields[i].file = lf; + strncpy(lf->fields[i].name, tok, LNSTAT_MAX_FIELD_NAME_LEN); + /* has to be null-terminate since we initialize to zero + * and field size is NAME_LEN + 1 */ + tok = strtok(NULL, " \t\n"); + if (!tok) { + lf->num_fields = i+1; + return 0; + } + } + return 0; +} + +static int lnstat_scan_fields(struct lnstat_file *lf) +{ + char buf[FGETS_BUF_SIZE]; + + rewind(lf->fp); + fgets(buf, sizeof(buf)-1, lf->fp); + + return __lnstat_scan_fields(lf, buf); +} + +/* fake function emulating lnstat_scan_fields() for old kernels */ +static int lnstat_scan_compat_rtstat_fields(struct lnstat_file *lf) +{ + char buf[FGETS_BUF_SIZE]; + + strncpy(buf, RTSTAT_COMPAT_LINE, sizeof(buf)-1); + + return __lnstat_scan_fields(lf, buf); +} + +/* find out whether string 'name; is in given string array */ +static int name_in_array(const int num, const char **arr, const char *name) +{ + int i; + for (i = 0; i < num; i++) { + if (!strcmp(arr[i], name)) + return 1; + } + return 0; +} + +/* allocate lnstat_file and open given file */ +static struct lnstat_file *alloc_and_open(const char *path, const char *file) +{ + struct lnstat_file *lf; + + /* allocate */ + lf = malloc(sizeof(*lf)); + if (!lf) + return NULL; + + /* initialize */ + memset(lf, 0, sizeof(*lf)); + + /* de->d_name is guaranteed to be <= NAME_MAX */ + strcpy(lf->basename, file); + strcpy(lf->path, path); + strcat(lf->path, "/"); + strcat(lf->path, lf->basename); + + /* initialize to default */ + lf->interval.tv_sec = 1; + + /* open */ + lf->fp = fopen(lf->path, "r"); + if (!lf->fp) { + free(lf); + return NULL; + } + + return lf; +} + + +/* lnstat_scan_dir - find and parse all available statistics files/fields */ +struct lnstat_file *lnstat_scan_dir(const char *path, const int num_req_files, + const char **req_files) +{ + DIR *dir; + struct lnstat_file *lnstat_files = NULL; + struct dirent *de; + + if (!path) + path = PROC_NET_STAT; + + dir = opendir(path); + if (!dir) { + struct lnstat_file *lf; + /* Old kernel, before /proc/net/stat was introduced */ + fprintf(stderr, "Your kernel doesn't have lnstat support. "); + + /* we only support rtstat, not multiple files */ + if (num_req_files >= 2) { + fputc('\n', stderr); + return NULL; + } + + /* we really only accept rt_cache */ + if (num_req_files && !name_in_array(num_req_files, + req_files, "rt_cache")) { + fputc('\n', stderr); + return NULL; + } + + fprintf(stderr, "Fallback to old rtstat-only operation\n"); + + lf = alloc_and_open("/proc/net", "rt_cache_stat"); + if (!lf) + return NULL; + lf->compat = 1; + strncpy(lf->basename, "rt_cache", sizeof(lf->basename)); + + /* FIXME: support for old files */ + if (lnstat_scan_compat_rtstat_fields(lf) < 0) + return NULL; + + lf->next = lnstat_files; + lnstat_files = lf; + return lnstat_files; + } + + while ((de = readdir(dir))) { + struct lnstat_file *lf; + + if (de->d_type != DT_REG) + continue; + + if (num_req_files && !name_in_array(num_req_files, + req_files, de->d_name)) + continue; + + lf = alloc_and_open(path, de->d_name); + if (!lf) + return NULL; + + /* fill in field structure */ + if (lnstat_scan_fields(lf) < 0) + return NULL; + + /* prepend to global list */ + lf->next = lnstat_files; + lnstat_files = lf; + } + closedir(dir); + + return lnstat_files; +} + +int lnstat_dump(FILE *outfd, struct lnstat_file *lnstat_files) +{ + struct lnstat_file *lf; + + for (lf = lnstat_files; lf; lf = lf->next) { + int i; + + fprintf(outfd, "%s:\n", lf->path); + + for (i = 0; i < lf->num_fields; i++) + fprintf(outfd, "\t%2u: %s\n", i+1, lf->fields[i].name); + + } + return 0; +} + +struct lnstat_field *lnstat_find_field(struct lnstat_file *lnstat_files, + const char *name) +{ + struct lnstat_file *lf; + struct lnstat_field *ret = NULL; + const char *colon = strchr(name, ':'); + char *file; + const char *field; + + if (colon) { + file = strndup(name, colon-name); + field = colon+1; + } else { + file = NULL; + field = name; + } + + for (lf = lnstat_files; lf; lf = lf->next) { + int i; + + if (file && strcmp(file, lf->basename)) + continue; + + for (i = 0; i < lf->num_fields; i++) { + if (!strcmp(field, lf->fields[i].name)) { + ret = &lf->fields[i]; + goto out; + } + } + } +out: + if (file) + free(file); + + return ret; +} diff --git a/misc/lnstat_util.o b/misc/lnstat_util.o new file mode 100644 index 0000000..b9de784 Binary files /dev/null and b/misc/lnstat_util.o differ diff --git a/misc/netbug b/misc/netbug new file mode 100755 index 0000000..6d13c8e --- /dev/null +++ b/misc/netbug @@ -0,0 +1,53 @@ +#! /bin/bash + +echo -n "Send network configuration summary to [ENTER means kuznet@ms2.inr.ac.ru] " +IFS="" read mail || exit 1 +[ -z "$mail" ] && mail=kuznet@ms2.inr.ac.ru + + +netbug="" +while [ "$netbug" = "" ]; do + netbug=`echo netbug.$$.$RANDOM` + if [ -e /tmp/$netbug ]; then + netbug="" + fi +done + +tmppath=/tmp/$netbug + +trap "rm -rf $tmppath $tmppath.tar.gz" 0 SIGINT + +mkdir $tmppath +mkdir $tmppath/net + +cat /proc/slabinfo > $tmppath/slabinfo +cat /proc/net/netstat > $tmppath/net/netstat +cat /proc/net/unix > $tmppath/net/unix +cat /proc/net/packet > $tmppath/net/packet +cat /proc/net/netlink > $tmppath/net/netlink +cat /proc/net/psched > $tmppath/net/psched +cat /proc/net/softnet_stat > $tmppath/net/softnet_stat +cat /proc/net/sockstat > $tmppath/net/sockstat +cat /proc/net/tcp > $tmppath/net/tcp +cat /proc/net/udp > $tmppath/net/udp +cat /proc/net/raw > $tmppath/net/raw +cat /proc/net/snmp > $tmppath/net/snmp + +ss -aioem -D $tmppath/tcpdiag + +if [ -e /proc/net/tcp6 ]; then + cat /proc/net/sockstat6 > $tmppath/net/sockstat6 + cat /proc/net/tcp6 > $tmppath/net/tcp6 + cat /proc/net/udp6 > $tmppath/net/udp6 + cat /proc/net/raw6 > $tmppath/net/raw6 + cat /proc/net/snmp6 > $tmppath/net/snmp6 +fi + +cd /tmp +tar c $netbug | gzip -9c > $netbug.tar.gz + +uuencode $netbug.tar.gz $netbug.tar.gz | mail -s $netbug "$mail" + +echo "Sending to <$mail>; subject is $netbug" + +exit 0 diff --git a/misc/nstat b/misc/nstat new file mode 100755 index 0000000..acc6870 Binary files /dev/null and b/misc/nstat differ diff --git a/misc/nstat.c b/misc/nstat.c new file mode 100644 index 0000000..f2887ec --- /dev/null +++ b/misc/nstat.c @@ -0,0 +1,620 @@ +/* + * nstat.c handy utility to read counters /proc/net/netstat and snmp + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <fnmatch.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <signal.h> +#include <math.h> + +#include <SNAPSHOT.h> + +int dump_zeros = 0; +int reset_history = 0; +int ignore_history = 0; +int no_output = 0; +int no_update = 0; +int scan_interval = 0; +int time_constant = 0; +double W; +char **patterns; +int npatterns; + +char info_source[128]; +int source_mismatch; + +int generic_proc_open(char *env, char *name) +{ + char store[128]; + char *p = getenv(env); + if (!p) { + p = getenv("PROC_ROOT") ? : "/proc"; + snprintf(store, sizeof(store)-1, "%s/%s", p, name); + p = store; + } + return open(store, O_RDONLY); +} + +int net_netstat_open(void) +{ + return generic_proc_open("PROC_NET_NETSTAT", "net/netstat"); +} + +int net_snmp_open(void) +{ + return generic_proc_open("PROC_NET_SNMP", "net/snmp"); +} + +int net_snmp6_open(void) +{ + return generic_proc_open("PROC_NET_SNMP6", "net/snmp6"); +} + +struct nstat_ent +{ + struct nstat_ent *next; + char *id; + unsigned long long val; + unsigned long ival; + double rate; +}; + +struct nstat_ent *kern_db; +struct nstat_ent *hist_db; + +char *useless_numbers[] = { +"IpForwarding", "IpDefaultTTL", +"TcpRtoAlgorithm", "TcpRtoMin", "TcpRtoMax", +"TcpMaxConn", "TcpCurrEstab" +}; + +int useless_number(char *id) +{ + int i; + for (i=0; i<sizeof(useless_numbers)/sizeof(*useless_numbers); i++) + if (strcmp(id, useless_numbers[i]) == 0) + return 1; + return 0; +} + +int match(char *id) +{ + int i; + + if (npatterns == 0) + return 1; + + for (i=0; i<npatterns; i++) { + if (!fnmatch(patterns[i], id, 0)) + return 1; + } + return 0; +} + +void load_good_table(FILE *fp) +{ + char buf[4096]; + struct nstat_ent *db = NULL; + struct nstat_ent *n; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + int nr; + unsigned long long val; + double rate; + char idbuf[sizeof(buf)]; + if (buf[0] == '#') { + buf[strlen(buf)-1] = 0; + if (info_source[0] && strcmp(info_source, buf+1)) + source_mismatch = 1; + info_source[0] = 0; + strncat(info_source, buf+1, sizeof(info_source)-1); + continue; + } + /* idbuf is as big as buf, so this is safe */ + nr = sscanf(buf, "%s%llu%lg", idbuf, &val, &rate); + if (nr < 2) + abort(); + if (nr < 3) + rate = 0; + if (useless_number(idbuf)) + continue; + if ((n = malloc(sizeof(*n))) == NULL) + abort(); + n->id = strdup(idbuf); + n->ival = (unsigned long)val; + n->val = val; + n->rate = rate; + n->next = db; + db = n; + } + + while (db) { + n = db; + db = db->next; + n->next = kern_db; + kern_db = n; + } +} + + +void load_ugly_table(FILE *fp) +{ + char buf[4096]; + struct nstat_ent *db = NULL; + struct nstat_ent *n; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char idbuf[sizeof(buf)]; + int off; + char *p; + + p = strchr(buf, ':'); + if (!p) + abort(); + *p = 0; + idbuf[0] = 0; + strncat(idbuf, buf, sizeof(idbuf) - 1); + off = p - buf; + p += 2; + + while (*p) { + char *next; + if ((next = strchr(p, ' ')) != NULL) + *next++ = 0; + else if ((next = strchr(p, '\n')) != NULL) + *next++ = 0; + if (off < sizeof(idbuf)) { + idbuf[off] = 0; + strncat(idbuf, p, sizeof(idbuf) - off - 1); + } + n = malloc(sizeof(*n)); + if (!n) + abort(); + n->id = strdup(idbuf); + n->rate = 0; + n->next = db; + db = n; + p = next; + } + n = db; + if (fgets(buf, sizeof(buf), fp) == NULL) + abort(); + do { + p = strrchr(buf, ' '); + if (!p) + abort(); + *p = 0; + if (sscanf(p+1, "%lu", &n->ival) != 1) + abort(); + n->val = n->ival; + /* Trick to skip "dummy" trailing ICMP MIB in 2.4 */ + if (strcmp(idbuf, "IcmpOutAddrMaskReps") == 0) + idbuf[5] = 0; + else + n = n->next; + } while (p > buf + off + 2); + } + + while (db) { + n = db; + db = db->next; + if (useless_number(n->id)) { + free(n->id); + free(n); + } else { + n->next = kern_db; + kern_db = n; + } + } +} + +void load_snmp(void) +{ + FILE *fp = fdopen(net_snmp_open(), "r"); + if (fp) { + load_ugly_table(fp); + fclose(fp); + } +} + +void load_snmp6(void) +{ + FILE *fp = fdopen(net_snmp6_open(), "r"); + if (fp) { + load_good_table(fp); + fclose(fp); + } +} + +void load_netstat(void) +{ + FILE *fp = fdopen(net_netstat_open(), "r"); + if (fp) { + load_ugly_table(fp); + fclose(fp); + } +} + +void dump_kern_db(FILE *fp, int to_hist) +{ + struct nstat_ent *n, *h; + h = hist_db; + fprintf(fp, "#%s\n", info_source); + for (n=kern_db; n; n=n->next) { + unsigned long long val = n->val; + if (!dump_zeros && !val && !n->rate) + continue; + if (!match(n->id)) { + struct nstat_ent *h1; + if (!to_hist) + continue; + for (h1 = h; h1; h1 = h1->next) { + if (strcmp(h1->id, n->id) == 0) { + val = h1->val; + h = h1->next; + break; + } + } + } + fprintf(fp, "%-32s%-16llu%6.1f\n", n->id, val, n->rate); + } +} + +void dump_incr_db(FILE *fp) +{ + struct nstat_ent *n, *h; + h = hist_db; + fprintf(fp, "#%s\n", info_source); + for (n=kern_db; n; n=n->next) { + int ovfl = 0; + unsigned long long val = n->val; + struct nstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { + if (strcmp(h1->id, n->id) == 0) { + if (val < h1->val) { + ovfl = 1; + val = h1->val; + } + val -= h1->val; + h = h1->next; + break; + } + } + if (!dump_zeros && !val && !n->rate) + continue; + if (!match(n->id)) + continue; + fprintf(fp, "%-32s%-16llu%6.1f%s\n", n->id, val, + n->rate, ovfl?" (overflow)":""); + } +} + +static int children; + +void sigchild(int signo) +{ +} + +void update_db(int interval) +{ + struct nstat_ent *n, *h; + + n = kern_db; + kern_db = NULL; + + load_netstat(); + load_snmp6(); + load_snmp(); + + h = kern_db; + kern_db = n; + + for (n = kern_db; n; n = n->next) { + struct nstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { + if (strcmp(h1->id, n->id) == 0) { + double sample; + unsigned long incr = h1->ival - n->ival; + n->val += incr; + n->ival = h1->ival; + sample = (double)(incr*1000)/interval; + if (interval >= scan_interval) { + n->rate += W*(sample-n->rate); + } else if (interval >= 1000) { + if (interval >= time_constant) { + n->rate = sample; + } else { + double w = W*(double)interval/scan_interval; + n->rate += w*(sample-n->rate); + } + } + + while (h != h1) { + struct nstat_ent *tmp = h; + h = h->next; + free(tmp->id); + free(tmp); + }; + h = h1->next; + free(h1->id); + free(h1); + break; + } + } + } +} + +#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) + + +void server_loop(int fd) +{ + struct timeval snaptime; + struct pollfd p; + p.fd = fd; + p.events = p.revents = POLLIN; + + sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d", + getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); + + load_netstat(); + load_snmp6(); + load_snmp(); + + for (;;) { + int status; + int tdiff; + struct timeval now; + gettimeofday(&now, NULL); + tdiff = T_DIFF(now, snaptime); + if (tdiff >= scan_interval) { + update_db(tdiff); + snaptime = now; + tdiff = 0; + } + if (poll(&p, 1, tdiff + scan_interval) > 0 + && (p.revents&POLLIN)) { + int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { + pid_t pid; + if (children >= 5) { + close(clnt); + } else if ((pid = fork()) != 0) { + if (pid>0) + children++; + close(clnt); + } else { + FILE *fp = fdopen(clnt, "w"); + if (fp) { + if (tdiff > 0) + update_db(tdiff); + dump_kern_db(fp, 0); + } + exit(0); + } + } + } + while (children && waitpid(-1, &status, WNOHANG) > 0) + children--; + } +} + +int verify_forging(int fd) +{ + struct ucred cred; + int olen = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + olen < sizeof(cred)) + return -1; + if (cred.uid == getuid() || cred.uid == 0) + return 0; + return -1; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: nstat [ -h?vVzrnasd:t: ] [ PATTERN [ PATTERN ] ]\n" + ); + exit(-1); +} + + +int main(int argc, char *argv[]) +{ + char hist_name[128]; + struct sockaddr_un sun; + FILE *hist_fp = NULL; + int ch; + int fd; + + while ((ch = getopt(argc, argv, "h?vVzrnasd:t:")) != EOF) { + switch(ch) { + case 'z': + dump_zeros = 1; + break; + case 'r': + reset_history = 1; + break; + case 'a': + ignore_history = 1; + break; + case 's': + no_update = 1; + break; + case 'n': + no_output = 1; + break; + case 'd': + scan_interval = 1000*atoi(optarg); + break; + case 't': + if (sscanf(optarg, "%d", &time_constant) != 1 || + time_constant <= 0) { + fprintf(stderr, "nstat: invalid time constant divisor\n"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("nstat utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + sun.sun_family = AF_UNIX; + sun.sun_path[0] = 0; + sprintf(sun.sun_path+1, "nstat%d", getuid()); + + if (scan_interval > 0) { + if (time_constant == 0) + time_constant = 60; + time_constant *= 1000; + W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant); + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("nstat: socket"); + exit(-1); + } + if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + perror("nstat: bind"); + exit(-1); + } + if (listen(fd, 5) < 0) { + perror("nstat: listen"); + exit(-1); + } + if (fork()) + exit(0); + chdir("/"); + close(0); close(1); close(2); setsid(); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, sigchild); + server_loop(fd); + exit(0); + } + + patterns = argv; + npatterns = argc; + + if (getenv("NSTAT_HISTORY")) + snprintf(hist_name, sizeof(hist_name), getenv("NSTAT_HISTORY")); + else + sprintf(hist_name, "/tmp/.nstat.u%d", getuid()); + + if (reset_history) + unlink(hist_name); + + if (!ignore_history || !no_update) { + struct stat stb; + + fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600); + if (fd < 0) { + perror("nstat: open history file"); + exit(-1); + } + if ((hist_fp = fdopen(fd, "r+")) == NULL) { + perror("nstat: fdopen history file"); + exit(-1); + } + if (flock(fileno(hist_fp), LOCK_EX)) { + perror("nstat: flock history file"); + exit(-1); + } + if (fstat(fileno(hist_fp), &stb) != 0) { + perror("nstat: fstat history file"); + exit(-1); + } + if (stb.st_nlink != 1 || stb.st_uid != getuid()) { + fprintf(stderr, "nstat: something is so wrong with history file, that I prefer not to proceed.\n"); + exit(-1); + } + if (!ignore_history) { + FILE *tfp; + long uptime; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { + if (fscanf(tfp, "%ld", &uptime) != 1) + uptime = -1; + fclose(tfp); + } + if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { + fprintf(stderr, "nstat: history is aged out, resetting\n"); + ftruncate(fileno(hist_fp), 0); + } + } + + load_good_table(hist_fp); + + hist_db = kern_db; + kern_db = NULL; + } + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && + (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + || (strcpy(sun.sun_path+1, "nstat0"), + connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + && verify_forging(fd) == 0) { + FILE *sfp = fdopen(fd, "r"); + load_good_table(sfp); + if (hist_db && source_mismatch) { + fprintf(stderr, "nstat: history is stale, ignoring it.\n"); + hist_db = NULL; + } + fclose(sfp); + } else { + if (fd >= 0) + close(fd); + if (hist_db && info_source[0] && strcmp(info_source, "kernel")) { + fprintf(stderr, "nstat: history is stale, ignoring it.\n"); + hist_db = NULL; + info_source[0] = 0; + } + load_netstat(); + load_snmp6(); + load_snmp(); + if (info_source[0] == 0) + strcpy(info_source, "kernel"); + } + + if (!no_output) { + if (ignore_history || hist_db == NULL) + dump_kern_db(stdout, 0); + else + dump_incr_db(stdout); + } + if (!no_update) { + ftruncate(fileno(hist_fp), 0); + rewind(hist_fp); + dump_kern_db(hist_fp, 1); + fflush(hist_fp); + } + exit(0); +} diff --git a/misc/rtacct b/misc/rtacct new file mode 100755 index 0000000..5edd221 Binary files /dev/null and b/misc/rtacct differ diff --git a/misc/rtacct.c b/misc/rtacct.c new file mode 100644 index 0000000..5c6748b --- /dev/null +++ b/misc/rtacct.c @@ -0,0 +1,625 @@ +/* + * rtacct.c Applet to display contents of /proc/net/rt_acct. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <fnmatch.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <signal.h> +#include <math.h> + +#include "rt_names.h" + +#include <SNAPSHOT.h> + +int reset_history = 0; +int ignore_history = 0; +int no_output = 0; +int no_update = 0; +int scan_interval = 0; +int time_constant = 0; +int dump_zeros = 0; +unsigned long magic_number = 0; +double W; + +int generic_proc_open(char *env, char *name) +{ + char store[1024]; + char *p = getenv(env); + if (!p) { + p = getenv("PROC_ROOT") ? : "/proc"; + snprintf(store, sizeof(store)-1, "%s/%s", p, name); + p = store; + } + return open(store, O_RDONLY); +} + +int net_rtacct_open(void) +{ + return generic_proc_open("PROC_NET_RTACCT", "net/rt_acct"); +} + +__u32 rmap[256/4]; + +struct rtacct_data +{ + __u32 ival[256*4]; + + unsigned long long val[256*4]; + double rate[256*4]; + __u8 signature[128]; +}; + +struct rtacct_data kern_db_static; + +struct rtacct_data *kern_db = &kern_db_static; +struct rtacct_data *hist_db; + +void nread(int fd, char *buf, int tot) +{ + int count = 0; + + while (count < tot) { + int n = read(fd, buf+count, tot-count); + if (n < 0) { + if (errno == EINTR) + continue; + exit(-1); + } + if (n == 0) + exit(-1); + count += n; + } +} + + +__u32 *read_kern_table(__u32 *tbl) +{ + static __u32 *tbl_ptr; + int fd; + + if (magic_number) { + if (tbl_ptr != NULL) + return tbl_ptr; + + fd = open("/dev/mem", O_RDONLY); + if (fd < 0) { + perror("magic open"); + exit(-1); + } + tbl_ptr = mmap(NULL, 4096, + PROT_READ, + MAP_SHARED, + fd, magic_number); + if ((unsigned long)tbl_ptr == ~0UL) { + perror("magic mmap"); + exit(-1); + } + close(fd); + return tbl_ptr; + } + + fd = net_rtacct_open(); + if (fd >= 0) { + nread(fd, (char*)tbl, 256*16); + close(fd); + } else { + memset(tbl, 0, 256*16); + } + return tbl; +} + +void format_rate(FILE *fp, double rate) +{ + char temp[64]; + + if (rate > 1024*1024) { + sprintf(temp, "%uM", (unsigned)rint(rate/(1024*1024))); + fprintf(fp, " %-10s", temp); + } else if (rate > 1024) { + sprintf(temp, "%uK", (unsigned)rint(rate/1024)); + fprintf(fp, " %-10s", temp); + } else + fprintf(fp, " %-10u", (unsigned)rate); +} + +void format_count(FILE *fp, unsigned long long val) +{ + if (val > 1024*1024*1024) + fprintf(fp, " %10lluM", val/(1024*1024)); + else if (val > 1024*1024) + fprintf(fp, " %10lluK", val/1024); + else + fprintf(fp, " %10llu", val); +} + +void dump_abs_db(FILE *fp) +{ + int realm; + char b1[16]; + + if (!no_output) { + fprintf(fp, "#%s\n", kern_db->signature); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); + + } + + for (realm=0; realm<256; realm++) { + int i; + unsigned long long *val; + double *rate; + + if (!(rmap[realm>>5] & (1<<(realm&0x1f)))) + continue; + + val = &kern_db->val[realm*4]; + rate = &kern_db->rate[realm*4]; + + if (!dump_zeros && + !val[0] && !rate[0] && + !val[1] && !rate[1] && + !val[2] && !rate[2] && + !val[3] && !rate[3]) + continue; + + if (hist_db) { + memcpy(&hist_db->val[realm*4], val, sizeof(*val)*4); + } + + if (no_output) + continue; + + fprintf(fp, "%-10s", rtnl_rtrealm_n2a(realm, b1, sizeof(b1))); + for (i = 0; i < 4; i++) + format_count(fp, val[i]); + fprintf(fp, "\n%-10s", ""); + for (i = 0; i < 4; i++) + format_rate(fp, rate[i]); + fprintf(fp, "\n"); + } +} + + +void dump_incr_db(FILE *fp) +{ + int k, realm; + char b1[16]; + + if (!no_output) { + fprintf(fp, "#%s\n", kern_db->signature); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); + } + + for (realm=0; realm<256; realm++) { + int ovfl = 0; + int i; + unsigned long long *val; + double *rate; + unsigned long long rval[4]; + + if (!(rmap[realm>>5] & (1<<(realm&0x1f)))) + continue; + + val = &kern_db->val[realm*4]; + rate = &kern_db->rate[realm*4]; + + for (k=0; k<4; k++) { + rval[k] = val[k]; + if (rval[k] < hist_db->val[realm*4+k]) + ovfl = 1; + else + rval[k] -= hist_db->val[realm*4+k]; + } + if (ovfl) { + for (k=0; k<4; k++) + rval[k] = val[k]; + } + if (hist_db) { + memcpy(&hist_db->val[realm*4], val, sizeof(*val)*4); + } + + if (no_output) + continue; + + if (!dump_zeros && + !rval[0] && !rate[0] && + !rval[1] && !rate[1] && + !rval[2] && !rate[2] && + !rval[3] && !rate[3]) + continue; + + + fprintf(fp, "%-10s", rtnl_rtrealm_n2a(realm, b1, sizeof(b1))); + for (i = 0; i < 4; i++) + format_count(fp, rval[i]); + fprintf(fp, "\n%-10s", ""); + for (i = 0; i < 4; i++) + format_rate(fp, rate[i]); + fprintf(fp, "\n"); + } +} + + +static int children; + +void sigchild(int signo) +{ +} + +/* Server side only: read kernel data, update tables, calculate rates. */ + +void update_db(int interval) +{ + int i; + __u32 *ival; + __u32 _ival[256*4]; + + ival = read_kern_table(_ival); + + for (i=0; i<256*4; i++) { + double sample; + __u32 incr = ival[i] - kern_db->ival[i]; + + if (ival[i] == 0 && incr == 0 && + kern_db->val[i] == 0 && kern_db->rate[i] == 0) + continue; + + kern_db->val[i] += incr; + kern_db->ival[i] = ival[i]; + sample = (double)(incr*1000)/interval; + if (interval >= scan_interval) { + kern_db->rate[i] += W*(sample-kern_db->rate[i]); + } else if (interval >= 1000) { + if (interval >= time_constant) { + kern_db->rate[i] = sample; + } else { + double w = W*(double)interval/scan_interval; + kern_db->rate[i] += w*(sample-kern_db->rate[i]); + } + } + } +} + +void send_db(int fd) +{ + int tot = 0; + + while (tot < sizeof(*kern_db)) { + int n = write(fd, ((char*)kern_db) + tot, sizeof(*kern_db)-tot); + if (n < 0) { + if (errno == EINTR) + continue; + return; + } + tot += n; + } +} + + + +#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) + + +void pad_kern_table(struct rtacct_data *dat, __u32 *ival) +{ + int i; + memset(dat->rate, 0, sizeof(dat->rate)); + if (dat->ival != ival) + memcpy(dat->ival, ival, sizeof(dat->ival)); + for (i=0; i<256*4; i++) + dat->val[i] = ival[i]; +} + +void server_loop(int fd) +{ + struct timeval snaptime; + struct pollfd p; + p.fd = fd; + p.events = p.revents = POLLIN; + + sprintf(kern_db->signature, "%d.%lu sampling_interval=%d time_const=%d", + getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); + + pad_kern_table(kern_db, read_kern_table(kern_db->ival)); + + for (;;) { + int status; + int tdiff; + struct timeval now; + gettimeofday(&now, NULL); + tdiff = T_DIFF(now, snaptime); + if (tdiff >= scan_interval) { + update_db(tdiff); + snaptime = now; + tdiff = 0; + } + if (poll(&p, 1, tdiff + scan_interval) > 0 + && (p.revents&POLLIN)) { + int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { + pid_t pid; + if (children >= 5) { + close(clnt); + } else if ((pid = fork()) != 0) { + if (pid>0) + children++; + close(clnt); + } else { + if (tdiff > 0) + update_db(tdiff); + send_db(clnt); + exit(0); + } + } + } + while (children && waitpid(-1, &status, WNOHANG) > 0) + children--; + } +} + +int verify_forging(int fd) +{ + struct ucred cred; + int olen = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + olen < sizeof(cred)) + return -1; + if (cred.uid == getuid() || cred.uid == 0) + return 0; + return -1; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: rtacct [ -h?vVzrnasd:t: ] [ ListOfRealms ]\n" + ); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + char hist_name[128]; + struct sockaddr_un sun; + int ch; + int fd; + + while ((ch = getopt(argc, argv, "h?vVzrM:nasd:t:")) != EOF) { + switch(ch) { + case 'z': + dump_zeros = 1; + break; + case 'r': + reset_history = 1; + break; + case 'a': + ignore_history = 1; + break; + case 's': + no_update = 1; + break; + case 'n': + no_output = 1; + break; + case 'd': + scan_interval = 1000*atoi(optarg); + break; + case 't': + if (sscanf(optarg, "%d", &time_constant) != 1 || + time_constant <= 0) { + fprintf(stderr, "rtacct: invalid time constant divisor\n"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("rtacct utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'M': + /* Some secret undocumented option, nobody + * is expected to ask about its sense. See? + */ + sscanf(optarg, "%lx", &magic_number); + break; + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc) { + while (argc > 0) { + __u32 realm; + if (rtnl_rtrealm_a2n(&realm, argv[0])) { + fprintf(stderr, "Warning: realm \"%s\" does not exist.\n", argv[0]); + exit(-1); + } + rmap[realm>>5] |= (1<<(realm&0x1f)); + argc--; argv++; + } + } else { + memset(rmap, ~0, sizeof(rmap)); + /* Always suppress zeros. */ + dump_zeros = 0; + } + + sun.sun_family = AF_UNIX; + sun.sun_path[0] = 0; + sprintf(sun.sun_path+1, "rtacct%d", getuid()); + + if (scan_interval > 0) { + if (time_constant == 0) + time_constant = 60; + time_constant *= 1000; + W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant); + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("rtacct: socket"); + exit(-1); + } + if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + perror("rtacct: bind"); + exit(-1); + } + if (listen(fd, 5) < 0) { + perror("rtacct: listen"); + exit(-1); + } + if (fork()) + exit(0); + chdir("/"); + close(0); close(1); close(2); setsid(); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, sigchild); + server_loop(fd); + exit(0); + } + + if (getenv("RTACCT_HISTORY")) + snprintf(hist_name, sizeof(hist_name), getenv("RTACCT_HISTORY")); + else + sprintf(hist_name, "/tmp/.rtacct.u%d", getuid()); + + if (reset_history) + unlink(hist_name); + + if (!ignore_history || !no_update) { + struct stat stb; + + fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600); + if (fd < 0) { + perror("rtacct: open history file"); + exit(-1); + } + if (flock(fd, LOCK_EX)) { + perror("rtacct: flock history file"); + exit(-1); + } + if (fstat(fd, &stb) != 0) { + perror("rtacct: fstat history file"); + exit(-1); + } + if (stb.st_nlink != 1 || stb.st_uid != getuid()) { + fprintf(stderr, "rtacct: something is so wrong with history file, that I prefer not to proceed.\n"); + exit(-1); + } + if (stb.st_size != sizeof(*hist_db)) + write(fd, kern_db, sizeof(*hist_db)); + + hist_db = mmap(NULL, sizeof(*hist_db), + PROT_READ|PROT_WRITE, + no_update ? MAP_PRIVATE : MAP_SHARED, + fd, 0); + + if ((unsigned long)hist_db == ~0UL) { + perror("mmap"); + exit(-1); + } + + if (!ignore_history) { + FILE *tfp; + long uptime; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { + if (fscanf(tfp, "%ld", &uptime) != 1) + uptime = -1; + fclose(tfp); + } + + if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { + fprintf(stderr, "rtacct: history is aged out, resetting\n"); + memset(hist_db, 0, sizeof(*hist_db)); + } + } + + close(fd); + } + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && + (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + || (strcpy(sun.sun_path+1, "rtacct0"), + connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + && verify_forging(fd) == 0) { + nread(fd, (char*)kern_db, sizeof(*kern_db)); + if (hist_db && hist_db->signature[0] && + strcmp(kern_db->signature, hist_db->signature)) { + fprintf(stderr, "rtacct: history is stale, ignoring it.\n"); + hist_db = NULL; + } + close(fd); + } else { + if (fd >= 0) + close(fd); + + if (hist_db && hist_db->signature[0] && + strcmp(hist_db->signature, "kernel")) { + fprintf(stderr, "rtacct: history is stale, ignoring it.\n"); + hist_db = NULL; + } + + pad_kern_table(kern_db, read_kern_table(kern_db->ival)); + strcpy(kern_db->signature, "kernel"); + } + + if (ignore_history || hist_db == NULL) + dump_abs_db(stdout); + else + dump_incr_db(stdout); + + exit(0); +} diff --git a/misc/ss b/misc/ss new file mode 100755 index 0000000..86af12e Binary files /dev/null and b/misc/ss differ diff --git a/misc/ss.c b/misc/ss.c new file mode 100644 index 0000000..668a5bf --- /dev/null +++ b/misc/ss.c @@ -0,0 +1,2803 @@ +/* + * ss.c "sockstat", socket statistics + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <resolv.h> +#include <dirent.h> +#include <fnmatch.h> +#include <getopt.h> + +#include "utils.h" +#include "rt_names.h" +#include "ll_map.h" +#include "libnetlink.h" +#include "SNAPSHOT.h" + +#include <linux/tcp.h> +#include <linux/tcp_diag.h> + +int resolve_hosts = 0; +int resolve_services = 1; +int preferred_family = AF_UNSPEC; +int show_options = 0; +int show_details = 0; +int show_users = 0; +int show_mem = 0; +int show_tcpinfo = 0; + +int netid_width; +int state_width; +int addrp_width; +int addr_width; +int serv_width; +int screen_width; + +static const char *TCP_PROTO = "tcp"; +static const char *UDP_PROTO = "udp"; +static const char *RAW_PROTO = "raw"; +static const char *dg_proto = NULL; + +enum +{ + TCP_DB, + UDP_DB, + RAW_DB, + UNIX_DG_DB, + UNIX_ST_DB, + PACKET_DG_DB, + PACKET_R_DB, + NETLINK_DB, + MAX_DB +}; + +#define PACKET_DBM ((1<<PACKET_DG_DB)|(1<<PACKET_R_DB)) +#define UNIX_DBM ((1<<UNIX_DG_DB)|(1<<UNIX_ST_DB)) +#define ALL_DB ((1<<MAX_DB)-1) + +enum { + SS_UNKNOWN, + SS_ESTABLISHED, + SS_SYN_SENT, + SS_SYN_RECV, + SS_FIN_WAIT1, + SS_FIN_WAIT2, + SS_TIME_WAIT, + SS_CLOSE, + SS_CLOSE_WAIT, + SS_LAST_ACK, + SS_LISTEN, + SS_CLOSING, + SS_MAX +}; + +#define SS_ALL ((1<<SS_MAX)-1) + +#include "ssfilter.h" + +struct filter +{ + int dbs; + int states; + int families; + struct ssfilter *f; +}; + +struct filter default_filter = { + dbs: (1<<TCP_DB), + states: SS_ALL & ~((1<<SS_LISTEN)|(1<<SS_CLOSE)|(1<<SS_TIME_WAIT)|(1<<SS_SYN_RECV)), + families: (1<<AF_INET)|(1<<AF_INET6), +}; + +struct filter current_filter; + +int generic_proc_open(char *env, char *name) +{ + char store[128]; + char *p = getenv(env); + if (!p) { + p = getenv("PROC_ROOT") ? : "/proc"; + snprintf(store, sizeof(store)-1, "%s/%s", p, name); + p = store; + } + return open(store, O_RDONLY); +} + +int net_tcp_open(void) +{ + return generic_proc_open("PROC_NET_TCP", "net/tcp"); +} + +int net_tcp6_open(void) +{ + return generic_proc_open("PROC_NET_TCP6", "net/tcp6"); +} + +int net_udp_open(void) +{ + return generic_proc_open("PROC_NET_UDP", "net/udp"); +} + +int net_udp6_open(void) +{ + return generic_proc_open("PROC_NET_UDP6", "net/udp6"); +} + +int net_raw_open(void) +{ + return generic_proc_open("PROC_NET_RAW", "net/raw"); +} + +int net_raw6_open(void) +{ + return generic_proc_open("PROC_NET_RAW6", "net/raw6"); +} + +int net_unix_open(void) +{ + return generic_proc_open("PROC_NET_UNIX", "net/unix"); +} + +int net_packet_open(void) +{ + return generic_proc_open("PROC_NET_PACKET", "net/packet"); +} + +int net_netlink_open(void) +{ + return generic_proc_open("PROC_NET_NETLINK", "net/netlink"); +} + +int slabinfo_open(void) +{ + return generic_proc_open("PROC_SLABINFO", "slabinfo"); +} + +int net_sockstat_open(void) +{ + return generic_proc_open("PROC_NET_SOCKSTAT", "net/sockstat"); +} + +int net_sockstat6_open(void) +{ + return generic_proc_open("PROC_NET_SOCKSTAT6", "net/sockstat6"); +} + +int net_snmp_open(void) +{ + return generic_proc_open("PROC_NET_SNMP", "net/snmp"); +} + +int net_netstat_open(void) +{ + return generic_proc_open("PROC_NET_NETSTAT", "net/netstat"); +} + +int ephemeral_ports_open(void) +{ + return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range"); +} + +int find_users(int ino, char *buf, int buflen) +{ + char pattern[64]; + int pattern_len; + char *ptr = buf; + char name[1024]; + DIR *dir; + struct dirent *d; + int cnt = 0; + int nameoff; + + if (!ino) + return 0; + + sprintf(pattern, "socket:[%d]", ino); + pattern_len = strlen(pattern); + + strncpy(name, getenv("PROC_ROOT") ? : "/proc/", sizeof(name)/2); + name[sizeof(name)/2] = 0; + if (strlen(name) == 0 || + name[strlen(name)-1] != '/') + strcat(name, "/"); + nameoff = strlen(name); + if ((dir = opendir(name)) == NULL) + return 0; + + while ((d = readdir(dir)) != NULL) { + DIR *dir1; + struct dirent *d1; + int pid; + int pos; + char crap; + char process[16]; + + if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1) + continue; + + sprintf(name+nameoff, "%d/fd/", pid); + pos = strlen(name); + if ((dir1 = opendir(name)) == NULL) + continue; + + process[0] = 0; + + while ((d1 = readdir(dir1)) != NULL) { + int fd, n; + char lnk[64]; + + if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1) + continue; + + sprintf(name+pos, "%d", fd); + n = readlink(name, lnk, sizeof(lnk)-1); + if (n != pattern_len || + memcmp(lnk, pattern, n)) + continue; + + if (ptr-buf >= buflen-1) + break; + + if (process[0] == 0) { + char tmp[1024]; + FILE *fp; + snprintf(tmp, sizeof(tmp), "%s/%d/stat", + getenv("PROC_ROOT") ? : "/proc", pid); + if ((fp = fopen(tmp, "r")) != NULL) { + fscanf(fp, "%*d (%[^)])", process); + fclose(fp); + } + } + + snprintf(ptr, buflen-(ptr-buf), "(\"%s\",%d,%d),", process, pid, fd); + ptr += strlen(ptr); + cnt++; + } + closedir(dir1); + } + closedir(dir); + if (ptr != buf) + ptr[-1] = 0; + return cnt; +} + + +/* Get stats from slab */ + +struct slabstat +{ + int socks; + int tcp_ports; + int tcp_tws; + int tcp_syns; + int skbs; +}; + +struct slabstat slabstat; + +static const char *slabstat_ids[] = +{ + "sock", + "tcp_bind_bucket", + "tcp_tw_bucket", + "tcp_open_request", + "skbuff_head_cache", +}; + +int get_slabstat(struct slabstat *s) +{ + char buf[256]; + FILE *fp; + int cnt; + + memset(s, 0, sizeof(*s)); + + if ((fp = fdopen(slabinfo_open(), "r")) == NULL) + return -1; + + cnt = sizeof(*s)/sizeof(int); + + fgets(buf, sizeof(buf), fp); + while(fgets(buf, sizeof(buf), fp) != NULL) { + int i; + for (i=0; i<sizeof(slabstat_ids)/sizeof(slabstat_ids[0]); i++) { + if (memcmp(buf, slabstat_ids[i], strlen(slabstat_ids[i])) == 0) { + sscanf(buf, "%*s%d", ((int *)s) + i); + cnt--; + break; + } + } + if (cnt <= 0) + break; + } + + fclose(fp); + return 0; +} + +static const char *sstate_name[] = { + "UNKNOWN", + [TCP_ESTABLISHED] = "ESTAB", + [TCP_SYN_SENT] = "SYN-SENT", + [TCP_SYN_RECV] = "SYN-RECV", + [TCP_FIN_WAIT1] = "FIN-WAIT-1", + [TCP_FIN_WAIT2] = "FIN-WAIT-2", + [TCP_TIME_WAIT] = "TIME-WAIT", + [TCP_CLOSE] = "UNCONN", + [TCP_CLOSE_WAIT] = "CLOSE-WAIT", + [TCP_LAST_ACK] = "LAST-ACK", + [TCP_LISTEN] = "LISTEN", + [TCP_CLOSING] = "CLOSING", +}; + +static const char *sstate_namel[] = { + "UNKNOWN", + [TCP_ESTABLISHED] = "established", + [TCP_SYN_SENT] = "syn-sent", + [TCP_SYN_RECV] = "syn-recv", + [TCP_FIN_WAIT1] = "fin-wait-1", + [TCP_FIN_WAIT2] = "fin-wait-2", + [TCP_TIME_WAIT] = "time-wait", + [TCP_CLOSE] = "unconnected", + [TCP_CLOSE_WAIT] = "close-wait", + [TCP_LAST_ACK] = "last-ack", + [TCP_LISTEN] = "listening", + [TCP_CLOSING] = "closing", +}; + +struct tcpstat +{ + inet_prefix local; + inet_prefix remote; + int lport; + int rport; + int state; + int rq, wq; + int timer; + int timeout; + int retrs; + int ino; + int probes; + int uid; + int refcnt; + unsigned long long sk; + int rto, ato, qack, cwnd, ssthresh; +}; + +static const char *tmr_name[] = { + "off", + "on", + "keepalive", + "timewait", + "persist", + "unknown" +}; + +const char *print_ms_timer(int timeout) +{ + static char buf[64]; + int secs, msecs, minutes; + if (timeout < 0) + timeout = 0; + secs = timeout/1000; + minutes = secs/60; + secs = secs%60; + msecs = timeout%1000; + buf[0] = 0; + if (minutes) { + msecs = 0; + snprintf(buf, sizeof(buf)-16, "%dmin", minutes); + if (minutes > 9) + secs = 0; + } + if (secs) { + if (secs > 9) + msecs = 0; + sprintf(buf+strlen(buf), "%d%s", secs, msecs ? "." : "sec"); + } + if (msecs) + sprintf(buf+strlen(buf), "%03dms", msecs); + return buf; +}; + +const char *print_hz_timer(int timeout) +{ + int hz = get_hz(); + return print_ms_timer(((timeout*1000) + hz-1)/hz); +}; + +struct scache +{ + struct scache *next; + int port; + char *name; + const char *proto; +}; + +struct scache *rlist; + +void init_service_resolver(void) +{ + char buf[128]; + FILE *fp = popen("/usr/sbin/rpcinfo -p 2>/dev/null", "r"); + if (fp) { + fgets(buf, sizeof(buf), fp); + while (fgets(buf, sizeof(buf), fp) != NULL) { + unsigned int progn, port; + char proto[128], prog[128]; + if (sscanf(buf, "%u %*d %s %u %s", &progn, proto, + &port, prog+4) == 4) { + struct scache *c = malloc(sizeof(*c)); + if (c) { + c->port = port; + memcpy(prog, "rpc.", 4); + c->name = strdup(prog); + if (strcmp(proto, TCP_PROTO) == 0) + c->proto = TCP_PROTO; + else if (strcmp(proto, UDP_PROTO) == 0) + c->proto = UDP_PROTO; + else + c->proto = NULL; + c->next = rlist; + rlist = c; + } + } + } + } +} + +static int ip_local_port_min, ip_local_port_max; + +/* Even do not try default linux ephemeral port ranges: + * default /etc/services contains so much of useless crap + * wouldbe "allocated" to this area that resolution + * is really harmful. I shrug each time when seeing + * "socks" or "cfinger" in dumps. + */ +static int is_ephemeral(int port) +{ + if (!ip_local_port_min) { + FILE *f = fdopen(ephemeral_ports_open(), "r"); + if (f) { + fscanf(f, "%d %d", + &ip_local_port_min, &ip_local_port_max); + fclose(f); + } else { + ip_local_port_min = 1024; + ip_local_port_max = 4999; + } + } + + return (port >= ip_local_port_min && port<= ip_local_port_max); +} + + +const char *__resolve_service(int port) +{ + struct scache *c; + + for (c = rlist; c; c = c->next) { + if (c->port == port && c->proto == dg_proto) + return c->name; + } + + if (!is_ephemeral(port)) { + static int notfirst; + struct servent *se; + if (!notfirst) { + setservent(1); + notfirst = 1; + } + se = getservbyport(htons(port), dg_proto); + if (se) + return se->s_name; + } + + return NULL; +} + + +const char *resolve_service(int port) +{ + static char buf[128]; + static struct scache cache[256]; + + if (port == 0) { + buf[0] = '*'; + buf[1] = 0; + return buf; + } + + if (resolve_services) { + if (dg_proto == RAW_PROTO) { + return inet_proto_n2a(port, buf, sizeof(buf)); + } else { + struct scache *c; + const char *res; + int hash = (port^(((unsigned long)dg_proto)>>2))&255; + + for (c = &cache[hash]; c; c = c->next) { + if (c->port == port && + c->proto == dg_proto) { + if (c->name) + return c->name; + goto do_numeric; + } + } + + if ((res = __resolve_service(port)) != NULL) { + if ((c = malloc(sizeof(*c))) == NULL) + goto do_numeric; + } else { + c = &cache[hash]; + if (c->name) + free(c->name); + } + c->port = port; + c->name = NULL; + c->proto = dg_proto; + if (res) { + c->name = strdup(res); + c->next = cache[hash].next; + cache[hash].next = c; + } + if (c->name) + return c->name; + } + } + + do_numeric: + sprintf(buf, "%u", port); + return buf; +} + +void formatted_print(const inet_prefix *a, int port) +{ + char buf[1024]; + const char *ap = buf; + int est_len; + + est_len = addr_width; + + if (a->family == AF_INET) { + if (a->data[0] == 0) { + buf[0] = '*'; + buf[1] = 0; + } else { + ap = format_host(AF_INET, 4, a->data, buf, sizeof(buf)); + } + } else { + ap = format_host(a->family, 16, a->data, buf, sizeof(buf)); + est_len = strlen(ap); + if (est_len <= addr_width) + est_len = addr_width; + else + est_len = addr_width + ((est_len-addr_width+3)/4)*4; + } + printf("%*s:%-*s ", est_len, ap, serv_width, resolve_service(port)); +} + +struct aafilter +{ + inet_prefix addr; + int port; + struct aafilter *next; +}; + +int inet2_addr_match(const inet_prefix *a, const inet_prefix *p, int plen) +{ + if (!inet_addr_match(a, p, plen)) + return 0; + + /* Cursed "v4 mapped" addresses: v4 mapped socket matches + * pure IPv4 rule, but v4-mapped rule selects only v4-mapped + * sockets. Fair? */ + if (p->family == AF_INET && a->family == AF_INET6) { + if (a->data[0] == 0 && a->data[1] == 0 && + a->data[2] == htonl(0xffff)) { + inet_prefix tmp = *a; + tmp.data[0] = a->data[3]; + return inet_addr_match(&tmp, p, plen); + } + } + return 1; +} + +int unix_match(const inet_prefix *a, const inet_prefix *p) +{ + char *addr, *pattern; + memcpy(&addr, a->data, sizeof(addr)); + memcpy(&pattern, p->data, sizeof(pattern)); + if (pattern == NULL) + return 1; + if (addr == NULL) + addr = ""; + return !fnmatch(pattern, addr, 0); +} + +int run_ssfilter(struct ssfilter *f, struct tcpstat *s) +{ + switch (f->type) { + case SSF_S_AUTO: + { + static int low, high=65535; + + if (s->local.family == AF_UNIX) { + char *p; + memcpy(&p, s->local.data, sizeof(p)); + return p == NULL || (p[0] == '@' && strlen(p) == 6 && + strspn(p+1, "0123456789abcdef") == 5); + } + if (s->local.family == AF_PACKET) + return s->lport == 0 && s->local.data == 0; + if (s->local.family == AF_NETLINK) + return s->lport < 0; + + if (!low) { + FILE *fp = fdopen(ephemeral_ports_open(), "r"); + if (fp) { + fscanf(fp, "%d%d", &low, &high); + fclose(fp); + } + } + return s->lport >= low && s->lport <= high; + } + case SSF_DCOND: + { + struct aafilter *a = (void*)f->pred; + if (a->addr.family == AF_UNIX) + return unix_match(&s->remote, &a->addr); + if (a->port != -1 && a->port != s->rport) + return 0; + if (a->addr.bitlen) { + do { + if (!inet2_addr_match(&s->remote, &a->addr, a->addr.bitlen)) + return 1; + } while ((a = a->next) != NULL); + return 0; + } + return 1; + } + case SSF_SCOND: + { + struct aafilter *a = (void*)f->pred; + if (a->addr.family == AF_UNIX) + return unix_match(&s->local, &a->addr); + if (a->port != -1 && a->port != s->lport) + return 0; + if (a->addr.bitlen) { + do { + if (!inet2_addr_match(&s->local, &a->addr, a->addr.bitlen)) + return 1; + } while ((a = a->next) != NULL); + return 0; + } + return 1; + } + case SSF_D_GE: + { + struct aafilter *a = (void*)f->pred; + return s->rport >= a->port; + } + case SSF_D_LE: + { + struct aafilter *a = (void*)f->pred; + return s->rport <= a->port; + } + case SSF_S_GE: + { + struct aafilter *a = (void*)f->pred; + return s->lport >= a->port; + } + case SSF_S_LE: + { + struct aafilter *a = (void*)f->pred; + return s->lport <= a->port; + } + + /* Yup. It is recursion. Sorry. */ + case SSF_AND: + return run_ssfilter(f->pred, s) && run_ssfilter(f->post, s); + case SSF_OR: + return run_ssfilter(f->pred, s) || run_ssfilter(f->post, s); + case SSF_NOT: + return !run_ssfilter(f->pred, s); + default: + abort(); + } +} + +/* Relocate external jumps by reloc. */ +static void ssfilter_patch(char *a, int len, int reloc) +{ + while (len > 0) { + struct tcpdiag_bc_op *op = (struct tcpdiag_bc_op*)a; + if (op->no == len+4) + op->no += reloc; + len -= op->yes; + a += op->yes; + } + if (len < 0) + abort(); +} + +static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode) +{ + switch (f->type) { + case SSF_S_AUTO: + { + if (!(*bytecode=malloc(4))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_AUTO, 4, 8 }; + return 8; + } + case SSF_DCOND: + case SSF_SCOND: + { + struct aafilter *a = (void*)f->pred; + struct aafilter *b; + char *ptr; + int code = (f->type == SSF_DCOND ? TCPDIAG_BC_D_COND : TCPDIAG_BC_S_COND); + int len = 0; + + for (b=a; b; b=b->next) { + len += 4 + sizeof(struct tcpdiag_hostcond); + if (a->addr.family == AF_INET6) + len += 16; + else + len += 4; + if (b->next) + len += 4; + } + if (!(ptr = malloc(len))) abort(); + *bytecode = ptr; + for (b=a; b; b=b->next) { + struct tcpdiag_bc_op *op = (struct tcpdiag_bc_op *)ptr; + int alen = (a->addr.family == AF_INET6 ? 16 : 4); + int oplen = alen + 4 + sizeof(struct tcpdiag_hostcond); + struct tcpdiag_hostcond *cond = (struct tcpdiag_hostcond*)(ptr+4); + + *op = (struct tcpdiag_bc_op){ code, oplen, oplen+4 }; + cond->family = a->addr.family; + cond->port = a->port; + cond->prefix_len = a->addr.bitlen; + memcpy(cond->addr, a->addr.data, alen); + ptr += oplen; + if (b->next) { + op = (struct tcpdiag_bc_op *)ptr; + *op = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, len - (ptr-*bytecode)}; + ptr += 4; + } + } + return ptr - *bytecode; + } + case SSF_D_GE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_D_GE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + case SSF_D_LE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_D_LE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + case SSF_S_GE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_S_GE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + case SSF_S_LE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_S_LE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + + case SSF_AND: + { + char *a1, *a2, *a, l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); + l2 = ssfilter_bytecompile(f->post, &a2); + if (!(a = malloc(l1+l2))) abort(); + memcpy(a, a1, l1); + memcpy(a+l1, a2, l2); + free(a1); free(a2); + ssfilter_patch(a, l1, l2); + *bytecode = a; + return l1+l2; + } + case SSF_OR: + { + char *a1, *a2, *a, l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); + l2 = ssfilter_bytecompile(f->post, &a2); + if (!(a = malloc(l1+l2+4))) abort(); + memcpy(a, a1, l1); + memcpy(a+l1+4, a2, l2); + free(a1); free(a2); + *(struct tcpdiag_bc_op*)(a+l1) = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, l2+4 }; + *bytecode = a; + return l1+l2+4; + } + case SSF_NOT: + { + char *a1, *a, l1; + l1 = ssfilter_bytecompile(f->pred, &a1); + if (!(a = malloc(l1+4))) abort(); + memcpy(a, a1, l1); + free(a1); + *(struct tcpdiag_bc_op*)(a+l1) = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, 8 }; + *bytecode = a; + return l1+4; + } + default: + abort(); + } +} + +static int remember_he(struct aafilter *a, struct hostent *he) +{ + char **ptr = he->h_addr_list; + int cnt = 0; + int len; + + if (he->h_addrtype == AF_INET) + len = 4; + else if (he->h_addrtype == AF_INET6) + len = 16; + else + return 0; + + while (*ptr) { + struct aafilter *b = a; + if (a->addr.bitlen) { + if ((b = malloc(sizeof(*b))) == NULL) + return cnt; + *b = *a; + b->next = a->next; + a->next = b; + } + memcpy(b->addr.data, *ptr, len); + b->addr.bytelen = len; + b->addr.bitlen = len*8; + b->addr.family = he->h_addrtype; + ptr++; + cnt++; + } + return cnt; +} + +static int get_dns_host(struct aafilter *a, const char *addr, int fam) +{ + static int notfirst; + int cnt = 0; + struct hostent *he; + + a->addr.bitlen = 0; + if (!notfirst) { + sethostent(1); + notfirst = 1; + } + he = gethostbyname2(addr, fam == AF_UNSPEC ? AF_INET : fam); + if (he) + cnt = remember_he(a, he); + if (fam == AF_UNSPEC) { + he = gethostbyname2(addr, AF_INET6); + if (he) + cnt += remember_he(a, he); + } + return !cnt; +} + +static int xll_initted = 0; + +static void xll_init(void) +{ + struct rtnl_handle rth; + rtnl_open(&rth, 0); + ll_init_map(&rth); + rtnl_close(&rth); + xll_initted = 1; +} + +static const char *xll_index_to_name(int index) +{ + if (!xll_initted) + xll_init(); + return ll_index_to_name(index); +} + +static int xll_name_to_index(const char *dev) +{ + if (!xll_initted) + xll_init(); + return ll_name_to_index(dev); +} + +void *parse_hostcond(char *addr) +{ + char *port = NULL; + struct aafilter a; + struct aafilter *res; + int fam = preferred_family; + + memset(&a, 0, sizeof(a)); + a.port = -1; + + if (fam == AF_UNIX || strncmp(addr, "unix:", 5) == 0) { + char *p; + a.addr.family = AF_UNIX; + if (strncmp(addr, "unix:", 5) == 0) + addr+=5; + p = strdup(addr); + a.addr.bitlen = 8*strlen(p); + memcpy(a.addr.data, &p, sizeof(p)); + goto out; + } + + if (fam == AF_PACKET || strncmp(addr, "link:", 5) == 0) { + a.addr.family = AF_PACKET; + a.addr.bitlen = 0; + if (strncmp(addr, "link:", 5) == 0) + addr+=5; + port = strchr(addr, ':'); + if (port) { + *port = 0; + if (port[1] && strcmp(port+1, "*")) { + if (get_integer(&a.port, port+1, 0)) { + if ((a.port = xll_name_to_index(port+1)) <= 0) + return NULL; + } + } + } + if (addr[0] && strcmp(addr, "*")) { + unsigned short tmp; + a.addr.bitlen = 32; + if (ll_proto_a2n(&tmp, addr)) + return NULL; + a.addr.data[0] = ntohs(tmp); + } + goto out; + } + + if (fam == AF_NETLINK || strncmp(addr, "netlink:", 8) == 0) { + a.addr.family = AF_NETLINK; + a.addr.bitlen = 0; + if (strncmp(addr, "netlink:", 8) == 0) + addr+=8; + port = strchr(addr, ':'); + if (port) { + *port = 0; + if (port[1] && strcmp(port+1, "*")) { + if (get_integer(&a.port, port+1, 0)) { + if (strcmp(port+1, "kernel") == 0) + a.port = 0; + else + return NULL; + } + } + } + if (addr[0] && strcmp(addr, "*")) { + a.addr.bitlen = 32; + if (get_u32(a.addr.data, addr, 0)) { + if (strcmp(addr, "rtnl") == 0) + a.addr.data[0] = 0; + else if (strcmp(addr, "fw") == 0) + a.addr.data[0] = 3; + else if (strcmp(addr, "tcpdiag") == 0) + a.addr.data[0] = 4; + else + return NULL; + } + } + goto out; + } + + if (strncmp(addr, "inet:", 5) == 0) { + addr += 5; + fam = AF_INET; + } else if (strncmp(addr, "inet6:", 6) == 0) { + addr += 6; + fam = AF_INET6; + } + + /* URL-like literal [] */ + if (addr[0] == '[') { + addr++; + if ((port = strchr(addr, ']')) == NULL) + return NULL; + *port++ = 0; + } else if (addr[0] == '*') { + port = addr+1; + } else { + port = strrchr(strchr(addr, '/') ? : addr, ':'); + } + if (port && *port) { + if (*port != ':') + return NULL; + *port++ = 0; + if (*port && *port != '*') { + if (get_integer(&a.port, port, 0)) { + struct servent *se1 = NULL; + struct servent *se2 = NULL; + if (current_filter.dbs&(1<<UDP_DB)) + se1 = getservbyname(port, UDP_PROTO); + if (current_filter.dbs&(1<<TCP_DB)) + se2 = getservbyname(port, TCP_PROTO); + if (se1 && se2 && se1->s_port != se2->s_port) { + fprintf(stderr, "Error: ambiguous port \"%s\".\n", port); + return NULL; + } + if (!se1) + se1 = se2; + if (se1) { + a.port = ntohs(se1->s_port); + } else { + struct scache *s; + for (s = rlist; s; s = s->next) { + if ((s->proto == UDP_PROTO && + (current_filter.dbs&(1<<UDP_DB))) || + (s->proto == TCP_PROTO && + (current_filter.dbs&(1<<TCP_DB)))) { + if (s->name && strcmp(s->name, port) == 0) { + if (a.port > 0 && a.port != s->port) { + fprintf(stderr, "Error: ambiguous port \"%s\".\n", port); + return NULL; + } + a.port = s->port; + } + } + } + if (a.port <= 0) { + fprintf(stderr, "Error: \"%s\" does not look like a port.\n", port); + return NULL; + } + } + } + } + } + if (addr && *addr && *addr != '*') { + if (get_prefix_1(&a.addr, addr, fam)) { + if (get_dns_host(&a, addr, fam)) { + fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", addr); + return NULL; + } + } + } + + out: + res = malloc(sizeof(*res)); + if (res) + memcpy(res, &a, sizeof(a)); + return res; +} + +static int tcp_show_line(char *line, struct filter *f, int family) +{ + struct tcpstat s; + char *loc, *rem, *data; + char opt[256]; + int n; + char *p; + + if ((p = strchr(line, ':')) == NULL) + return -1; + loc = p+2; + + if ((p = strchr(loc, ':')) == NULL) + return -1; + p[5] = 0; + rem = p+6; + + if ((p = strchr(rem, ':')) == NULL) + return -1; + p[5] = 0; + data = p+6; + + do { + int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + + if (!(f->states & (1<<state))) + return 0; + } while (0); + + s.local.family = s.remote.family = family; + if (family == AF_INET) { + sscanf(loc, "%x:%x", s.local.data, (unsigned*)&s.lport); + sscanf(rem, "%x:%x", s.remote.data, (unsigned*)&s.rport); + s.local.bytelen = s.remote.bytelen = 4; + } else { + sscanf(loc, "%08x%08x%08x%08x:%x", + s.local.data, + s.local.data+1, + s.local.data+2, + s.local.data+3, + &s.lport); + sscanf(rem, "%08x%08x%08x%08x:%x", + s.remote.data, + s.remote.data+1, + s.remote.data+2, + s.remote.data+3, + &s.rport); + s.local.bytelen = s.remote.bytelen = 16; + } + + if (f->f && run_ssfilter(f->f, &s) == 0) + return 0; + + opt[0] = 0; + n = sscanf(data, "%x %x:%x %x:%x %x %d %d %d %d %llx %d %d %d %d %d %[^\n]\n", + &s.state, &s.wq, &s.rq, + &s.timer, &s.timeout, &s.retrs, &s.uid, &s.probes, &s.ino, + &s.refcnt, &s.sk, &s.rto, &s.ato, &s.qack, + &s.cwnd, &s.ssthresh, opt); + + if (n < 17) + opt[0] = 0; + + if (n < 12) { + s.rto = 0; + s.cwnd = 2; + s.ssthresh = -1; + s.ato = s.qack = 0; + } + + if (netid_width) + printf("%-*s ", netid_width, "tcp"); + if (state_width) + printf("%-*s ", state_width, sstate_name[s.state]); + + printf("%-6d %-6d ", s.rq, s.wq); + + formatted_print(&s.local, s.lport); + formatted_print(&s.remote, s.rport); + + if (show_options) { + if (s.timer) { + if (s.timer > 4) + s.timer = 5; + printf(" timer:(%s,%s,%d)", + tmr_name[s.timer], + print_hz_timer(s.timeout), + s.timer != 1 ? s.probes : s.retrs); + } + } + if (show_tcpinfo) { + if (s.rto && s.rto != 3*get_hz()) + printf(" rto:%g", (double)s.rto/get_hz()); + if (s.ato) + printf(" ato:%g", (double)s.ato/get_hz()); + if (s.cwnd != 2) + printf(" cwnd:%d", s.cwnd); + if (s.ssthresh != -1) + printf(" ssthresh:%d", s.ssthresh); + if (s.qack/2) + printf(" qack:%d", s.qack/2); + if (s.qack&1) + printf(" bidir"); + } + if (show_users) { + char ubuf[4096]; + if (find_users(s.ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + if (show_details) { + if (s.uid) + printf(" uid:%u", (unsigned)s.uid); + printf(" ino:%u", (unsigned)s.ino); + printf(" sk:%llx", s.sk); + if (opt[0]) + printf(" opt:\"%s\"", opt); + } + printf("\n"); + + return 0; +} + +static int generic_record_read(int fd, char *buf, int bufsize, + int (*worker)(char*, struct filter *, int), + struct filter *f, int fam) +{ + int n; + int recsize; + int eof = 0; + char *p; + + /* Load the first chunk and calculate record length from it. */ + n = read(fd, buf, bufsize); + if (n < 0) + goto outerr; + /* I _know_ that this is wrong, do not remind. :-) + * But this works nowadays. */ + if (n < bufsize) + eof = 1; + p = memchr(buf, '\n', n); + if (p == NULL || (p-buf) >= n) + goto outwrongformat; + recsize = (p-buf)+1; + p = buf+recsize; + + for (;;) { + while ((p+recsize) - buf <= n) { + if (p[recsize-1] != '\n') + goto outwrongformat; + p[recsize-1] = 0; + if (worker(p, f, fam) < 0) + goto done; + p += recsize; + } + if (!eof) { + int remains = (buf+bufsize) - p; + memcpy(buf, p, remains); + p = buf+remains; + n = read(fd, p, (buf+bufsize) - p); + if (n < 0) + goto outerr; + if (n < (buf+bufsize) - p) { + eof = 1; + if (n == 0) { + if (remains) + goto outwrongformat; + goto done; + } + } + n += remains; + p = buf; + } else { + if (p != buf+n) + goto outwrongformat; + goto done; + } + } +done: + return 0; + +outwrongformat: + errno = EINVAL; +outerr: + return -1; +} + +static char *sprint_bw(char *buf, double bw) +{ + if (bw > 1000000.) + sprintf(buf,"%.1fM", bw / 1000000.); + else if (bw > 1000.) + sprintf(buf,"%.1fK", bw / 1000.); + else + sprintf(buf, "%g", bw); + + return buf; +} + +static void tcp_show_info(const struct nlmsghdr *nlh, struct tcpdiagmsg *r) +{ + struct rtattr * tb[TCPDIAG_MAX+1]; + char b1[64]; + double rtt = 0; + + parse_rtattr(tb, TCPDIAG_MAX, (struct rtattr*)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (tb[TCPDIAG_MEMINFO]) { + const struct tcpdiag_meminfo *minfo + = RTA_DATA(tb[TCPDIAG_MEMINFO]); + printf(" mem:(r%u,w%u,f%u,t%u)", + minfo->tcpdiag_rmem, + minfo->tcpdiag_wmem, + minfo->tcpdiag_fmem, + minfo->tcpdiag_tmem); + } + + if (tb[TCPDIAG_INFO]) { + struct tcp_info *info; + int len = RTA_PAYLOAD(tb[TCPDIAG_INFO]); + + /* workaround for older kernels with less fields */ + if (len < sizeof(*info)) { + info = alloca(sizeof(*info)); + memset(info, 0, sizeof(*info)); + memcpy(info, RTA_DATA(tb[TCPDIAG_INFO]), len); + } else + info = RTA_DATA(tb[TCPDIAG_INFO]); + + if (show_options) { + if (info->tcpi_options & TCPI_OPT_TIMESTAMPS) + printf(" ts"); + if (info->tcpi_options & TCPI_OPT_SACK) + printf(" sack"); + if (info->tcpi_options & TCPI_OPT_ECN) + printf(" ecn"); + } + if (info->tcpi_options & TCPI_OPT_WSCALE) + printf(" wscale:%d,%d", info->tcpi_snd_wscale, + info->tcpi_rcv_wscale); + if (info->tcpi_rto && info->tcpi_rto != 3000000) + printf(" rto:%g", (double)info->tcpi_rto/1000); + if (info->tcpi_rtt) + printf(" rtt:%g/%g", (double)info->tcpi_rtt/1000, + (double)info->tcpi_rttvar/1000); + if (info->tcpi_ato) + printf(" ato:%g", (double)info->tcpi_ato/1000); + if (info->tcpi_snd_cwnd != 2) + printf(" cwnd:%d", info->tcpi_snd_cwnd); + if (info->tcpi_snd_ssthresh < 0xFFFF) + printf(" ssthresh:%d", info->tcpi_snd_ssthresh); + + rtt = (double) info->tcpi_rtt; + if (tb[TCPDIAG_VEGASINFO]) { + const struct tcpvegas_info *vinfo + = RTA_DATA(tb[TCPDIAG_VEGASINFO]); + + if (vinfo->tcpv_enabled) + printf(" vegas"); + + if (vinfo->tcpv_rtt && + vinfo->tcpv_rtt != 0x7fffffff) + rtt = vinfo->tcpv_rtt; + } + + if (rtt > 0 && info->tcpi_snd_mss && info->tcpi_snd_cwnd) { + printf(" send %sbps", + sprint_bw(b1, (double) info->tcpi_snd_cwnd * + (double) info->tcpi_snd_mss * 8000000. + / rtt)); + } + + if (info->tcpi_rcv_rtt) + printf(" rcv_rtt:%g", (double) info->tcpi_rcv_rtt/1000); + if (info->tcpi_rcv_space) + printf(" rcv_space:%d", info->tcpi_rcv_space); + + } +} + +int tcp_show_sock(struct nlmsghdr *nlh, struct filter *f) +{ + struct tcpdiagmsg *r = NLMSG_DATA(nlh); + struct tcpstat s; + + s.state = r->tcpdiag_state; + s.local.family = s.remote.family = r->tcpdiag_family; + s.lport = ntohs(r->id.tcpdiag_sport); + s.rport = ntohs(r->id.tcpdiag_dport); + if (s.local.family == AF_INET) { + s.local.bytelen = s.remote.bytelen = 4; + } else { + s.local.bytelen = s.remote.bytelen = 16; + } + memcpy(s.local.data, r->id.tcpdiag_src, s.local.bytelen); + memcpy(s.remote.data, r->id.tcpdiag_dst, s.local.bytelen); + + if (f && f->f && run_ssfilter(f->f, &s) == 0) + return 0; + + if (netid_width) + printf("%-*s ", netid_width, "tcp"); + if (state_width) + printf("%-*s ", state_width, sstate_name[s.state]); + + printf("%-6d %-6d ", r->tcpdiag_rqueue, r->tcpdiag_wqueue); + + formatted_print(&s.local, s.lport); + formatted_print(&s.remote, s.rport); + + if (show_options) { + if (r->tcpdiag_timer) { + if (r->tcpdiag_timer > 4) + r->tcpdiag_timer = 5; + printf(" timer:(%s,%s,%d)", + tmr_name[r->tcpdiag_timer], + print_ms_timer(r->tcpdiag_expires), + r->tcpdiag_retrans); + } + } + if (show_users) { + char ubuf[4096]; + if (find_users(r->tcpdiag_inode, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + if (show_details) { + if (r->tcpdiag_uid) + printf(" uid:%u", (unsigned)r->tcpdiag_uid); + printf(" ino:%u", (unsigned)r->tcpdiag_inode); + printf(" sk:%08x", r->id.tcpdiag_cookie[0]); + if (r->id.tcpdiag_cookie[1] != 0) + printf("%08x", r->id.tcpdiag_cookie[1]); + } + if (show_mem || show_tcpinfo) { + printf("\n\t"); + tcp_show_info(nlh, r); + } + + printf("\n"); + + return 0; + +} + +int tcp_show_netlink(struct filter *f, FILE *dump_fp) +{ + int fd; + struct sockaddr_nl nladdr; + struct { + struct nlmsghdr nlh; + struct tcpdiagreq r; + } req; + char *bc = NULL; + int bclen; + struct msghdr msg; + struct rtattr rta; + char buf[8192]; + struct iovec iov[3]; + + if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TCPDIAG)) < 0) + return -1; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = TCPDIAG_GETSOCK; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = 123456; + memset(&req.r, 0, sizeof(req.r)); + req.r.tcpdiag_family = AF_INET; + req.r.tcpdiag_states = f->states; + if (show_mem) + req.r.tcpdiag_ext |= (1<<(TCPDIAG_MEMINFO-1)); + + if (show_tcpinfo) { + req.r.tcpdiag_ext |= (1<<(TCPDIAG_INFO-1)); + req.r.tcpdiag_ext |= (1<<(TCPDIAG_VEGASINFO-1)); + } + + iov[0] = (struct iovec){ &req, sizeof(req) }; + if (f->f) { + bclen = ssfilter_bytecompile(f->f, &bc); + rta.rta_type = TCPDIAG_REQ_BYTECODE; + rta.rta_len = RTA_LENGTH(bclen); + iov[1] = (struct iovec){ &rta, sizeof(rta) }; + iov[2] = (struct iovec){ bc, bclen }; + req.nlh.nlmsg_len += RTA_LENGTH(bclen); + } + + msg = (struct msghdr) { + (void*)&nladdr, sizeof(nladdr), + iov, f->f ? 3 : 1, + NULL, 0, + 0 + }; + + if (sendmsg(fd, &msg, 0) < 0) + return -1; + + + iov[0] = (struct iovec){ buf, sizeof(buf) }; + + while (1) { + int status; + struct nlmsghdr *h; + + msg = (struct msghdr) { + (void*)&nladdr, sizeof(nladdr), + iov, 1, + NULL, 0, + 0 + }; + + status = recvmsg(fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return 0; + } + + if (dump_fp) + fwrite(buf, 1, NLMSG_ALIGN(status), dump_fp); + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + if (/*h->nlmsg_pid != rth->local.nl_pid ||*/ + h->nlmsg_seq != 123456) + goto skip_it; + + if (h->nlmsg_type == NLMSG_DONE) + return 0; + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("TCPDIAG answers"); + } + return 0; + } + if (!dump_fp) { + err = tcp_show_sock(h, NULL); + if (err < 0) + return err; + } + +skip_it: + h = NLMSG_NEXT(h, status); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } + return 0; +} + +int tcp_show_netlink_file(struct filter *f) +{ + FILE *fp; + char buf[8192]; + + if ((fp = fopen(getenv("TCPDIAG_FILE"), "r")) == NULL) { + perror("fopen($TCPDIAG_FILE)"); + return -1; + } + + while (1) { + int status, err; + struct nlmsghdr *h = (struct nlmsghdr*)buf; + + status = fread(buf, 1, sizeof(*h), fp); + if (status < 0) { + perror("Reading header from $TCPDIAG_FILE"); + return -1; + } + if (status != sizeof(*h)) { + perror("Unexpected EOF reading $TCPDIAG_FILE"); + return -1; + } + + status = fread(h+1, 1, NLMSG_ALIGN(h->nlmsg_len-sizeof(*h)), fp); + + if (status < 0) { + perror("Reading $TCPDIAG_FILE"); + return -1; + } + if (status + sizeof(*h) < h->nlmsg_len) { + perror("Unexpected EOF reading $TCPDIAG_FILE"); + return -1; + } + + /* The only legal exit point */ + if (h->nlmsg_type == NLMSG_DONE) + return 0; + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("TCPDIAG answered"); + } + return -1; + } + + err = tcp_show_sock(h, f); + if (err < 0) + return err; + } +} + +int tcp_show(struct filter *f) +{ + int fd = -1; + char *buf = NULL; + int bufsize = 64*1024; + + dg_proto = TCP_PROTO; + + if (getenv("TCPDIAG_FILE")) + return tcp_show_netlink_file(f); + + if (!getenv("PROC_NET_TCP") && !getenv("PROC_ROOT") + && tcp_show_netlink(f, NULL) == 0) + return 0; + + /* Sigh... We have to parse /proc/net/tcp... */ + + /* Estimate amount of sockets and try to allocate + * huge buffer to read all the table at one read. + * Limit it by 16MB though. The assumption is: as soon as + * kernel was able to hold information about N connections, + * it is able to give us some memory for snapshot. + */ + if (1) { + int guess = slabstat.socks+slabstat.tcp_syns; + if (f->states&(1<<SS_TIME_WAIT)) + guess += slabstat.tcp_tws; + if (guess > (16*1024*1024)/128) + guess = (16*1024*1024)/128; + guess *= 128; + if (guess > bufsize) + bufsize = guess; + } + while (bufsize >= 64*1024) { + if ((buf = malloc(bufsize)) != NULL) + break; + bufsize /= 2; + } + if (buf == NULL) { + errno = ENOMEM; + return -1; + } + + if (f->families & (1<<AF_INET)) { + if ((fd = net_tcp_open()) < 0) + goto outerr; + if (generic_record_read(fd, buf, bufsize, tcp_show_line, f, AF_INET)) + goto outerr; + close(fd); + } + + if ((f->families & (1<<AF_INET6)) && + (fd = net_tcp6_open()) >= 0) { + if (generic_record_read(fd, buf, bufsize, tcp_show_line, f, AF_INET6)) + goto outerr; + close(fd); + } + + free(buf); + return 0; + +outerr: + do { + int saved_errno = errno; + if (buf) + free(buf); + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; + } while (0); +} + + +int dgram_show_line(char *line, struct filter *f, int family) +{ + struct tcpstat s; + char *loc, *rem, *data; + char opt[256]; + int n; + char *p; + + if ((p = strchr(line, ':')) == NULL) + return -1; + loc = p+2; + + if ((p = strchr(loc, ':')) == NULL) + return -1; + p[5] = 0; + rem = p+6; + + if ((p = strchr(rem, ':')) == NULL) + return -1; + p[5] = 0; + data = p+6; + + do { + int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + + if (!(f->states & (1<<state))) + return 0; + } while (0); + + s.local.family = s.remote.family = family; + if (family == AF_INET) { + sscanf(loc, "%x:%x", s.local.data, (unsigned*)&s.lport); + sscanf(rem, "%x:%x", s.remote.data, (unsigned*)&s.rport); + s.local.bytelen = s.remote.bytelen = 4; + } else { + sscanf(loc, "%08x%08x%08x%08x:%x", + s.local.data, + s.local.data+1, + s.local.data+2, + s.local.data+3, + &s.lport); + sscanf(rem, "%08x%08x%08x%08x:%x", + s.remote.data, + s.remote.data+1, + s.remote.data+2, + s.remote.data+3, + &s.rport); + s.local.bytelen = s.remote.bytelen = 16; + } + + if (f->f && run_ssfilter(f->f, &s) == 0) + return 0; + + opt[0] = 0; + n = sscanf(data, "%x %x:%x %*x:%*x %*x %d %*d %d %d %llx %[^\n]\n", + &s.state, &s.wq, &s.rq, + &s.uid, &s.ino, + &s.refcnt, &s.sk, opt); + + if (n < 9) + opt[0] = 0; + + if (netid_width) + printf("%-*s ", netid_width, dg_proto); + if (state_width) + printf("%-*s ", state_width, sstate_name[s.state]); + + printf("%-6d %-6d ", s.rq, s.wq); + + formatted_print(&s.local, s.lport); + formatted_print(&s.remote, s.rport); + + if (show_users) { + char ubuf[4096]; + if (find_users(s.ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + + if (show_details) { + if (s.uid) + printf(" uid=%u", (unsigned)s.uid); + printf(" ino=%u", (unsigned)s.ino); + printf(" sk=%llx", s.sk); + if (opt[0]) + printf(" opt:\"%s\"", opt); + } + printf("\n"); + + return 0; +} + + +int udp_show(struct filter *f) +{ + int fd = -1; + char buf[8192]; + int bufsize = sizeof(buf); + + dg_proto = UDP_PROTO; + + if (f->families&(1<<AF_INET)) { + if ((fd = net_udp_open()) < 0) + goto outerr; + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET)) + goto outerr; + close(fd); + } + + if ((f->families&(1<<AF_INET6)) && + (fd = net_udp6_open()) >= 0) { + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET6)) + goto outerr; + close(fd); + } + return 0; + +outerr: + do { + int saved_errno = errno; + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; + } while (0); +} + +int raw_show(struct filter *f) +{ + int fd = -1; + char buf[8192]; + int bufsize = sizeof(buf); + + dg_proto = RAW_PROTO; + + if (f->families&(1<<AF_INET)) { + if ((fd = net_raw_open()) < 0) + goto outerr; + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET)) + goto outerr; + close(fd); + } + + if ((f->families&(1<<AF_INET6)) && + (fd = net_raw6_open()) >= 0) { + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET6)) + goto outerr; + close(fd); + } + return 0; + +outerr: + do { + int saved_errno = errno; + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; + } while (0); +} + + +struct unixstat +{ + struct unixstat *next; + int ino; + int peer; + int rq; + int wq; + int state; + int type; + char *name; +}; + + + +int unix_state_map[] = { SS_CLOSE, SS_SYN_SENT, + SS_ESTABLISHED, SS_CLOSING }; + + +#define MAX_UNIX_REMEMBER (1024*1024/sizeof(struct unixstat)) + +void unix_list_free(struct unixstat *list) +{ + while (list) { + struct unixstat *s = list; + list = list->next; + if (s->name) + free(s->name); + free(s); + } +} + +void unix_list_print(struct unixstat *list, struct filter *f) +{ + struct unixstat *s; + char *peer; + + for (s = list; s; s = s->next) { + if (!(f->states & (1<<s->state))) + continue; + if (s->type == SOCK_STREAM && !(f->dbs&(1<<UNIX_ST_DB))) + continue; + if (s->type == SOCK_DGRAM && !(f->dbs&(1<<UNIX_DG_DB))) + continue; + + peer = "*"; + if (s->peer) { + struct unixstat *p; + for (p = list; p; p = p->next) { + if (s->peer == p->ino) + break; + } + if (!p) { + peer = "?"; + } else { + peer = p->name ? : "*"; + } + } + + if (f->f) { + struct tcpstat tst; + tst.local.family = AF_UNIX; + tst.remote.family = AF_UNIX; + memcpy(tst.local.data, &s->name, sizeof(s->name)); + if (strcmp(peer, "*") == 0) + memset(tst.remote.data, 0, sizeof(peer)); + else + memcpy(tst.remote.data, &peer, sizeof(peer)); + if (run_ssfilter(f->f, &tst) == 0) + continue; + } + + if (netid_width) + printf("%-*s ", netid_width, + s->type == SOCK_STREAM ? "u_str" : "u_dgr"); + if (state_width) + printf("%-*s ", state_width, sstate_name[s->state]); + printf("%-6d %-6d ", s->rq, s->wq); + printf("%*s %-*d %*s %-*d", + addr_width, s->name ? : "*", serv_width, s->ino, + addr_width, peer, serv_width, s->peer); + if (show_users) { + char ubuf[4096]; + if (find_users(s->ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + printf("\n"); + } +} + +int unix_show(struct filter *f) +{ + FILE *fp; + char buf[256]; + char name[128]; + int newformat = 0; + int cnt; + struct unixstat *list = NULL; + + if ((fp = fdopen(net_unix_open(), "r")) == NULL) + return -1; + fgets(buf, sizeof(buf)-1, fp); + + if (memcmp(buf, "Peer", 4) == 0) + newformat = 1; + cnt = 0; + + while (fgets(buf, sizeof(buf)-1, fp)) { + struct unixstat *u, **insp; + int flags; + + if (!(u = malloc(sizeof(*u)))) + break; + u->name = NULL; + + if (sscanf(buf, "%x: %x %x %x %x %x %d %s", + &u->peer, &u->rq, &u->wq, &flags, &u->type, + &u->state, &u->ino, name) < 8) + name[0] = 0; + + if (flags&(1<<16)) { + u->state = SS_LISTEN; + } else { + u->state = unix_state_map[u->state-1]; + if (u->type == SOCK_DGRAM && + u->state == SS_CLOSE && + u->peer) + u->state = SS_ESTABLISHED; + } + + if (!newformat) { + u->peer = 0; + u->rq = 0; + u->wq = 0; + } + + insp = &list; + while (*insp) { + if (u->type < (*insp)->type || + (u->type == (*insp)->type && + u->ino < (*insp)->ino)) + break; + insp = &(*insp)->next; + } + u->next = *insp; + *insp = u; + + if (name[0]) { + if ((u->name = malloc(strlen(name)+1)) == NULL) + break; + strcpy(u->name, name); + } + if (++cnt > MAX_UNIX_REMEMBER) { + unix_list_print(list, f); + unix_list_free(list); + list = NULL; + cnt = 0; + } + } + + if (list) { + unix_list_print(list, f); + unix_list_free(list); + list = NULL; + cnt = 0; + } + + return 0; +} + + +int packet_show(struct filter *f) +{ + FILE *fp; + char buf[256]; + int type; + int prot; + int iface; + int state; + int rq; + int uid; + int ino; + unsigned long long sk; + + if (!(f->states & (1<<SS_CLOSE))) + return 0; + + if ((fp = fdopen(net_packet_open(), "r")) == NULL) + return -1; + fgets(buf, sizeof(buf)-1, fp); + + while (fgets(buf, sizeof(buf)-1, fp)) { + sscanf(buf, "%llx %*d %d %x %d %d %u %u %u", + &sk, + &type, &prot, &iface, &state, + &rq, &uid, &ino); + + if (type == SOCK_RAW && !(f->dbs&(1<<PACKET_R_DB))) + continue; + if (type == SOCK_DGRAM && !(f->dbs&(1<<PACKET_DG_DB))) + continue; + if (f->f) { + struct tcpstat tst; + tst.local.family = AF_PACKET; + tst.remote.family = AF_PACKET; + tst.rport = 0; + tst.lport = iface; + tst.local.data[0] = prot; + tst.remote.data[0] = 0; + if (run_ssfilter(f->f, &tst) == 0) + continue; + } + + if (netid_width) + printf("%-*s ", netid_width, + type == SOCK_RAW ? "p_raw" : "p_dgr"); + if (state_width) + printf("%-*s ", state_width, "UNCONN"); + printf("%-6d %-6d ", rq, 0); + if (prot == 3) { + printf("%*s:", addr_width, "*"); + } else { + char tb[16]; + printf("%*s:", addr_width, + ll_proto_n2a(htons(prot), tb, sizeof(tb))); + } + if (iface == 0) { + printf("%-*s ", serv_width, "*"); + } else { + printf("%-*s ", serv_width, xll_index_to_name(iface)); + } + printf("%*s*%-*s", + addr_width, "", serv_width, ""); + + if (show_users) { + char ubuf[4096]; + if (find_users(ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + if (show_details) { + printf(" ino=%u uid=%u sk=%llx", ino, uid, sk); + } + printf("\n"); + } + + return 0; +} + +int netlink_show(struct filter *f) +{ + FILE *fp; + char buf[256]; + int prot, pid; + unsigned groups; + int rq, wq, rc; + unsigned long long sk, cb; + + if (!(f->states & (1<<SS_CLOSE))) + return 0; + + if ((fp = fdopen(net_netlink_open(), "r")) == NULL) + return -1; + fgets(buf, sizeof(buf)-1, fp); + + while (fgets(buf, sizeof(buf)-1, fp)) { + sscanf(buf, "%llx %d %d %x %d %d %llx %d", + &sk, + &prot, &pid, &groups, &rq, &wq, &cb, &rc); + + if (f->f) { + struct tcpstat tst; + tst.local.family = AF_NETLINK; + tst.remote.family = AF_NETLINK; + tst.rport = -1; + tst.lport = pid; + tst.local.data[0] = prot; + tst.remote.data[0] = 0; + if (run_ssfilter(f->f, &tst) == 0) + continue; + } + + if (netid_width) + printf("%-*s ", netid_width, "nl"); + if (state_width) + printf("%-*s ", state_width, "UNCONN"); + printf("%-6d %-6d ", rq, wq); + if (resolve_services && prot == 0) + printf("%*s:", addr_width, "rtnl"); + else if (resolve_services && prot == 3) + printf("%*s:", addr_width, "fw"); + else if (resolve_services && prot == 4) + printf("%*s:", addr_width, "tcpdiag"); + else + printf("%*d:", addr_width, prot); + if (pid == -1) { + printf("%-*s ", serv_width, "*"); + } else if (resolve_services) { + int done = 0; + if (!pid) { + done = 1; + printf("%-*s ", serv_width, "kernel"); + } else if (pid > 0) { + char procname[64]; + FILE *fp; + sprintf(procname, "%s/%d/stat", + getenv("PROC_ROOT") ? : "/proc", pid); + if ((fp = fopen(procname, "r")) != NULL) { + if (fscanf(fp, "%*d (%[^)])", procname) == 1) { + sprintf(procname+strlen(procname), "/%d", pid); + printf("%-*s ", serv_width, procname); + done = 1; + } + fclose(fp); + } + } + if (!done) + printf("%-*d ", serv_width, pid); + } else { + printf("%-*d ", serv_width, pid); + } + printf("%*s*%-*s", + addr_width, "", serv_width, ""); + + if (show_details) { + printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups); + } + printf("\n"); + } + + return 0; +} + +struct snmpstat +{ + int tcp_estab; +}; + +int get_snmp_int(char *proto, char *key, int *result) +{ + char buf[1024]; + FILE *fp; + int protolen = strlen(proto); + int keylen = strlen(key); + + *result = 0; + + if ((fp = fdopen(net_snmp_open(), "r")) == NULL) + return -1; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *p = buf; + int pos = 0; + if (memcmp(buf, proto, protolen)) + continue; + while ((p = strchr(p, ' ')) != NULL) { + pos++; + p++; + if (memcmp(p, key, keylen) == 0 && + (p[keylen] == ' ' || p[keylen] == '\n')) + break; + } + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + if (memcmp(buf, proto, protolen)) + break; + p = buf; + while ((p = strchr(p, ' ')) != NULL) { + p++; + if (--pos == 0) { + sscanf(p, "%d", result); + fclose(fp); + return 0; + } + } + } + + fclose(fp); + errno = ESRCH; + return -1; +} + + +/* Get stats from sockstat */ + +struct sockstat +{ + int socks; + int tcp_mem; + int tcp_total; + int tcp_orphans; + int tcp_tws; + int tcp4_hashed; + int udp4; + int raw4; + int frag4; + int frag4_mem; + int tcp6_hashed; + int udp6; + int raw6; + int frag6; + int frag6_mem; +}; + +static void get_sockstat_line(char *line, struct sockstat *s) +{ + char id[256], rem[256]; + + if (sscanf(line, "%[^ ] %[^\n]\n", id, rem) != 2) + return; + + if (strcmp(id, "sockets:") == 0) + sscanf(rem, "%*s%d", &s->socks); + else if (strcmp(id, "UDP:") == 0) + sscanf(rem, "%*s%d", &s->udp4); + else if (strcmp(id, "UDP6:") == 0) + sscanf(rem, "%*s%d", &s->udp6); + else if (strcmp(id, "RAW:") == 0) + sscanf(rem, "%*s%d", &s->raw4); + else if (strcmp(id, "RAW6:") == 0) + sscanf(rem, "%*s%d", &s->raw6); + else if (strcmp(id, "TCP6:") == 0) + sscanf(rem, "%*s%d", &s->tcp6_hashed); + else if (strcmp(id, "FRAG:") == 0) + sscanf(rem, "%*s%d%*s%d", &s->frag4, &s->frag4_mem); + else if (strcmp(id, "FRAG6:") == 0) + sscanf(rem, "%*s%d%*s%d", &s->frag6, &s->frag6_mem); + else if (strcmp(id, "TCP:") == 0) + sscanf(rem, "%*s%d%*s%d%*s%d%*s%d%*s%d", + &s->tcp4_hashed, + &s->tcp_orphans, &s->tcp_tws, &s->tcp_total, &s->tcp_mem); +} + +int get_sockstat(struct sockstat *s) +{ + char buf[256]; + FILE *fp; + + memset(s, 0, sizeof(*s)); + + if ((fp = fdopen(net_sockstat_open(), "r")) == NULL) + return -1; + while(fgets(buf, sizeof(buf), fp) != NULL) + get_sockstat_line(buf, s); + fclose(fp); + + if ((fp = fdopen(net_sockstat6_open(), "r")) == NULL) + return 0; + while(fgets(buf, sizeof(buf), fp) != NULL) + get_sockstat_line(buf, s); + fclose(fp); + + return 0; +} + +int print_summary(void) +{ + struct sockstat s; + struct snmpstat sn; + + if (get_sockstat(&s) < 0) + perror("ss: get_sockstat"); + if (get_snmp_int("Tcp:", "CurrEstab", &sn.tcp_estab) < 0) + perror("ss: get_snmpstat"); + + printf("Total: %d (kernel %d)\n", s.socks, slabstat.socks); + + printf("TCP: %d (estab %d, closed %d, orphaned %d, synrecv %d, timewait %d/%d), ports %d\n", + s.tcp_total + slabstat.tcp_syns + s.tcp_tws, + sn.tcp_estab, + s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws), + s.tcp_orphans, + slabstat.tcp_syns, + s.tcp_tws, slabstat.tcp_tws, + slabstat.tcp_ports + ); + + printf("\n"); + printf("Transport Total IP IPv6\n"); + printf("* %-9d %-9s %-9s\n", slabstat.socks, "-", "-"); + printf("RAW %-9d %-9d %-9d\n", s.raw4+s.raw6, s.raw4, s.raw6); + printf("UDP %-9d %-9d %-9d\n", s.udp4+s.udp6, s.udp4, s.udp6); + printf("TCP %-9d %-9d %-9d\n", s.tcp4_hashed+s.tcp6_hashed, s.tcp4_hashed, s.tcp6_hashed); + printf("INET %-9d %-9d %-9d\n", + s.raw4+s.udp4+s.tcp4_hashed+ + s.raw6+s.udp6+s.tcp6_hashed, + s.raw4+s.udp4+s.tcp4_hashed, + s.raw6+s.udp6+s.tcp6_hashed); + printf("FRAG %-9d %-9d %-9d\n", s.frag4+s.frag6, s.frag4, s.frag6); + + printf("\n"); + + return 0; +} + + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: ss [ OPTIONS ]\n" +" ss [ OPTIONS ] [ FILTER ]\n" +" -h, --help this message\n" +" -V, --version output version information\n" +" -n, --numeric don't resolve service names\n" +" -r, --resolve resolve host names\n" +" -a, --all display all sockets\n" +" -l, --listening display listening sockets\n" +" -o, --options show timer information\n" +" -e, --extended show detailed socket information\n" +" -m, --memory show socket memory usage\n" +" -p, --processes show process using socket\n" +" -i, --info show internal TCP information\n" +" -s, --summary show socket usage summary\n" +"\n" +" -4, --ipv4 display only IP version 4 sockets\n" +" -6, --ipv6 display only IP version 6 sockets\n" +" -0, --packet display PACKET sockets\n" +" -t, --tcp display only TCP sockets\n" +" -u, --udp display only UDP sockets\n" +" -w, --raw display only RAW sockets\n" +" -x, --unix display only Unix domain sockets\n" +" -f, --family=FAMILY display sockets of type FAMILY\n" +"\n" +" -A, --query=QUERY\n" +" QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]\n" +"\n" +" -F, --filter=FILE read filter information from FILE\n" +" FILTER := [ state TCP-STATE ] [ EXPRESSION ]\n" + ); + exit(-1); +} + + +int scan_state(const char *state) +{ + int i; + if (strcasecmp(state, "close") == 0 || + strcasecmp(state, "closed") == 0) + return (1<<SS_CLOSE); + if (strcasecmp(state, "syn-rcv") == 0) + return (1<<SS_SYN_RECV); + if (strcasecmp(state, "established") == 0) + return (1<<SS_ESTABLISHED); + if (strcasecmp(state, "all") == 0) + return SS_ALL; + if (strcasecmp(state, "connected") == 0) + return SS_ALL & ~((1<<SS_CLOSE)|(1<<SS_LISTEN)); + if (strcasecmp(state, "synchronized") == 0) + return SS_ALL & ~((1<<SS_CLOSE)|(1<<SS_LISTEN)|(1<<SS_SYN_SENT)); + if (strcasecmp(state, "bucket") == 0) + return (1<<SS_SYN_RECV)|(1<<SS_TIME_WAIT); + if (strcasecmp(state, "big") == 0) + return SS_ALL & ~((1<<SS_SYN_RECV)|(1<<SS_TIME_WAIT)); + for (i=0; i<SS_MAX; i++) { + if (strcasecmp(state, sstate_namel[i]) == 0) + return (1<<i); + } + return 0; +} + +static const struct option long_opts[] = { + { "numeric", 0, 0, 'n' }, + { "resolve", 0, 0, 'r' }, + { "options", 0, 0, 'o' }, + { "extended", 0, 0, 'e' }, + { "memory", 0, 0, 'm' }, + { "info", 0, 0, 'i' }, + { "processes", 0, 0, 'p' }, + { "tcp", 0, 0, 't' }, + { "udp", 0, 0, 'u' }, + { "raw", 0, 0, 'w' }, + { "unix", 0, 0, 'x' }, + { "all", 0, 0, 'a' }, + { "listening", 0, 0, 'l' }, + { "ipv4", 0, 0, '4' }, + { "ipv6", 0, 0, '6' }, + { "packet", 0, 0, '0' }, + { "family", 1, 0, 'f' }, + { "socket", 1, 0, 'A' }, + { "summary", 0, 0, 's' }, + { "diag", 0, 0, 'D' }, + { "filter", 1, 0, 'F' }, + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { 0 } + +}; + +int main(int argc, char *argv[]) +{ + int do_default = 1; + int saw_states = 0; + int saw_query = 0; + int do_summary = 0; + const char *dump_tcpdiag = NULL; + FILE *filter_fp = NULL; + int ch; + + memset(¤t_filter, 0, sizeof(current_filter)); + + current_filter.states = default_filter.states; + + while ((ch = getopt_long(argc, argv, "haletuwxnro460spf:miA:D:F:vV", + long_opts, NULL)) != EOF) { + switch(ch) { + case 'n': + resolve_services = 0; + break; + case 'r': + resolve_hosts = 1; + break; + case 'o': + show_options = 1; + break; + case 'e': + show_options = 1; + show_details++; + break; + case 'm': + show_mem = 1; + break; + case 'i': + show_tcpinfo = 1; + break; + case 'p': + show_users++; + break; + case 't': + current_filter.dbs |= (1<<TCP_DB); + do_default = 0; + break; + case 'u': + current_filter.dbs |= (1<<UDP_DB); + do_default = 0; + break; + case 'w': + current_filter.dbs |= (1<<RAW_DB); + do_default = 0; + break; + case 'x': + current_filter.dbs |= UNIX_DBM; + do_default = 0; + break; + case 'a': + current_filter.states = SS_ALL; + break; + case 'l': + current_filter.states = (1<<SS_LISTEN); + break; + case '4': + preferred_family = AF_INET; + break; + case '6': + preferred_family = AF_INET6; + break; + case '0': + preferred_family = AF_PACKET; + break; + case 'f': + if (strcmp(optarg, "inet") == 0) + preferred_family = AF_INET; + else if (strcmp(optarg, "inet6") == 0) + preferred_family = AF_INET6; + else if (strcmp(optarg, "link") == 0) + preferred_family = AF_PACKET; + else if (strcmp(optarg, "unix") == 0) + preferred_family = AF_UNIX; + else if (strcmp(optarg, "netlink") == 0) + preferred_family = AF_NETLINK; + else if (strcmp(optarg, "help") == 0) + usage(); + else { + fprintf(stderr, "ss: \"%s\" is invalid family\n", optarg); + usage(); + } + break; + case 'A': + { + char *p, *p1; + if (!saw_query) { + current_filter.dbs = 0; + saw_query = 1; + do_default = 0; + } + p = p1 = optarg; + do { + if ((p1 = strchr(p, ',')) != NULL) + *p1 = 0; + if (strcmp(p, "all") == 0) { + current_filter.dbs = ALL_DB; + } else if (strcmp(p, "inet") == 0) { + current_filter.dbs |= (1<<TCP_DB)|(1<<UDP_DB)|(1<<RAW_DB); + } else if (strcmp(p, "udp") == 0) { + current_filter.dbs |= (1<<UDP_DB); + } else if (strcmp(p, "tcp") == 0) { + current_filter.dbs |= (1<<TCP_DB); + } else if (strcmp(p, "raw") == 0) { + current_filter.dbs |= (1<<RAW_DB); + } else if (strcmp(p, "unix") == 0) { + current_filter.dbs |= UNIX_DBM; + } else if (strcasecmp(p, "unix_stream") == 0 || + strcmp(p, "u_str") == 0) { + current_filter.dbs |= (1<<UNIX_ST_DB); + } else if (strcasecmp(p, "unix_dgram") == 0 || + strcmp(p, "u_dgr") == 0) { + current_filter.dbs |= (1<<UNIX_DG_DB); + } else if (strcmp(p, "packet") == 0) { + current_filter.dbs |= PACKET_DBM; + } else if (strcmp(p, "packet_raw") == 0 || + strcmp(p, "p_raw") == 0) { + current_filter.dbs |= (1<<PACKET_R_DB); + } else if (strcmp(p, "packet_dgram") == 0 || + strcmp(p, "p_dgr") == 0) { + current_filter.dbs |= (1<<PACKET_DG_DB); + } else if (strcmp(p, "netlink") == 0) { + current_filter.dbs |= (1<<NETLINK_DB); + } else { + fprintf(stderr, "ss: \"%s\" is illegal socket table id\n", p); + usage(); + } + p = p1 + 1; + } while (p1); + break; + } + case 's': + do_summary = 1; + break; + case 'D': + dump_tcpdiag = optarg; + break; + case 'F': + if (filter_fp) { + fprintf(stderr, "More than one filter file\n"); + exit(-1); + } + if (optarg[0] == '-') + filter_fp = stdin; + else + filter_fp = fopen(optarg, "r"); + if (!filter_fp) { + perror("fopen filter file"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("ss utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + get_slabstat(&slabstat); + + if (do_summary) { + print_summary(); + if (do_default && argc == 0) + exit(0); + } + + if (do_default) + current_filter.dbs = default_filter.dbs; + + if (preferred_family == AF_UNSPEC) { + if (!(current_filter.dbs&~UNIX_DBM)) + preferred_family = AF_UNIX; + else if (!(current_filter.dbs&~PACKET_DBM)) + preferred_family = AF_PACKET; + else if (!(current_filter.dbs&~(1<<NETLINK_DB))) + preferred_family = AF_NETLINK; + } + + if (preferred_family != AF_UNSPEC) { + int mask2; + if (preferred_family == AF_INET || + preferred_family == AF_INET6) { + mask2= (1<<TCP_DB); + if (!do_default) + mask2 = (1<<UDP_DB)|(1<<RAW_DB); + } else if (preferred_family == AF_PACKET) { + mask2 = PACKET_DBM; + } else if (preferred_family == AF_UNIX) { + mask2 = UNIX_DBM; + } else if (preferred_family == AF_NETLINK) { + mask2 = (1<<NETLINK_DB); + } else { + mask2 = 0; + } + + if (do_default) + current_filter.dbs = mask2; + else + current_filter.dbs &= mask2; + current_filter.families = (1<<preferred_family); + } else { + if (!do_default) + current_filter.families = ~0; + else + current_filter.families = default_filter.families; + } + if (current_filter.dbs == 0) { + fprintf(stderr, "ss: no socket tables to show with such filter.\n"); + exit(0); + } + if (current_filter.families == 0) { + fprintf(stderr, "ss: no families to show with such filter.\n"); + exit(0); + } + + if (resolve_services && resolve_hosts && + (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)))) + init_service_resolver(); + + /* Now parse filter... */ + if (argc == 0 && filter_fp) { + if (ssfilter_parse(¤t_filter.f, 0, NULL, filter_fp)) + usage(); + } + + while (argc > 0) { + if (strcmp(*argv, "state") == 0) { + NEXT_ARG(); + if (!saw_states) + current_filter.states = 0; + current_filter.states |= scan_state(*argv); + saw_states = 1; + } else if (strcmp(*argv, "exclude") == 0 || + strcmp(*argv, "excl") == 0) { + NEXT_ARG(); + if (!saw_states) + current_filter.states = SS_ALL; + current_filter.states &= ~scan_state(*argv); + saw_states = 1; + } else { + if (ssfilter_parse(¤t_filter.f, argc, argv, filter_fp)) + usage(); + break; + } + argc--; argv++; + } + + if (current_filter.states == 0) { + fprintf(stderr, "ss: no socket states to show with such filter.\n"); + exit(0); + } + + if (dump_tcpdiag) { + FILE *dump_fp = stdout; + if (!(current_filter.dbs & (1<<TCP_DB))) { + fprintf(stderr, "ss: tcpdiag dump requested and no tcp in filter.\n"); + exit(0); + } + if (dump_tcpdiag[0] != '-') { + dump_fp = fopen(dump_tcpdiag, "w"); + if (!dump_tcpdiag) { + perror("fopen dump file"); + exit(-1); + } + } + tcp_show_netlink(¤t_filter, dump_fp); + fflush(dump_fp); + exit(0); + } + + netid_width = 0; + if (current_filter.dbs&(current_filter.dbs-1)) + netid_width = 5; + + state_width = 0; + if (current_filter.states&(current_filter.states-1)) + state_width = 10; + + screen_width = 80; + if (isatty(STDOUT_FILENO)) { + struct winsize w; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { + if (w.ws_col > 0) + screen_width = w.ws_col; + } + } + + addrp_width = screen_width; + addrp_width -= netid_width+1; + addrp_width -= state_width+1; + addrp_width -= 14; + + if (addrp_width&1) { + if (netid_width) + netid_width++; + else if (state_width) + state_width++; + } + + addrp_width /= 2; + addrp_width--; + + serv_width = resolve_services ? 7 : 5; + + if (addrp_width < 15+serv_width+1) + addrp_width = 15+serv_width+1; + + addr_width = addrp_width - serv_width - 1; + + if (netid_width) + printf("%-*s ", netid_width, "Netid"); + if (state_width) + printf("%-*s ", state_width, "State"); + printf("%-6s %-6s ", "Recv-Q", "Send-Q"); + + printf("%*s:%-*s %*s:%-*s\n", + addr_width, "Local Address", serv_width, "Port", + addr_width, "Peer Address", serv_width, "Port"); + +//printf("%08x %08x %08x\n", current_filter.dbs, current_filter.states, current_filter.families); + fflush(stdout); + + if (current_filter.dbs & (1<<NETLINK_DB)) + netlink_show(¤t_filter); + if (current_filter.dbs & PACKET_DBM) + packet_show(¤t_filter); + if (current_filter.dbs & UNIX_DBM) + unix_show(¤t_filter); + if (current_filter.dbs & (1<<RAW_DB)) + raw_show(¤t_filter); + if (current_filter.dbs & (1<<UDP_DB)) + udp_show(¤t_filter); + if (current_filter.dbs & (1<<TCP_DB)) + tcp_show(¤t_filter); + return 0; +} diff --git a/misc/ss.o b/misc/ss.o new file mode 100644 index 0000000..18ee0d8 Binary files /dev/null and b/misc/ss.o differ diff --git a/misc/ssfilter.c b/misc/ssfilter.c new file mode 100644 index 0000000..fa73a87 --- /dev/null +++ b/misc/ssfilter.c @@ -0,0 +1,1581 @@ +/* A Bison parser, made by GNU Bison 1.875c. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + 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, 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + HOSTCOND = 258, + DCOND = 259, + SCOND = 260, + DPORT = 261, + SPORT = 262, + LEQ = 263, + GEQ = 264, + NEQ = 265, + AUTOBOUND = 266 + }; +#endif +#define HOSTCOND 258 +#define DCOND 259 +#define SCOND 260 +#define DPORT 261 +#define SPORT 262 +#define LEQ 263 +#define GEQ 264 +#define NEQ 265 +#define AUTOBOUND 266 + + + + +/* Copy the first part of user declarations. */ +#line 1 "ssfilter.y" + + +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <string.h> +#include "ssfilter.h" + +typedef struct ssfilter * ssfilter_t; + +#define YYSTYPE ssfilter_t + +static struct ssfilter * alloc_node(int type, void *pred) +{ + struct ssfilter *n = malloc(sizeof(*n)); + if (n == NULL) + abort(); + n->type = type; + n->pred = pred; + n->post = NULL; + return n; +} + +static char **yy_argv; +static int yy_argc; +static FILE *yy_fp; +static ssfilter_t *yy_ret; + +static int yylex(void); + +static void yyerror(char *s) +{ + fprintf(stderr, "ss: bison bellows (while parsing filter): \"%s!\"", s); +} + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 214 of yacc.c. */ +#line 146 "ssfilter.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +# ifndef YYFREE +# define YYFREE free +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# endif + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# endif +# else +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined (__GNUC__) && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 3 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 90 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 20 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 4 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 24 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 46 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 266 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 14, 2, 2, 2, 2, 13, 2, + 18, 19, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 16, 17, 15, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 12, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 6, 8, 9, 12, 15, 19, 23, + 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, + 65, 69, 72, 76, 79 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 21, 0, -1, 22, 23, -1, 22, -1, -1, 4, + 3, -1, 5, 3, -1, 6, 9, 3, -1, 6, + 8, 3, -1, 6, 15, 3, -1, 6, 16, 3, + -1, 6, 17, 3, -1, 6, 10, 3, -1, 7, + 9, 3, -1, 7, 8, 3, -1, 7, 15, 3, + -1, 7, 16, 3, -1, 7, 17, 3, -1, 7, + 10, 3, -1, 11, -1, 23, 12, 23, -1, 23, + 23, -1, 23, 13, 23, -1, 14, 23, -1, 18, + 23, 19, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned char yyrline[] = +{ + 0, 44, 44, 49, 51, 53, 57, 61, 65, 69, + 73, 77, 81, 86, 90, 94, 98, 102, 106, 111, + 115, 120, 125, 131, 135 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "HOSTCOND", "DCOND", "SCOND", "DPORT", + "SPORT", "LEQ", "GEQ", "NEQ", "AUTOBOUND", "'|'", "'&'", "'!'", "'>'", + "'<'", "'='", "'('", "')'", "$accept", "applet", "null", "expr", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 124, 38, 33, 62, 60, 61, 40, 41 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 20, 21, 21, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 2, 1, 0, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, + 3, 2, 3, 2, 3 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 4, 0, 3, 1, 0, 0, 0, 0, 19, 0, + 0, 2, 5, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, + 21, 8, 7, 12, 9, 10, 11, 14, 13, 18, + 15, 16, 17, 24, 20, 22 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yysigned_char yydefgoto[] = +{ + -1, 1, 2, 30 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -3 +static const yysigned_char yypact[] = +{ + -3, 1, 47, -3, 6, 11, 69, 73, -3, 47, + 47, 17, -3, -3, 12, 13, 16, 22, 29, 30, + 31, 37, 38, 39, 41, 44, 62, -1, 47, 47, + 17, -3, -3, -3, -3, -3, -3, -3, -3, -3, + -3, -3, -3, -3, 32, 47 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yysigned_char yypgoto[] = +{ + -3, -3, -3, -2 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yysigned_char yytable[] = +{ + 11, 3, 0, 4, 5, 6, 7, 26, 27, 12, + 8, 28, 29, 9, 13, 31, 32, 10, 43, 33, + 0, 4, 5, 6, 7, 34, 44, 45, 8, 28, + 29, 9, 35, 36, 37, 10, 4, 5, 6, 7, + 38, 39, 40, 8, 41, 29, 9, 42, 0, 0, + 10, 4, 5, 6, 7, 0, 0, 0, 8, 0, + 0, 9, 0, 0, 0, 10, 4, 5, 6, 7, + 0, 0, 0, 8, 0, 0, -1, 14, 15, 16, + 10, 20, 21, 22, 17, 18, 19, 0, 23, 24, + 25 +}; + +static const yysigned_char yycheck[] = +{ + 2, 0, -1, 4, 5, 6, 7, 9, 10, 3, + 11, 12, 13, 14, 3, 3, 3, 18, 19, 3, + -1, 4, 5, 6, 7, 3, 28, 29, 11, 12, + 13, 14, 3, 3, 3, 18, 4, 5, 6, 7, + 3, 3, 3, 11, 3, 13, 14, 3, -1, -1, + 18, 4, 5, 6, 7, -1, -1, -1, 11, -1, + -1, 14, -1, -1, -1, 18, 4, 5, 6, 7, + -1, -1, -1, 11, -1, -1, 14, 8, 9, 10, + 18, 8, 9, 10, 15, 16, 17, -1, 15, 16, + 17 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 21, 22, 0, 4, 5, 6, 7, 11, 14, + 18, 23, 3, 3, 8, 9, 10, 15, 16, 17, + 8, 9, 10, 15, 16, 17, 23, 23, 12, 13, + 23, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 19, 23, 23 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + ((Current).first_line = (Rhs)[1].first_line, \ + (Current).first_column = (Rhs)[1].first_column, \ + (Current).last_line = (Rhs)[N].last_line, \ + (Current).last_column = (Rhs)[N].last_column) +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 45 "ssfilter.y" + { + *yy_ret = yyvsp[0]; + yyval = yyvsp[0]; + ;} + break; + + case 4: +#line 51 "ssfilter.y" + { yyval = NULL; ;} + break; + + case 5: +#line 54 "ssfilter.y" + { + yyval = alloc_node(SSF_DCOND, yyvsp[0]); + ;} + break; + + case 6: +#line 58 "ssfilter.y" + { + yyval = alloc_node(SSF_SCOND, yyvsp[0]); + ;} + break; + + case 7: +#line 62 "ssfilter.y" + { + yyval = alloc_node(SSF_D_GE, yyvsp[0]); + ;} + break; + + case 8: +#line 66 "ssfilter.y" + { + yyval = alloc_node(SSF_D_LE, yyvsp[0]); + ;} + break; + + case 9: +#line 70 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_D_LE, yyvsp[0])); + ;} + break; + + case 10: +#line 74 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_D_GE, yyvsp[0])); + ;} + break; + + case 11: +#line 78 "ssfilter.y" + { + yyval = alloc_node(SSF_DCOND, yyvsp[0]); + ;} + break; + + case 12: +#line 82 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_DCOND, yyvsp[0])); + ;} + break; + + case 13: +#line 87 "ssfilter.y" + { + yyval = alloc_node(SSF_S_GE, yyvsp[0]); + ;} + break; + + case 14: +#line 91 "ssfilter.y" + { + yyval = alloc_node(SSF_S_LE, yyvsp[0]); + ;} + break; + + case 15: +#line 95 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_S_LE, yyvsp[0])); + ;} + break; + + case 16: +#line 99 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_S_GE, yyvsp[0])); + ;} + break; + + case 17: +#line 103 "ssfilter.y" + { + yyval = alloc_node(SSF_SCOND, yyvsp[0]); + ;} + break; + + case 18: +#line 107 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_SCOND, yyvsp[0])); + ;} + break; + + case 19: +#line 112 "ssfilter.y" + { + yyval = alloc_node(SSF_S_AUTO, NULL); + ;} + break; + + case 20: +#line 116 "ssfilter.y" + { + yyval = alloc_node(SSF_OR, yyvsp[-2]); + yyval->post = yyvsp[0]; + ;} + break; + + case 21: +#line 121 "ssfilter.y" + { + yyval = alloc_node(SSF_AND, yyvsp[-1]); + yyval->post = yyvsp[0]; + ;} + break; + + case 22: +#line 127 "ssfilter.y" + { + yyval = alloc_node(SSF_AND, yyvsp[-2]); + yyval->post = yyvsp[0]; + ;} + break; + + case 23: +#line 132 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, yyvsp[0]); + ;} + break; + + case 24: +#line 136 "ssfilter.y" + { + yyval = yyvsp[-1]; + ;} + break; + + + } + +/* Line 1000 of yacc.c. */ +#line 1219 "ssfilter.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + const char* yyprefix; + char *yymsg; + int yyx; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 0; + + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); + yycount += 1; + if (yycount == 5) + { + yysize = 0; + break; + } + } + yysize += (sizeof ("syntax error, unexpected ") + + yystrlen (yytname[yytype])); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yyp = yystpcpy (yyp, yyprefix); + yyp = yystpcpy (yyp, yytname[yyx]); + yyprefix = " or "; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* If at end of input, pop the error token, + then the rest of the stack, then return failure. */ + if (yychar == YYEOF) + for (;;) + { + YYPOPSTACK; + if (yyssp == yyss) + YYABORT; + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + } + } + else + { + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + +#ifdef __GNUC__ + /* Pacify GCC when the user code never invokes YYERROR and the label + yyerrorlab therefore never appears in user code. */ + if (0) + goto yyerrorlab; +#endif + + yyvsp -= yylen; + yyssp -= yylen; + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + YYPOPSTACK; + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 140 "ssfilter.y" + + +static char *get_token_from_line(char **ptr) +{ + char *tok, *cp = *ptr; + + while (*cp == ' ' || *cp == '\t') cp++; + + if (*cp == 0) { + *ptr = cp; + return NULL; + } + + tok = cp; + + while (*cp != 0 && *cp != ' ' && *cp != '\t') { + /* Backslash escapes everything. */ + if (*cp == '\\') { + char *tp; + for (tp = cp; tp != tok; tp--) + *tp = *(tp-1); + cp++; + tok++; + if (*cp == 0) + break; + } + cp++; + } + if (*cp) + *cp++ = 0; + *ptr = cp; + return tok; +} + +int yylex(void) +{ + static char argbuf[1024]; + static char *tokptr = argbuf; + static int argc; + char *curtok; + + do { + while (*tokptr == 0) { + tokptr = NULL; + if (argc < yy_argc) { + tokptr = yy_argv[argc]; + argc++; + } else if (yy_fp) { + while (tokptr == NULL) { + if (fgets(argbuf, sizeof(argbuf)-1, yy_fp) == NULL) + return 0; + argbuf[sizeof(argbuf)-1] = 0; + if (strlen(argbuf) == sizeof(argbuf) - 1) { + fprintf(stderr, "Too long line in filter"); + exit(-1); + } + if (argbuf[strlen(argbuf)-1] == '\n') + argbuf[strlen(argbuf)-1] = 0; + if (argbuf[0] == '#' || argbuf[0] == '0') + continue; + tokptr = argbuf; + } + } else { + return 0; + } + } + } while ((curtok = get_token_from_line(&tokptr)) == NULL); + + if (strcmp(curtok, "!") == 0 || + strcmp(curtok, "not") == 0) + return '!'; + if (strcmp(curtok, "&") == 0 || + strcmp(curtok, "&&") == 0 || + strcmp(curtok, "and") == 0) + return '&'; + if (strcmp(curtok, "|") == 0 || + strcmp(curtok, "||") == 0 || + strcmp(curtok, "or") == 0) + return '|'; + if (strcmp(curtok, "(") == 0) + return '('; + if (strcmp(curtok, ")") == 0) + return ')'; + if (strcmp(curtok, "dst") == 0) + return DCOND; + if (strcmp(curtok, "src") == 0) + return SCOND; + if (strcmp(curtok, "dport") == 0) + return DPORT; + if (strcmp(curtok, "sport") == 0) + return SPORT; + if (strcmp(curtok, ">=") == 0 || + strcmp(curtok, "ge") == 0 || + strcmp(curtok, "geq") == 0) + return GEQ; + if (strcmp(curtok, "<=") == 0 || + strcmp(curtok, "le") == 0 || + strcmp(curtok, "leq") == 0) + return LEQ; + if (strcmp(curtok, "!=") == 0 || + strcmp(curtok, "ne") == 0 || + strcmp(curtok, "neq") == 0) + return NEQ; + if (strcmp(curtok, "=") == 0 || + strcmp(curtok, "==") == 0 || + strcmp(curtok, "eq") == 0) + return '='; + if (strcmp(curtok, ">") == 0 || + strcmp(curtok, "gt") == 0) + return '>'; + if (strcmp(curtok, "<") == 0 || + strcmp(curtok, "lt") == 0) + return '<'; + if (strcmp(curtok, "autobound") == 0) + return AUTOBOUND; + yylval = (void*)parse_hostcond(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse dst/src address.\n"); + exit(1); + } + return HOSTCOND; +} + +int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp) +{ + yy_argc = argc; + yy_argv = argv; + yy_fp = fp; + yy_ret = f; + + if (yyparse()) { + fprintf(stderr, " Sorry.\n"); + return -1; + } + return 0; +} + + diff --git a/misc/ssfilter.h b/misc/ssfilter.h new file mode 100644 index 0000000..00b92e3 --- /dev/null +++ b/misc/ssfilter.h @@ -0,0 +1,21 @@ +#define SSF_DCOND 0 +#define SSF_SCOND 1 +#define SSF_OR 2 +#define SSF_AND 3 +#define SSF_NOT 4 +#define SSF_D_GE 5 +#define SSF_D_LE 6 +#define SSF_S_GE 7 +#define SSF_S_LE 8 +#define SSF_S_AUTO 9 + +struct ssfilter +{ + int type; + struct ssfilter *post; + struct ssfilter *pred; +}; + +int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp); +void *parse_hostcond(char*); + diff --git a/misc/ssfilter.o b/misc/ssfilter.o new file mode 100644 index 0000000..477cb5b Binary files /dev/null and b/misc/ssfilter.o differ diff --git a/misc/ssfilter.y b/misc/ssfilter.y new file mode 100644 index 0000000..8be5368 --- /dev/null +++ b/misc/ssfilter.y @@ -0,0 +1,275 @@ +%{ + +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <string.h> +#include "ssfilter.h" + +typedef struct ssfilter * ssfilter_t; + +#define YYSTYPE ssfilter_t + +static struct ssfilter * alloc_node(int type, void *pred) +{ + struct ssfilter *n = malloc(sizeof(*n)); + if (n == NULL) + abort(); + n->type = type; + n->pred = pred; + n->post = NULL; + return n; +} + +static char **yy_argv; +static int yy_argc; +static FILE *yy_fp; +static ssfilter_t *yy_ret; + +static int yylex(void); + +static void yyerror(char *s) +{ + fprintf(stderr, "ss: bison bellows (while parsing filter): \"%s!\"", s); +} + +%} + +%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND +%left '|' +%left '&' +%nonassoc '!' + +%% +applet: null expr + { + *yy_ret = $2; + $$ = $2; + } + | null + ; +null: /* NOTHING */ { $$ = NULL; } + ; +expr: DCOND HOSTCOND + { + $$ = alloc_node(SSF_DCOND, $2); + } + | SCOND HOSTCOND + { + $$ = alloc_node(SSF_SCOND, $2); + } + | DPORT GEQ HOSTCOND + { + $$ = alloc_node(SSF_D_GE, $3); + } + | DPORT LEQ HOSTCOND + { + $$ = alloc_node(SSF_D_LE, $3); + } + | DPORT '>' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_D_LE, $3)); + } + | DPORT '<' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_D_GE, $3)); + } + | DPORT '=' HOSTCOND + { + $$ = alloc_node(SSF_DCOND, $3); + } + | DPORT NEQ HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_DCOND, $3)); + } + + | SPORT GEQ HOSTCOND + { + $$ = alloc_node(SSF_S_GE, $3); + } + | SPORT LEQ HOSTCOND + { + $$ = alloc_node(SSF_S_LE, $3); + } + | SPORT '>' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_S_LE, $3)); + } + | SPORT '<' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_S_GE, $3)); + } + | SPORT '=' HOSTCOND + { + $$ = alloc_node(SSF_SCOND, $3); + } + | SPORT NEQ HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_SCOND, $3)); + } + + | AUTOBOUND + { + $$ = alloc_node(SSF_S_AUTO, NULL); + } + | expr '|' expr + { + $$ = alloc_node(SSF_OR, $1); + $$->post = $3; + } + | expr expr + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $2; + } + | expr '&' expr + + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $3; + } + | '!' expr + { + $$ = alloc_node(SSF_NOT, $2); + } + | '(' expr ')' + { + $$ = $2; + } +; +%% + +static char *get_token_from_line(char **ptr) +{ + char *tok, *cp = *ptr; + + while (*cp == ' ' || *cp == '\t') cp++; + + if (*cp == 0) { + *ptr = cp; + return NULL; + } + + tok = cp; + + while (*cp != 0 && *cp != ' ' && *cp != '\t') { + /* Backslash escapes everything. */ + if (*cp == '\\') { + char *tp; + for (tp = cp; tp != tok; tp--) + *tp = *(tp-1); + cp++; + tok++; + if (*cp == 0) + break; + } + cp++; + } + if (*cp) + *cp++ = 0; + *ptr = cp; + return tok; +} + +int yylex(void) +{ + static char argbuf[1024]; + static char *tokptr = argbuf; + static int argc; + char *curtok; + + do { + while (*tokptr == 0) { + tokptr = NULL; + if (argc < yy_argc) { + tokptr = yy_argv[argc]; + argc++; + } else if (yy_fp) { + while (tokptr == NULL) { + if (fgets(argbuf, sizeof(argbuf)-1, yy_fp) == NULL) + return 0; + argbuf[sizeof(argbuf)-1] = 0; + if (strlen(argbuf) == sizeof(argbuf) - 1) { + fprintf(stderr, "Too long line in filter"); + exit(-1); + } + if (argbuf[strlen(argbuf)-1] == '\n') + argbuf[strlen(argbuf)-1] = 0; + if (argbuf[0] == '#' || argbuf[0] == '0') + continue; + tokptr = argbuf; + } + } else { + return 0; + } + } + } while ((curtok = get_token_from_line(&tokptr)) == NULL); + + if (strcmp(curtok, "!") == 0 || + strcmp(curtok, "not") == 0) + return '!'; + if (strcmp(curtok, "&") == 0 || + strcmp(curtok, "&&") == 0 || + strcmp(curtok, "and") == 0) + return '&'; + if (strcmp(curtok, "|") == 0 || + strcmp(curtok, "||") == 0 || + strcmp(curtok, "or") == 0) + return '|'; + if (strcmp(curtok, "(") == 0) + return '('; + if (strcmp(curtok, ")") == 0) + return ')'; + if (strcmp(curtok, "dst") == 0) + return DCOND; + if (strcmp(curtok, "src") == 0) + return SCOND; + if (strcmp(curtok, "dport") == 0) + return DPORT; + if (strcmp(curtok, "sport") == 0) + return SPORT; + if (strcmp(curtok, ">=") == 0 || + strcmp(curtok, "ge") == 0 || + strcmp(curtok, "geq") == 0) + return GEQ; + if (strcmp(curtok, "<=") == 0 || + strcmp(curtok, "le") == 0 || + strcmp(curtok, "leq") == 0) + return LEQ; + if (strcmp(curtok, "!=") == 0 || + strcmp(curtok, "ne") == 0 || + strcmp(curtok, "neq") == 0) + return NEQ; + if (strcmp(curtok, "=") == 0 || + strcmp(curtok, "==") == 0 || + strcmp(curtok, "eq") == 0) + return '='; + if (strcmp(curtok, ">") == 0 || + strcmp(curtok, "gt") == 0) + return '>'; + if (strcmp(curtok, "<") == 0 || + strcmp(curtok, "lt") == 0) + return '<'; + if (strcmp(curtok, "autobound") == 0) + return AUTOBOUND; + yylval = (void*)parse_hostcond(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse dst/src address.\n"); + exit(1); + } + return HOSTCOND; +} + +int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp) +{ + yy_argc = argc; + yy_argv = argv; + yy_fp = fp; + yy_ret = f; + + if (yyparse()) { + fprintf(stderr, " Sorry.\n"); + return -1; + } + return 0; +} diff --git a/netem/Makefile b/netem/Makefile new file mode 100644 index 0000000..881ed9f --- /dev/null +++ b/netem/Makefile @@ -0,0 +1,27 @@ +DISTGEN = maketable normal pareto paretonormal +DISTDATA = normal.dist pareto.dist paretonormal.dist experimental.dist + +LDLIBS += -lm + +%.dist: %.c + $(HOSTCC) $(CFLAGS) -o $* $< -lm + ./$* >$@ + +%.dist: %.dat + ./maketable $< >$@ + +all: $(DISTGEN) $(DISTDATA) + +install: all + mkdir -p $(DESTDIR)/usr/lib/tc + for i in $(DISTDATA); \ + do install -m 755 $$i $(DESTDIR)/usr/lib/tc; \ + done + +clean: + rm -f $(DISTDATA) $(DISTGEN) + +maketable: maketable.c + $(HOSTCC) $(CFLAGS) -o $@ $< -lm + + diff --git a/netem/README.distribution b/netem/README.distribution new file mode 100644 index 0000000..23f7ecb --- /dev/null +++ b/netem/README.distribution @@ -0,0 +1,97 @@ +Notes about distribution tables from Nistnet +------------------------------------------------------------------------------- +I. About the distribution tables + +The table used for "synthesizing" the distribution is essentially a scaled, +translated, inverse to the cumulative distribution function. + +Here's how to think about it: Let F() be the cumulative distribution +function for a probability distribution X. We'll assume we've scaled +things so that X has mean 0 and standard deviation 1, though that's not +so important here. Then: + + F(x) = P(X <= x) = \int_{-inf}^x f + +where f is the probability density function. + +F is monotonically increasing, so has an inverse function G, with range +0 to 1. Here, G(t) = the x such that P(X <= x) = t. (In general, G may +have singularities if X has point masses, i.e., points x such that +P(X = x) > 0.) + +Now we create a tabular representation of G as follows: Choose some table +size N, and for the ith entry, put in G(i/N). Let's call this table T. + +The claim now is, I can create a (discrete) random variable Y whose +distribution has the same approximate "shape" as X, simply by letting +Y = T(U), where U is a discrete uniform random variable with range 1 to N. +To see this, it's enough to show that Y's cumulative distribution function, +(let's call it H), is a discrete approximation to F. But + + H(x) = P(Y <= x) + = (# of entries in T <= x) / N -- as Y chosen uniformly from T + = i/N, where i is the largest integer such that G(i/N) <= x + = i/N, where i is the largest integer such that i/N <= F(x) + -- since G and F are inverse functions (and F is + increasing) + = floor(N*F(x))/N + +as desired. + +II. How to create distribution tables (in theory) + +How can we create this table in practice? In some cases, F may have a +simple expression which allows evaluating its inverse directly. The +pareto distribution is one example of this. In other cases, and +especially for matching an experimentally observed distribution, it's +easiest simply to create a table for F and "invert" it. Here, we give +a concrete example, namely how the new "experimental" distribution was +created. + +1. Collect enough data points to characterize the distribution. Here, I +collected 25,000 "ping" roundtrip times to a "distant" point (time.nist.gov). +That's far more data than is really necessary, but it was fairly painless to +collect it, so... + +2. Normalize the data so that it has mean 0 and standard deviation 1. + +3. Determine the cumulative distribution. The code I wrote creates a table +covering the range -10 to +10, with granularity .00005. Obviously, this +is absurdly over-precise, but since it's a one-time only computation, I +figured it hardly mattered. + +4. Invert the table: for each table entry F(x) = y, make the y*TABLESIZE +(here, 4096) entry be x*TABLEFACTOR (here, 8192). This creates a table +for the ("normalized") inverse of size TABLESIZE, covering its domain 0 +to 1 with granularity 1/TABLESIZE. Note that even with the granularity +used in creating the table for F, it's possible not all the entries in +the table for G will be filled in. So, make a pass through the +inverse's table, filling in any missing entries by linear interpolation. + +III. How to create distribution tables (in practice) + +If you want to do all this yourself, I've provided several tools to help: + +1. maketable does the steps 2-4 above, and then generates the appropriate +header file. So if you have your own time distribution, you can generate +the header simply by: + + maketable < time.values > header.h + +2. As explained in the other README file, the somewhat sleazy way I have +of generating correlated values needs correction. You can generate your +own correction tables by compiling makesigtable and makemutable with +your header file. Check the Makefile to see how this is done. + +3. Warning: maketable, makesigtable and especially makemutable do +enormous amounts of floating point arithmetic. Don't try running +these on an old 486. (NIST Net itself will run fine on such a +system, since in operation, it just needs to do a few simple integral +calculations. But getting there takes some work.) + +4. The tables produced are all normalized for mean 0 and standard +deviation 1. How do you know what values to use for real? Here, I've +provided a simple "stats" utility. Give it a series of floating point +values, and it will return their mean (mu), standard deviation (sigma), +and correlation coefficient (rho). You can then plug these values +directly into NIST Net. diff --git a/netem/experimental.dat b/netem/experimental.dat new file mode 100644 index 0000000..3663a3e --- /dev/null +++ b/netem/experimental.dat @@ -0,0 +1,13448 @@ +211.6 +205.6 +203.0 +218.6 +213.9 +199.1 +208.7 +207.7 +203.4 +201.7 +200.3 +213.8 +213.4 +209.8 +204.3 +201.8 +196.3 +216.2 +208.9 +202.4 +205.2 +211.1 +210.9 +208.5 +199.9 +211.6 +211.9 +204.6 +215.4 +202.5 +206.5 +201.1 +198.4 +220.2 +203.7 +219.5 +199.1 +207.6 +205.3 +202.3 +219.7 +230.0 +211.0 +202.7 +209.9 +215.4 +202.9 +209.6 +200.5 +197.3 +212.3 +207.6 +210.5 +202.7 +205.7 +211.2 +208.0 +211.0 +209.4 +204.8 +204.8 +208.7 +210.1 +205.3 +202.5 +210.4 +209.4 +204.5 +204.7 +215.0 +202.6 +209.9 +220.2 +203.8 +206.3 +199.4 +221.8 +200.0 +199.6 +209.3 +206.2 +215.8 +196.9 +211.6 +198.4 +201.2 +209.4 +204.3 +219.0 +212.7 +214.6 +196.3 +202.0 +201.9 +197.5 +229.5 +207.5 +213.8 +209.2 +212.9 +193.9 +200.8 +208.6 +196.8 +201.3 +204.9 +204.7 +209.5 +211.3 +215.3 +203.7 +190.1 +235.6 +203.8 +210.0 +209.7 +214.3 +213.0 +206.3 +197.7 +208.2 +226.3 +216.5 +198.0 +201.3 +211.3 +195.8 +210.9 +208.1 +201.2 +201.7 +213.1 +207.9 +206.6 +207.1 +202.2 +199.6 +205.5 +207.3 +219.7 +204.1 +204.4 +209.0 +212.7 +196.4 +214.0 +208.8 +209.7 +217.2 +196.2 +195.0 +227.7 +207.2 +233.3 +207.9 +204.0 +194.4 +219.2 +208.7 +198.6 +205.0 +204.0 +223.7 +207.4 +209.2 +208.7 +205.4 +212.8 +207.8 +203.0 +204.1 +221.0 +198.4 +217.7 +218.4 +374.2 +220.1 +210.8 +212.1 +214.3 +213.3 +210.3 +202.4 +209.7 +218.1 +205.0 +204.5 +220.3 +209.8 +218.3 +216.6 +206.0 +208.9 +221.0 +213.0 +202.1 +204.2 +220.6 +212.4 +226.1 +208.8 +206.1 +220.7 +219.3 +210.9 +211.2 +213.0 +201.4 +210.5 +206.2 +201.9 +224.5 +219.3 +201.1 +195.6 +223.6 +196.7 +213.7 +202.3 +215.6 +211.4 +209.6 +207.6 +212.4 +203.4 +205.4 +216.1 +216.7 +205.3 +213.9 +208.9 +208.4 +205.1 +199.3 +200.6 +199.1 +203.2 +207.6 +203.8 +201.9 +208.5 +196.4 +213.6 +217.6 +201.5 +210.1 +213.5 +203.8 +214.1 +211.9 +201.5 +186.9 +199.7 +209.1 +200.2 +205.8 +206.7 +200.0 +198.1 +209.3 +207.8 +208.7 +208.0 +208.6 +231.3 +214.5 +210.1 +200.8 +208.9 +216.9 +205.7 +214.9 +236.8 +200.9 +219.1 +204.6 +210.0 +214.0 +222.6 +209.6 +207.0 +196.3 +207.7 +207.9 +208.0 +220.2 +198.2 +204.9 +204.1 +201.0 +204.8 +213.3 +203.9 +222.5 +205.2 +203.5 +209.7 +212.1 +210.1 +221.1 +210.2 +208.0 +201.4 +209.0 +211.9 +201.6 +214.4 +199.6 +198.8 +210.2 +207.3 +206.5 +204.8 +196.3 +199.8 +206.4 +195.3 +202.8 +202.7 +203.8 +211.2 +208.4 +198.6 +202.0 +214.9 +204.2 +201.1 +195.9 +196.1 +211.2 +197.0 +207.7 +196.6 +205.7 +211.4 +201.4 +205.0 +195.5 +198.9 +214.4 +207.3 +204.2 +207.2 +198.5 +220.7 +214.1 +213.2 +207.7 +203.6 +265.8 +221.0 +213.1 +195.4 +197.3 +213.0 +207.7 +206.0 +198.4 +202.3 +213.9 +218.6 +207.6 +206.1 +212.8 +216.8 +213.7 +209.8 +198.1 +202.4 +205.3 +207.0 +209.2 +209.9 +204.4 +199.6 +205.5 +203.9 +216.0 +213.1 +202.4 +199.0 +219.5 +193.9 +197.3 +212.2 +216.7 +217.5 +201.0 +206.2 +202.9 +211.3 +203.1 +218.0 +208.6 +217.8 +209.0 +211.8 +220.1 +212.7 +207.2 +221.2 +215.2 +196.9 +216.6 +203.1 +207.1 +216.7 +206.7 +215.0 +219.3 +204.3 +219.6 +207.1 +211.8 +210.2 +217.2 +207.9 +219.9 +205.4 +201.1 +214.1 +205.8 +212.5 +222.8 +211.9 +217.4 +203.8 +222.9 +206.6 +207.6 +197.5 +206.2 +218.5 +220.3 +207.7 +203.5 +226.4 +216.8 +206.0 +193.2 +198.2 +201.3 +202.4 +208.5 +212.6 +205.0 +202.2 +210.0 +202.4 +203.9 +193.3 +212.4 +203.4 +212.1 +206.1 +206.9 +207.0 +216.1 +201.1 +204.7 +202.4 +207.5 +203.9 +200.9 +210.0 +207.1 +217.2 +197.4 +199.2 +210.8 +209.2 +218.4 +200.2 +211.7 +213.6 +203.3 +197.9 +203.0 +204.2 +207.9 +209.4 +225.4 +237.3 +209.5 +208.2 +207.5 +207.0 +203.0 +219.3 +228.3 +213.5 +205.1 +198.9 +212.7 +201.5 +210.0 +206.5 +203.3 +206.1 +210.1 +219.7 +206.8 +215.4 +220.4 +217.3 +211.4 +206.0 +208.3 +207.3 +205.5 +210.8 +209.3 +197.2 +207.2 +191.7 +204.2 +207.2 +216.1 +209.1 +203.8 +201.8 +208.7 +212.4 +214.5 +213.8 +201.3 +219.7 +214.8 +211.9 +223.8 +208.6 +203.5 +207.4 +207.0 +198.0 +208.2 +218.6 +205.1 +214.6 +215.2 +215.3 +204.3 +210.1 +221.9 +210.7 +198.2 +205.2 +201.1 +219.0 +207.2 +205.9 +203.8 +200.5 +217.5 +208.7 +208.4 +192.6 +211.0 +209.1 +206.5 +197.4 +202.1 +210.0 +198.3 +222.2 +211.9 +212.3 +222.2 +195.1 +200.7 +212.1 +208.3 +211.8 +211.7 +206.5 +211.8 +207.6 +214.2 +207.7 +204.7 +208.2 +208.4 +207.9 +212.1 +223.2 +206.3 +205.6 +201.8 +211.9 +207.6 +203.0 +221.2 +206.3 +222.4 +253.5 +204.4 +218.9 +211.9 +210.9 +214.0 +226.7 +214.4 +199.7 +213.8 +207.0 +201.8 +206.6 +203.1 +202.1 +203.6 +213.9 +196.9 +200.4 +204.6 +333.4 +204.5 +220.9 +207.3 +212.1 +203.7 +200.9 +198.2 +204.0 +201.4 +198.2 +209.6 +211.5 +201.2 +200.4 +207.4 +200.7 +213.8 +207.7 +188.0 +210.0 +210.5 +207.3 +198.6 +206.1 +186.9 +201.4 +204.0 +200.8 +207.8 +211.7 +198.7 +206.1 +213.0 +214.8 +212.8 +208.8 +210.4 +206.5 +210.1 +201.7 +202.7 +201.3 +194.1 +200.8 +196.8 +204.2 +217.5 +209.0 +198.7 +203.2 +213.8 +198.0 +207.1 +204.0 +215.3 +199.5 +214.1 +200.1 +206.9 +219.9 +204.8 +208.6 +207.8 +207.5 +203.8 +210.9 +210.6 +205.3 +202.1 +212.9 +214.8 +210.9 +217.2 +218.3 +221.5 +201.8 +212.7 +215.0 +206.7 +222.8 +210.9 +211.5 +202.0 +208.1 +268.9 +205.8 +204.0 +198.4 +206.3 +209.3 +206.4 +207.4 +226.9 +209.9 +199.6 +206.5 +210.9 +224.1 +211.9 +214.4 +212.2 +211.5 +209.4 +205.3 +204.8 +207.7 +208.9 +213.7 +201.0 +217.4 +198.1 +219.0 +206.5 +229.1 +220.1 +196.8 +203.1 +208.8 +201.7 +195.7 +207.0 +202.4 +206.6 +204.9 +196.6 +204.3 +198.6 +203.9 +215.8 +194.9 +202.7 +225.5 +205.9 +201.4 +213.1 +214.2 +218.8 +209.4 +204.4 +206.7 +209.8 +198.4 +211.8 +212.1 +209.1 +202.3 +213.7 +215.5 +218.3 +209.1 +216.6 +214.8 +206.4 +205.6 +214.4 +209.2 +211.7 +211.3 +211.0 +205.6 +204.2 +191.7 +213.8 +204.9 +205.3 +212.0 +199.9 +198.3 +211.8 +203.0 +212.2 +203.0 +201.8 +214.4 +214.1 +199.6 +205.3 +208.2 +196.7 +196.7 +209.1 +205.1 +212.5 +213.1 +197.3 +208.8 +218.0 +220.0 +198.4 +206.3 +206.9 +253.2 +194.3 +202.6 +210.6 +219.1 +197.8 +197.1 +194.0 +211.6 +209.6 +198.3 +213.0 +207.7 +207.0 +213.3 +206.9 +197.6 +204.8 +202.0 +200.0 +215.2 +204.5 +206.3 +206.7 +203.2 +194.9 +206.3 +209.9 +210.6 +214.2 +208.6 +207.4 +213.9 +210.4 +210.0 +200.6 +203.8 +202.7 +204.2 +202.7 +210.2 +192.5 +215.4 +211.7 +208.3 +204.8 +203.3 +197.7 +216.7 +200.9 +203.6 +208.6 +206.5 +209.9 +200.1 +198.4 +203.3 +210.4 +211.6 +202.0 +203.1 +204.0 +204.0 +215.0 +211.4 +202.0 +197.2 +197.6 +209.9 +205.4 +213.1 +199.1 +212.4 +216.1 +218.3 +214.6 +224.1 +206.9 +199.4 +213.4 +261.2 +199.4 +208.8 +209.9 +205.7 +203.1 +203.2 +204.6 +201.6 +210.6 +213.2 +214.8 +203.8 +204.9 +220.7 +201.5 +212.5 +216.8 +209.7 +203.1 +213.3 +204.7 +218.2 +215.5 +215.6 +211.6 +214.2 +205.1 +216.6 +216.3 +203.5 +200.8 +213.7 +221.9 +215.0 +210.2 +217.1 +214.7 +208.8 +217.4 +231.1 +213.7 +215.0 +213.5 +216.7 +207.7 +201.0 +210.4 +210.9 +206.7 +203.7 +199.2 +209.3 +206.3 +202.4 +210.1 +212.3 +202.2 +207.2 +197.8 +205.9 +202.0 +214.2 +203.5 +204.4 +200.0 +204.0 +193.8 +192.3 +229.0 +204.5 +194.8 +213.6 +215.9 +214.8 +221.6 +208.5 +201.5 +204.4 +206.4 +194.5 +199.4 +201.5 +209.7 +212.5 +202.1 +208.2 +205.4 +204.5 +199.4 +194.5 +199.6 +201.5 +206.2 +219.9 +198.5 +216.2 +195.7 +205.0 +208.0 +204.9 +195.9 +207.4 +216.9 +195.9 +204.4 +208.3 +206.1 +188.5 +202.3 +201.7 +200.5 +206.2 +191.5 +218.6 +206.5 +208.9 +209.9 +201.5 +212.7 +203.2 +209.7 +212.1 +208.4 +207.2 +206.5 +204.5 +222.7 +207.6 +207.4 +210.3 +212.2 +219.1 +215.2 +211.1 +205.9 +205.5 +205.9 +203.1 +205.4 +184.5 +205.0 +194.8 +213.5 +209.8 +195.4 +202.9 +205.3 +196.3 +202.0 +198.2 +201.5 +195.3 +230.9 +207.8 +212.6 +202.7 +204.8 +205.0 +202.8 +206.2 +200.2 +202.7 +203.5 +205.5 +196.9 +209.4 +212.1 +200.8 +205.0 +208.0 +207.1 +198.0 +204.8 +205.8 +200.9 +202.1 +202.4 +206.9 +209.1 +199.7 +197.1 +206.9 +200.2 +193.7 +195.0 +250.8 +207.5 +204.5 +208.8 +209.8 +194.8 +200.2 +205.1 +197.3 +208.3 +200.4 +204.7 +211.1 +203.4 +218.2 +194.6 +201.5 +202.2 +202.9 +198.8 +218.2 +201.7 +189.8 +210.1 +208.0 +204.3 +205.8 +204.2 +207.8 +200.2 +197.9 +198.9 +208.1 +202.4 +196.2 +195.5 +204.6 +211.0 +205.0 +193.6 +197.2 +198.6 +193.8 +198.9 +232.4 +201.8 +212.2 +208.6 +204.5 +199.3 +211.2 +203.1 +209.7 +214.3 +203.9 +200.3 +203.3 +206.1 +206.9 +209.1 +209.1 +199.3 +199.4 +198.8 +198.9 +199.9 +193.7 +204.6 +203.4 +199.7 +212.6 +200.7 +208.1 +198.8 +200.5 +209.2 +208.4 +205.7 +197.1 +202.6 +199.5 +208.4 +200.1 +204.9 +202.9 +201.5 +207.6 +200.6 +204.2 +210.0 +207.1 +205.1 +198.5 +204.9 +196.5 +208.0 +202.4 +202.7 +196.2 +206.9 +201.5 +203.3 +198.7 +211.9 +208.4 +206.7 +209.4 +204.0 +202.3 +205.0 +205.3 +206.0 +213.1 +205.7 +199.3 +206.2 +204.6 +209.3 +205.7 +202.7 +213.3 +202.3 +197.8 +196.5 +193.4 +211.6 +209.9 +195.5 +196.2 +210.2 +207.1 +207.0 +221.8 +217.2 +215.4 +207.0 +200.1 +207.5 +206.0 +200.7 +190.9 +209.8 +213.5 +206.3 +196.0 +213.1 +202.7 +211.6 +196.5 +209.9 +212.3 +199.9 +206.8 +225.1 +203.9 +204.3 +197.7 +203.5 +203.2 +193.5 +200.9 +201.4 +189.1 +203.9 +194.5 +205.4 +204.8 +204.9 +201.3 +208.4 +196.9 +206.8 +207.7 +201.6 +210.3 +211.6 +209.8 +200.2 +205.2 +197.6 +195.9 +212.8 +206.4 +201.0 +208.2 +207.5 +202.5 +193.3 +206.5 +221.2 +198.8 +216.6 +217.0 +209.1 +206.6 +197.7 +211.0 +199.9 +198.0 +210.4 +200.5 +211.7 +219.6 +206.8 +207.2 +210.6 +205.4 +203.8 +207.4 +206.2 +205.1 +208.7 +196.3 +204.7 +210.8 +214.4 +196.3 +206.5 +210.8 +193.2 +203.3 +203.9 +207.7 +194.9 +203.7 +195.5 +218.7 +201.1 +199.5 +207.6 +209.3 +207.5 +205.7 +203.9 +205.4 +201.3 +205.8 +205.4 +208.8 +214.3 +203.4 +207.5 +188.9 +205.5 +200.7 +212.5 +197.9 +219.0 +213.6 +197.3 +202.7 +216.3 +205.0 +210.2 +203.2 +203.9 +206.8 +213.6 +200.1 +204.4 +211.4 +213.4 +200.2 +208.4 +209.1 +198.8 +207.4 +195.0 +205.6 +200.5 +204.3 +201.9 +206.4 +199.0 +196.1 +207.6 +195.4 +197.2 +200.7 +190.8 +211.9 +191.5 +201.4 +193.5 +205.1 +206.8 +199.5 +207.4 +209.8 +199.1 +194.6 +201.6 +211.6 +206.8 +203.9 +196.8 +206.3 +210.1 +200.6 +227.4 +201.9 +210.8 +205.8 +217.2 +205.8 +196.1 +200.7 +213.8 +205.4 +211.6 +212.3 +213.6 +201.7 +199.9 +203.2 +212.6 +211.0 +208.1 +198.1 +201.7 +211.6 +207.4 +212.4 +207.3 +214.9 +214.5 +214.5 +202.7 +200.1 +206.4 +213.4 +189.7 +203.4 +202.2 +198.2 +206.5 +213.7 +207.6 +202.8 +209.2 +205.5 +196.4 +207.6 +207.4 +207.3 +188.8 +215.6 +195.4 +207.7 +208.2 +200.9 +208.4 +203.3 +210.8 +199.6 +208.3 +206.7 +201.6 +202.9 +197.5 +206.4 +209.0 +208.4 +211.6 +204.4 +210.0 +190.9 +199.3 +207.6 +202.5 +197.0 +200.8 +203.1 +204.0 +199.0 +208.0 +204.6 +196.6 +200.8 +205.2 +198.8 +203.0 +208.3 +200.1 +205.5 +203.7 +202.2 +203.8 +211.5 +201.8 +213.2 +207.4 +207.8 +202.2 +208.2 +204.2 +200.4 +186.1 +188.5 +220.4 +212.8 +193.3 +196.9 +203.0 +207.3 +202.4 +201.7 +204.8 +192.2 +218.7 +226.3 +209.5 +201.4 +207.3 +202.6 +210.7 +208.4 +208.4 +207.4 +210.4 +191.2 +203.6 +197.1 +207.5 +197.8 +206.2 +214.5 +208.2 +207.3 +204.7 +199.6 +206.3 +189.0 +214.4 +209.4 +208.1 +199.9 +190.5 +223.0 +198.8 +201.1 +192.4 +204.0 +209.0 +206.7 +204.3 +198.7 +210.9 +212.0 +204.8 +204.2 +199.5 +203.5 +203.0 +190.6 +207.9 +207.9 +193.2 +210.9 +200.1 +207.6 +193.6 +204.9 +197.7 +200.9 +213.0 +215.0 +204.4 +196.6 +209.6 +209.9 +199.8 +198.8 +202.1 +203.4 +205.4 +204.4 +196.2 +190.7 +210.9 +197.7 +194.7 +204.0 +201.5 +195.3 +209.0 +203.6 +196.1 +205.2 +206.7 +206.6 +191.4 +193.4 +206.6 +205.9 +207.9 +201.7 +213.3 +199.4 +202.8 +196.1 +208.3 +206.4 +205.2 +191.9 +207.3 +191.5 +210.8 +200.9 +210.4 +208.3 +211.0 +202.7 +198.8 +196.8 +202.7 +196.9 +214.6 +210.2 +226.1 +220.8 +213.5 +194.9 +210.4 +203.7 +203.7 +180.8 +213.7 +208.0 +209.8 +209.7 +213.8 +185.5 +208.5 +203.5 +212.8 +193.1 +199.2 +211.0 +217.4 +211.0 +202.7 +205.0 +208.6 +197.5 +197.1 +201.0 +195.9 +208.4 +205.7 +205.8 +194.0 +204.4 +194.5 +194.3 +200.1 +209.5 +218.0 +202.8 +197.5 +206.7 +199.8 +205.2 +201.4 +205.2 +186.0 +208.4 +218.4 +206.7 +201.9 +209.7 +208.0 +203.9 +193.1 +202.0 +198.0 +199.5 +211.0 +191.8 +198.7 +197.3 +195.6 +202.9 +203.4 +206.1 +205.6 +207.5 +220.8 +204.7 +207.7 +252.5 +203.9 +203.2 +201.3 +200.1 +201.1 +196.8 +197.6 +206.4 +209.6 +197.9 +199.4 +212.6 +205.4 +200.9 +197.5 +202.2 +199.5 +206.7 +215.1 +216.4 +221.9 +199.2 +246.4 +196.0 +205.1 +205.4 +207.8 +192.6 +204.6 +209.2 +213.4 +198.9 +205.3 +205.8 +201.1 +195.2 +199.4 +200.8 +210.5 +202.3 +217.9 +208.4 +220.8 +218.4 +195.7 +199.4 +198.8 +192.0 +210.9 +218.5 +194.5 +203.6 +195.0 +208.8 +197.4 +204.1 +200.7 +201.0 +206.6 +202.2 +208.7 +213.1 +198.3 +212.2 +201.9 +206.3 +203.4 +198.0 +198.0 +205.3 +199.6 +196.6 +202.8 +201.7 +208.7 +195.6 +199.4 +205.4 +205.2 +202.2 +193.3 +191.9 +195.1 +201.1 +210.5 +208.7 +196.9 +193.4 +200.8 +199.6 +204.1 +200.4 +197.6 +204.1 +206.9 +205.2 +206.9 +194.7 +200.4 +198.8 +201.7 +201.8 +207.0 +193.2 +199.9 +201.3 +192.5 +197.9 +206.9 +190.0 +203.8 +208.8 +200.9 +203.3 +194.5 +192.6 +204.9 +205.5 +196.6 +194.8 +197.9 +198.1 +211.2 +198.8 +202.2 +205.9 +199.5 +204.7 +201.6 +201.2 +203.4 +204.2 +190.7 +206.7 +205.4 +208.4 +203.1 +204.2 +198.4 +194.3 +191.6 +198.9 +203.5 +198.7 +192.2 +198.4 +194.5 +181.1 +200.9 +200.0 +209.2 +210.4 +200.0 +201.1 +193.9 +207.0 +193.4 +202.6 +192.8 +196.0 +203.8 +184.2 +179.3 +202.3 +191.4 +199.7 +195.4 +189.9 +197.0 +187.5 +192.1 +198.3 +202.2 +205.0 +212.3 +198.0 +205.5 +210.1 +197.6 +198.7 +206.6 +203.4 +194.3 +181.2 +199.0 +202.4 +189.1 +181.6 +200.4 +188.1 +180.1 +203.1 +201.1 +195.5 +201.6 +201.3 +197.6 +196.0 +205.5 +184.9 +186.5 +190.8 +188.6 +207.2 +199.5 +198.6 +199.8 +212.2 +208.1 +196.9 +199.6 +205.3 +196.9 +188.9 +205.4 +212.5 +197.5 +201.8 +188.8 +187.1 +199.9 +195.4 +188.7 +198.7 +185.0 +191.6 +193.3 +191.8 +209.4 +197.4 +195.2 +189.4 +189.7 +199.9 +199.3 +188.7 +188.3 +190.9 +181.6 +209.8 +194.6 +198.2 +199.9 +198.1 +186.8 +195.3 +190.9 +198.8 +189.3 +207.5 +179.2 +188.8 +185.6 +206.2 +184.8 +190.7 +203.5 +199.2 +202.0 +197.6 +197.2 +196.4 +210.4 +200.1 +194.8 +186.7 +198.2 +197.8 +186.5 +200.2 +192.7 +192.7 +190.4 +220.9 +207.5 +188.6 +198.5 +203.0 +202.2 +189.6 +177.3 +194.8 +195.2 +243.9 +196.5 +180.6 +214.6 +196.4 +220.6 +194.7 +200.5 +193.7 +199.7 +203.0 +201.4 +187.7 +199.8 +191.8 +203.9 +203.8 +191.3 +206.6 +201.7 +202.1 +202.6 +200.0 +203.6 +195.9 +204.8 +212.8 +199.2 +203.3 +206.6 +192.2 +205.0 +198.9 +205.3 +195.0 +198.1 +190.4 +203.7 +188.2 +204.2 +211.1 +192.5 +194.5 +198.3 +205.7 +198.5 +210.2 +206.8 +195.4 +200.8 +202.7 +220.0 +204.1 +209.5 +200.2 +187.1 +205.4 +202.6 +203.3 +214.1 +193.8 +207.4 +208.2 +204.9 +215.9 +202.6 +198.0 +193.8 +198.2 +206.2 +203.9 +190.6 +210.8 +195.6 +207.6 +206.6 +195.4 +189.9 +199.7 +203.1 +207.1 +192.2 +197.3 +197.6 +193.3 +207.9 +201.3 +206.8 +201.9 +195.7 +204.1 +201.1 +192.5 +206.7 +213.1 +195.2 +205.1 +196.2 +203.5 +195.5 +200.3 +194.7 +194.5 +200.6 +211.2 +202.1 +194.6 +199.9 +212.6 +206.8 +196.2 +205.8 +202.8 +201.6 +205.2 +205.8 +193.1 +202.0 +196.2 +208.1 +209.5 +199.8 +208.8 +192.3 +207.9 +201.3 +205.7 +205.9 +208.6 +210.3 +202.2 +212.1 +210.3 +199.6 +200.8 +209.1 +202.5 +215.0 +201.5 +209.2 +207.0 +215.3 +205.6 +213.7 +203.7 +199.8 +201.4 +194.7 +194.3 +188.7 +200.9 +203.8 +203.2 +212.5 +207.0 +211.3 +204.3 +204.5 +194.0 +210.7 +207.1 +207.5 +200.7 +200.8 +200.7 +200.1 +203.7 +191.1 +201.8 +194.8 +195.2 +197.4 +190.5 +192.7 +206.6 +200.8 +204.3 +206.5 +209.8 +202.5 +207.6 +198.4 +203.3 +202.1 +200.6 +198.4 +191.0 +203.5 +198.6 +184.3 +183.7 +189.1 +205.4 +187.8 +194.5 +199.2 +196.4 +210.9 +176.8 +191.6 +182.7 +181.3 +205.7 +203.2 +186.3 +187.6 +189.1 +180.8 +180.2 +187.6 +194.9 +192.8 +185.2 +198.3 +209.3 +177.5 +193.9 +193.1 +203.4 +192.1 +200.9 +182.6 +204.9 +197.6 +212.8 +206.9 +193.3 +201.0 +195.3 +197.1 +189.6 +198.5 +190.4 +188.8 +197.7 +189.9 +200.7 +196.8 +186.3 +181.5 +184.9 +200.2 +198.7 +205.8 +200.2 +198.3 +207.9 +206.1 +201.5 +197.8 +199.5 +198.1 +211.3 +201.6 +202.4 +196.0 +197.7 +209.2 +199.3 +205.5 +191.6 +206.4 +196.5 +209.5 +203.4 +201.4 +200.1 +205.2 +190.9 +205.1 +197.5 +196.1 +194.4 +194.7 +188.9 +180.8 +206.5 +199.8 +193.1 +195.2 +192.7 +199.2 +199.5 +188.1 +180.2 +191.0 +206.9 +208.2 +202.5 +200.0 +207.0 +201.6 +195.6 +195.6 +195.0 +196.3 +190.2 +194.0 +182.9 +192.1 +206.5 +181.4 +192.3 +199.6 +201.6 +192.4 +200.5 +207.1 +198.5 +198.8 +190.8 +200.3 +199.3 +200.5 +187.7 +208.3 +205.6 +189.3 +198.4 +204.9 +197.4 +198.7 +190.6 +214.5 +212.5 +207.6 +196.9 +183.4 +185.1 +205.8 +226.5 +202.5 +201.8 +202.9 +210.4 +189.7 +195.6 +198.7 +193.0 +198.8 +193.1 +202.5 +195.2 +195.0 +198.3 +203.6 +208.3 +195.9 +200.8 +189.4 +207.9 +182.8 +194.9 +199.7 +180.7 +187.2 +189.5 +196.1 +190.1 +192.5 +185.7 +212.2 +204.2 +191.9 +184.5 +182.5 +198.5 +191.0 +192.0 +195.6 +201.1 +193.7 +203.8 +200.5 +199.1 +190.3 +209.5 +195.0 +184.0 +193.6 +203.3 +191.4 +194.9 +195.5 +193.5 +182.7 +189.7 +196.1 +178.9 +199.5 +195.3 +185.9 +199.1 +210.0 +195.7 +193.8 +196.4 +195.3 +201.4 +209.5 +205.6 +197.5 +188.9 +193.8 +185.3 +193.3 +198.1 +201.4 +184.7 +182.5 +183.7 +185.5 +199.8 +200.3 +194.1 +176.9 +192.2 +200.0 +186.4 +191.6 +200.1 +202.3 +205.1 +186.4 +182.3 +194.7 +177.5 +201.4 +189.6 +195.5 +185.4 +194.8 +204.1 +188.0 +182.1 +181.7 +184.5 +234.2 +209.4 +193.3 +204.0 +184.7 +194.3 +193.4 +191.1 +188.3 +193.9 +198.2 +202.8 +198.1 +191.2 +200.9 +205.4 +203.6 +193.8 +215.8 +185.8 +195.4 +204.7 +190.3 +190.7 +177.7 +182.1 +193.2 +178.3 +199.3 +203.5 +187.3 +198.8 +187.8 +187.7 +186.7 +200.0 +190.0 +203.1 +181.7 +207.2 +183.8 +180.3 +193.5 +190.2 +193.7 +198.6 +195.6 +192.1 +200.5 +188.6 +190.9 +188.0 +192.8 +191.4 +179.9 +197.6 +200.6 +206.1 +201.3 +199.6 +198.8 +201.0 +180.2 +202.9 +197.3 +186.1 +200.5 +182.4 +192.7 +194.5 +182.8 +193.9 +195.1 +187.7 +201.0 +196.1 +194.0 +198.8 +192.8 +186.1 +200.6 +186.3 +187.6 +178.0 +175.8 +198.9 +199.3 +193.4 +193.3 +198.7 +194.5 +180.9 +197.3 +189.7 +193.0 +208.2 +200.1 +193.9 +211.2 +206.6 +210.2 +185.5 +180.8 +206.8 +185.5 +195.8 +199.3 +187.9 +194.9 +175.8 +198.1 +199.0 +200.6 +300.8 +194.0 +199.7 +181.2 +189.9 +195.3 +209.6 +198.1 +184.9 +192.5 +188.8 +193.8 +201.4 +208.2 +192.5 +199.9 +185.0 +207.5 +196.5 +198.8 +193.3 +200.1 +186.7 +194.4 +194.3 +197.2 +198.4 +192.8 +194.3 +188.6 +194.7 +190.7 +192.1 +194.5 +185.7 +194.6 +177.5 +203.6 +180.8 +185.0 +178.9 +205.7 +187.4 +185.9 +192.9 +182.7 +197.3 +198.0 +194.5 +194.7 +194.7 +198.2 +184.7 +199.0 +200.9 +195.4 +198.7 +188.1 +187.5 +190.6 +179.2 +190.2 +195.9 +188.8 +205.7 +191.9 +204.0 +193.3 +199.5 +200.7 +179.3 +190.4 +206.4 +199.8 +189.5 +194.1 +203.3 +196.9 +200.1 +179.6 +217.2 +199.0 +184.0 +177.4 +200.5 +205.3 +193.2 +198.8 +187.2 +191.2 +186.6 +188.3 +199.4 +192.8 +209.8 +181.5 +192.8 +176.0 +189.9 +203.5 +192.5 +193.1 +190.3 +193.1 +203.0 +194.6 +188.4 +199.8 +199.6 +195.0 +200.3 +195.5 +198.5 +203.1 +193.4 +203.6 +195.6 +186.2 +206.4 +197.3 +265.4 +203.7 +205.7 +197.0 +194.9 +193.6 +201.1 +200.6 +197.1 +196.0 +196.3 +195.4 +194.2 +198.4 +202.6 +197.1 +209.4 +204.7 +195.9 +192.8 +203.4 +193.3 +188.5 +190.7 +190.3 +197.6 +197.7 +199.1 +193.0 +198.3 +205.0 +191.4 +197.2 +201.3 +197.6 +197.7 +202.9 +203.4 +198.5 +198.8 +205.4 +194.6 +189.5 +193.3 +190.4 +193.0 +202.5 +198.2 +194.7 +198.5 +184.3 +187.8 +193.3 +190.8 +194.9 +190.3 +201.8 +192.9 +198.5 +195.9 +195.8 +210.1 +194.2 +202.6 +194.5 +197.6 +200.1 +191.8 +192.8 +199.4 +199.7 +199.3 +194.2 +196.9 +195.7 +189.9 +197.1 +205.9 +191.1 +196.5 +200.9 +200.6 +199.1 +203.0 +204.2 +198.7 +192.2 +194.9 +188.6 +194.3 +198.5 +190.6 +189.4 +205.8 +207.0 +200.9 +198.0 +196.9 +196.6 +187.3 +199.9 +196.1 +196.5 +200.0 +186.0 +182.7 +193.3 +195.2 +190.0 +195.9 +190.0 +201.7 +187.1 +199.1 +203.5 +191.8 +199.5 +195.1 +207.3 +205.6 +191.9 +200.1 +196.5 +205.4 +190.8 +195.9 +194.6 +190.2 +197.4 +204.4 +210.5 +289.6 +197.2 +191.4 +199.2 +196.6 +201.4 +191.7 +194.4 +191.9 +193.1 +182.9 +191.5 +202.0 +186.8 +195.5 +193.4 +189.8 +181.3 +199.9 +200.3 +193.5 +196.5 +190.5 +200.6 +209.8 +197.7 +199.5 +200.9 +205.0 +199.4 +206.3 +205.7 +202.7 +189.0 +203.6 +198.9 +188.4 +193.7 +204.1 +198.4 +208.8 +201.4 +198.0 +188.7 +208.9 +196.9 +235.5 +198.1 +202.8 +195.8 +193.2 +203.0 +204.2 +201.2 +201.4 +202.8 +213.8 +197.2 +197.0 +191.2 +196.2 +210.8 +203.5 +193.4 +211.7 +194.0 +204.6 +197.0 +200.0 +197.9 +204.2 +196.6 +184.4 +188.6 +194.4 +188.1 +202.4 +203.0 +204.4 +194.1 +195.8 +195.3 +201.9 +194.7 +205.8 +201.9 +208.6 +195.5 +209.5 +194.8 +202.7 +202.2 +204.4 +197.1 +205.9 +196.2 +197.1 +197.4 +198.6 +198.8 +201.1 +203.9 +200.3 +199.5 +224.5 +199.2 +196.2 +197.7 +194.7 +194.1 +202.5 +191.2 +203.1 +199.1 +197.5 +201.0 +198.7 +207.9 +191.7 +192.1 +191.7 +208.7 +178.3 +202.0 +200.4 +202.1 +206.8 +194.2 +197.0 +203.3 +195.1 +210.0 +193.9 +191.1 +200.1 +192.9 +202.3 +189.5 +193.9 +200.8 +205.5 +198.7 +205.4 +184.7 +198.6 +189.7 +187.8 +202.5 +196.2 +203.5 +213.4 +199.7 +207.5 +207.3 +204.7 +190.5 +194.9 +184.7 +198.0 +200.9 +189.8 +208.9 +189.5 +218.6 +202.8 +189.0 +202.2 +204.3 +191.5 +193.6 +201.6 +204.6 +197.0 +200.9 +186.0 +205.9 +194.1 +203.3 +197.3 +200.3 +195.9 +207.3 +206.7 +206.7 +193.0 +203.1 +238.1 +192.2 +193.3 +197.4 +212.3 +202.5 +197.5 +204.1 +196.6 +183.8 +204.5 +188.1 +217.6 +194.5 +199.1 +210.9 +200.9 +187.7 +199.2 +195.0 +191.0 +198.3 +194.1 +191.9 +213.8 +199.0 +201.8 +197.5 +201.5 +195.6 +207.5 +200.6 +194.2 +210.5 +192.7 +188.1 +203.0 +237.1 +204.7 +205.1 +205.2 +200.3 +188.5 +202.2 +213.1 +195.0 +201.4 +204.2 +195.9 +186.5 +192.2 +206.5 +177.5 +189.9 +192.0 +214.9 +204.0 +194.2 +200.9 +197.1 +200.0 +196.4 +197.2 +189.0 +194.3 +206.5 +192.5 +190.5 +204.1 +196.7 +194.4 +181.9 +193.0 +190.6 +193.6 +178.0 +178.2 +200.9 +189.5 +194.2 +182.1 +183.6 +183.7 +176.9 +181.7 +194.9 +190.7 +187.4 +178.6 +182.0 +186.5 +183.7 +182.1 +186.2 +199.6 +192.4 +189.2 +194.7 +176.3 +184.6 +203.2 +201.9 +195.2 +192.7 +186.7 +195.2 +187.0 +201.1 +202.1 +187.7 +195.9 +181.7 +189.7 +179.9 +177.3 +180.3 +198.3 +184.6 +183.3 +196.9 +178.6 +184.3 +185.3 +183.2 +193.9 +194.7 +195.5 +199.6 +192.0 +189.4 +195.0 +193.6 +200.5 +177.6 +181.0 +200.0 +190.3 +189.7 +205.5 +178.5 +201.7 +192.7 +196.8 +189.2 +177.6 +198.0 +191.8 +178.6 +206.8 +190.0 +192.3 +180.1 +194.6 +179.7 +207.1 +195.6 +200.5 +186.7 +190.1 +178.6 +205.7 +346.2 +188.8 +204.4 +200.7 +176.5 +193.8 +195.9 +193.0 +186.5 +189.5 +190.6 +178.0 +188.6 +186.7 +180.9 +193.8 +194.0 +180.6 +196.7 +178.7 +180.1 +187.7 +179.6 +201.3 +219.2 +184.0 +206.3 +186.7 +192.0 +179.4 +190.1 +187.0 +191.6 +194.7 +195.5 +194.2 +195.8 +200.6 +180.3 +195.6 +209.9 +197.2 +201.1 +196.9 +186.3 +202.7 +182.7 +200.4 +201.2 +196.2 +181.1 +182.6 +187.2 +225.3 +186.8 +197.6 +192.0 +185.0 +199.8 +191.7 +187.4 +192.5 +189.1 +210.9 +187.0 +191.2 +190.9 +207.1 +198.7 +208.8 +190.6 +193.7 +186.5 +182.9 +178.7 +194.1 +184.3 +194.0 +185.4 +215.7 +194.6 +207.9 +204.7 +183.7 +189.3 +196.0 +202.6 +206.2 +190.1 +216.6 +179.8 +206.9 +188.4 +190.8 +181.7 +197.7 +195.7 +178.3 +179.0 +179.3 +203.2 +178.4 +180.1 +175.7 +194.1 +193.0 +203.2 +192.6 +194.0 +190.7 +193.0 +194.2 +183.7 +197.2 +188.4 +176.1 +184.3 +192.2 +195.7 +186.0 +193.7 +196.2 +183.7 +180.7 +181.0 +189.4 +199.3 +201.5 +199.8 +197.2 +201.7 +199.9 +192.2 +188.2 +187.5 +176.6 +205.3 +199.0 +190.0 +202.4 +201.8 +216.8 +208.2 +197.4 +194.3 +205.9 +179.0 +193.5 +182.9 +188.0 +198.3 +197.7 +177.6 +183.8 +182.3 +198.8 +192.5 +183.2 +208.4 +192.4 +193.8 +198.6 +196.2 +183.9 +186.2 +197.8 +180.6 +204.7 +195.3 +182.8 +192.5 +216.3 +203.8 +197.4 +179.5 +188.5 +196.8 +194.4 +189.2 +199.8 +200.7 +205.1 +183.4 +181.5 +193.6 +207.9 +177.4 +191.5 +197.3 +189.8 +191.9 +189.5 +201.2 +211.0 +201.4 +205.5 +201.1 +199.3 +193.5 +203.5 +191.6 +194.0 +196.7 +188.5 +189.8 +195.9 +181.1 +193.2 +197.2 +201.0 +186.2 +200.9 +183.5 +183.0 +207.9 +189.7 +193.8 +182.9 +232.1 +186.2 +200.3 +194.6 +184.2 +192.2 +182.9 +193.6 +207.4 +203.3 +185.6 +197.6 +205.9 +193.0 +182.3 +190.1 +193.3 +200.2 +183.9 +195.7 +186.9 +200.1 +189.6 +194.2 +195.5 +178.3 +183.9 +183.0 +179.2 +209.8 +199.6 +185.4 +210.9 +188.6 +188.9 +185.4 +177.0 +183.0 +186.1 +191.7 +190.7 +192.1 +195.2 +185.0 +186.6 +194.2 +189.5 +187.7 +187.9 +191.8 +181.1 +180.2 +180.0 +185.6 +181.8 +187.9 +192.9 +198.7 +182.0 +182.7 +175.8 +202.0 +190.6 +195.3 +191.0 +201.4 +194.3 +179.5 +185.0 +240.3 +187.1 +197.8 +204.8 +210.7 +203.2 +202.0 +197.8 +203.5 +205.1 +190.0 +190.3 +204.1 +197.9 +198.6 +199.3 +182.1 +200.8 +196.0 +197.1 +203.5 +198.6 +200.7 +201.1 +189.1 +175.8 +199.9 +203.7 +192.2 +182.5 +185.0 +195.9 +204.1 +197.0 +195.5 +202.0 +201.5 +187.1 +186.7 +198.2 +211.8 +197.6 +178.5 +198.5 +196.4 +188.1 +185.0 +199.5 +200.9 +201.7 +193.5 +188.3 +180.9 +190.6 +202.8 +183.9 +190.1 +205.3 +198.6 +183.2 +198.0 +200.9 +198.8 +194.8 +198.3 +195.8 +204.6 +202.6 +207.0 +185.9 +201.9 +195.9 +207.6 +197.4 +206.7 +188.2 +184.7 +183.9 +198.3 +186.4 +191.0 +208.8 +209.4 +187.7 +208.0 +198.4 +191.9 +187.1 +188.5 +189.2 +190.7 +179.9 +204.1 +195.5 +183.7 +183.1 +183.7 +194.6 +187.9 +183.5 +184.7 +203.2 +197.8 +179.4 +185.8 +205.3 +179.8 +194.8 +186.7 +191.5 +197.4 +197.0 +209.2 +194.1 +187.8 +192.9 +202.2 +194.4 +206.6 +191.6 +190.8 +187.5 +193.5 +205.2 +185.1 +185.6 +189.9 +196.0 +203.6 +195.4 +209.9 +196.5 +200.8 +190.1 +191.5 +232.8 +209.2 +193.1 +183.4 +199.5 +187.2 +199.4 +203.6 +198.3 +204.1 +196.4 +202.0 +180.6 +191.6 +202.9 +184.9 +180.8 +192.3 +205.2 +205.5 +179.0 +183.3 +203.1 +184.1 +188.6 +180.9 +195.8 +195.0 +181.1 +198.3 +190.7 +191.8 +192.1 +191.5 +257.4 +197.3 +206.5 +213.4 +184.2 +193.5 +208.6 +209.8 +192.0 +201.5 +198.6 +205.2 +198.9 +212.6 +195.9 +213.7 +215.9 +209.2 +215.2 +207.4 +208.9 +192.2 +192.3 +193.8 +185.1 +206.0 +179.1 +199.9 +198.3 +201.6 +186.8 +202.3 +190.4 +183.6 +191.8 +190.6 +194.7 +184.1 +184.4 +193.8 +194.0 +195.6 +204.5 +194.9 +196.1 +201.1 +196.1 +210.9 +198.7 +192.1 +208.3 +192.8 +193.6 +205.1 +191.5 +190.7 +179.2 +191.6 +192.1 +182.3 +176.1 +185.5 +187.1 +183.4 +191.9 +176.1 +178.0 +185.2 +191.3 +181.3 +178.1 +181.7 +200.3 +187.5 +201.6 +192.3 +177.7 +180.6 +192.9 +185.4 +183.1 +178.4 +196.9 +191.9 +186.1 +195.7 +177.7 +184.1 +195.4 +184.2 +188.8 +179.6 +182.0 +182.9 +185.1 +183.8 +189.3 +216.9 +196.6 +185.7 +235.5 +184.9 +181.8 +180.0 +186.6 +188.4 +190.7 +185.4 +210.9 +184.1 +203.0 +203.5 +198.9 +204.2 +199.7 +182.1 +178.1 +205.1 +179.4 +184.5 +180.7 +200.5 +197.3 +178.2 +178.8 +180.9 +219.2 +180.2 +192.5 +198.6 +238.0 +201.1 +182.4 +203.3 +182.0 +188.9 +201.2 +184.6 +182.7 +187.7 +188.5 +202.5 +199.8 +189.4 +191.1 +181.2 +191.7 +194.6 +181.3 +185.3 +192.8 +182.4 +191.6 +188.3 +202.6 +212.1 +179.6 +185.9 +183.4 +187.8 +184.4 +186.8 +197.0 +191.3 +186.7 +201.9 +187.2 +195.7 +178.5 +187.3 +190.8 +198.4 +198.8 +189.1 +189.6 +197.6 +201.0 +185.0 +182.2 +184.2 +193.2 +191.6 +187.4 +196.3 +190.7 +184.5 +206.1 +200.7 +193.0 +196.2 +195.1 +177.1 +180.7 +187.3 +188.3 +181.5 +180.7 +216.9 +185.7 +196.1 +193.2 +185.5 +186.9 +190.4 +189.7 +196.1 +193.9 +193.6 +185.7 +190.3 +199.8 +190.4 +187.4 +195.5 +190.0 +188.7 +190.2 +195.6 +195.2 +184.7 +186.9 +187.4 +179.3 +184.7 +206.7 +194.3 +198.9 +179.0 +185.7 +185.2 +206.7 +184.3 +202.8 +183.5 +178.9 +238.3 +181.1 +189.5 +176.7 +178.7 +183.4 +180.3 +192.4 +193.6 +186.6 +190.0 +184.0 +188.5 +188.6 +196.3 +182.2 +191.9 +191.1 +197.7 +188.4 +194.2 +201.7 +204.3 +199.6 +190.7 +183.0 +189.9 +192.9 +187.6 +182.8 +203.3 +189.3 +194.0 +189.4 +188.0 +194.4 +211.7 +186.3 +185.3 +187.2 +182.9 +194.9 +187.4 +190.4 +197.5 +184.5 +184.8 +180.0 +197.9 +205.9 +189.2 +196.5 +188.1 +185.4 +195.5 +185.0 +203.1 +188.6 +202.3 +180.6 +191.3 +204.4 +208.2 +189.2 +226.9 +200.0 +178.8 +181.5 +197.7 +204.1 +184.8 +178.5 +186.5 +190.9 +190.4 +181.4 +189.8 +179.2 +189.8 +179.6 +203.8 +205.4 +184.2 +192.8 +204.7 +186.6 +195.5 +203.7 +197.2 +178.0 +180.3 +187.0 +179.1 +187.5 +187.9 +183.4 +181.8 +184.1 +181.3 +184.0 +180.6 +230.8 +193.0 +190.5 +200.4 +187.9 +175.9 +204.2 +206.5 +183.2 +194.2 +187.2 +200.2 +189.6 +188.6 +188.6 +175.4 +184.2 +174.0 +177.2 +180.6 +181.5 +182.3 +187.1 +180.0 +184.7 +179.3 +195.7 +180.5 +179.5 +184.8 +204.0 +202.1 +178.7 +189.4 +196.4 +190.8 +184.3 +189.2 +182.8 +184.6 +178.4 +183.9 +185.0 +184.6 +179.2 +179.0 +182.4 +197.0 +188.4 +188.5 +196.0 +179.6 +188.1 +180.3 +196.1 +189.0 +178.4 +176.5 +186.4 +179.3 +187.7 +187.9 +184.8 +176.9 +188.7 +182.8 +192.3 +190.0 +197.2 +191.6 +197.8 +182.3 +184.2 +187.3 +188.2 +207.3 +189.2 +190.7 +202.3 +191.7 +195.4 +196.2 +190.1 +204.2 +194.8 +187.8 +209.1 +192.1 +193.1 +199.3 +192.1 +204.9 +190.8 +186.5 +189.3 +181.9 +193.3 +182.4 +195.2 +189.1 +196.8 +200.8 +187.6 +201.1 +197.0 +196.6 +177.7 +188.9 +186.2 +186.0 +199.9 +203.8 +190.2 +190.2 +186.3 +179.1 +198.8 +186.3 +192.4 +201.5 +190.2 +191.0 +178.0 +194.1 +197.9 +189.9 +180.2 +176.2 +193.5 +201.9 +201.3 +200.7 +190.3 +198.4 +186.0 +190.2 +182.7 +186.0 +190.0 +196.8 +193.0 +201.5 +184.7 +187.7 +190.8 +187.2 +183.5 +178.1 +178.9 +195.8 +178.2 +188.0 +177.2 +182.5 +184.2 +180.7 +189.6 +203.4 +181.7 +185.1 +177.5 +180.9 +194.0 +190.1 +190.1 +182.9 +184.4 +201.8 +191.1 +184.9 +197.7 +193.3 +190.3 +197.2 +188.2 +191.5 +190.0 +188.0 +187.5 +182.5 +186.6 +183.6 +193.9 +187.5 +186.9 +197.3 +187.2 +177.2 +186.8 +191.9 +186.9 +198.7 +179.7 +191.7 +193.6 +184.2 +178.4 +187.4 +203.3 +196.8 +195.4 +182.9 +187.7 +189.4 +192.5 +190.0 +190.0 +185.4 +194.6 +183.7 +180.2 +199.6 +190.5 +205.9 +176.4 +182.8 +179.0 +199.9 +186.7 +188.8 +189.5 +189.8 +193.5 +203.0 +183.8 +204.2 +176.4 +206.5 +184.7 +174.9 +183.2 +174.4 +196.2 +179.1 +183.7 +180.2 +174.5 +185.5 +203.5 +174.7 +186.1 +175.1 +198.2 +200.3 +184.2 +180.4 +187.9 +194.3 +194.9 +181.3 +180.2 +186.0 +187.6 +180.2 +188.1 +196.5 +175.9 +184.0 +194.4 +186.3 +207.6 +188.1 +185.2 +207.5 +177.7 +204.2 +203.3 +180.8 +176.0 +181.5 +202.3 +191.6 +186.8 +183.0 +189.7 +191.2 +179.2 +196.2 +185.9 +184.3 +183.3 +186.8 +179.3 +188.6 +184.8 +176.7 +189.6 +177.5 +186.3 +197.2 +178.1 +189.8 +175.8 +178.9 +191.9 +176.9 +186.8 +186.6 +194.1 +179.3 +178.7 +183.9 +220.3 +181.6 +183.7 +196.7 +180.2 +176.9 +189.5 +187.5 +185.8 +184.3 +179.9 +186.0 +178.5 +179.5 +227.6 +187.1 +199.2 +183.4 +177.2 +181.3 +190.7 +197.8 +180.1 +203.4 +198.1 +196.8 +184.3 +188.5 +189.6 +187.9 +200.7 +187.1 +193.3 +183.0 +199.1 +187.3 +181.8 +184.0 +178.0 +198.7 +177.4 +195.6 +184.1 +183.4 +186.0 +183.1 +181.8 +190.6 +188.8 +184.8 +196.5 +186.2 +184.7 +182.6 +187.4 +194.1 +185.3 +181.6 +198.4 +190.1 +200.2 +214.9 +195.1 +192.9 +190.8 +179.4 +194.9 +206.0 +193.2 +186.6 +214.1 +216.1 +189.0 +184.2 +201.1 +182.1 +200.2 +188.5 +193.0 +184.8 +188.8 +186.6 +188.3 +193.9 +202.4 +196.8 +214.5 +202.1 +186.7 +197.3 +184.2 +186.5 +202.6 +187.3 +204.3 +176.1 +183.9 +189.2 +189.7 +180.8 +199.4 +185.7 +193.3 +192.7 +192.8 +204.9 +188.7 +196.5 +187.9 +177.4 +189.6 +177.2 +197.3 +183.9 +189.7 +184.6 +191.0 +195.4 +175.7 +179.1 +184.4 +193.2 +180.4 +191.2 +187.2 +181.1 +203.5 +189.2 +186.4 +176.3 +191.5 +193.6 +187.0 +186.8 +187.3 +184.8 +197.6 +207.3 +191.8 +187.1 +196.3 +206.9 +199.9 +186.3 +189.9 +179.8 +200.0 +193.8 +190.9 +180.9 +176.3 +189.1 +180.4 +190.9 +182.4 +182.8 +181.1 +193.9 +184.0 +185.9 +199.1 +199.5 +204.2 +181.9 +199.0 +184.1 +207.4 +194.8 +216.5 +188.4 +206.7 +199.2 +179.3 +186.7 +200.7 +186.3 +195.5 +204.9 +194.0 +187.5 +196.3 +181.4 +186.0 +197.8 +198.1 +196.1 +184.3 +183.3 +211.3 +192.8 +187.9 +183.4 +192.9 +187.4 +194.2 +177.1 +200.0 +199.3 +177.2 +180.1 +178.6 +182.3 +189.1 +191.3 +197.7 +187.6 +187.3 +203.9 +204.4 +180.4 +186.5 +188.4 +176.1 +206.5 +187.7 +194.1 +193.5 +203.7 +186.8 +200.1 +187.0 +203.3 +196.3 +208.1 +192.7 +202.1 +204.7 +183.9 +186.7 +198.2 +189.4 +186.4 +188.3 +195.2 +203.3 +202.3 +201.1 +180.1 +191.3 +180.4 +187.7 +193.4 +196.5 +208.8 +185.3 +186.8 +203.6 +179.0 +185.0 +181.1 +193.8 +196.9 +201.0 +187.3 +189.6 +190.8 +188.9 +197.6 +203.0 +193.2 +185.6 +195.6 +199.1 +191.8 +178.7 +183.6 +184.1 +192.0 +182.4 +183.8 +209.5 +205.7 +187.0 +187.6 +200.6 +187.1 +187.2 +184.9 +180.3 +189.4 +182.7 +196.8 +184.7 +185.8 +184.0 +194.1 +180.5 +199.1 +181.7 +205.3 +182.6 +186.8 +180.2 +181.9 +187.4 +187.9 +191.8 +204.8 +178.5 +181.6 +181.6 +179.5 +181.9 +190.1 +194.1 +180.0 +179.1 +181.2 +175.0 +186.3 +184.8 +182.6 +186.8 +185.3 +181.2 +190.0 +195.3 +186.9 +190.6 +187.3 +193.4 +176.9 +190.5 +194.6 +181.0 +175.8 +187.9 +183.2 +187.7 +191.4 +183.1 +178.6 +193.4 +185.5 +190.1 +194.6 +191.0 +177.7 +187.3 +201.3 +188.6 +182.5 +196.3 +176.2 +184.9 +189.6 +186.9 +227.9 +188.1 +185.0 +182.9 +196.4 +183.1 +179.8 +186.5 +185.2 +199.7 +187.7 +191.6 +191.9 +194.8 +183.9 +185.3 +188.2 +192.7 +180.8 +183.5 +199.8 +196.8 +184.2 +179.5 +204.2 +183.5 +175.4 +207.3 +192.5 +191.9 +208.6 +194.7 +195.2 +190.3 +180.8 +182.6 +203.2 +191.4 +189.7 +183.9 +185.7 +192.3 +190.7 +206.8 +182.9 +195.2 +193.1 +201.2 +177.0 +188.1 +182.3 +185.0 +198.5 +186.4 +183.9 +189.4 +181.8 +199.9 +198.7 +183.7 +191.8 +187.5 +209.4 +201.8 +178.0 +201.8 +198.0 +187.2 +185.5 +178.1 +195.7 +200.1 +174.8 +176.4 +187.6 +177.3 +178.1 +197.5 +183.7 +207.7 +180.6 +176.6 +190.8 +191.0 +180.1 +180.4 +178.8 +185.5 +194.8 +188.1 +185.8 +179.5 +181.0 +196.1 +192.6 +179.2 +180.7 +206.3 +175.9 +196.8 +179.4 +195.1 +187.5 +183.9 +183.8 +184.6 +208.6 +195.5 +202.9 +191.9 +187.0 +189.2 +178.5 +176.1 +186.6 +195.1 +181.6 +201.0 +192.1 +192.9 +202.2 +191.0 +205.8 +204.3 +191.8 +185.1 +190.1 +193.6 +192.4 +185.2 +180.6 +192.6 +185.7 +187.5 +189.2 +184.0 +189.1 +200.6 +185.2 +182.8 +182.0 +185.4 +178.3 +188.4 +193.2 +194.9 +197.1 +194.4 +184.8 +179.2 +195.6 +182.6 +204.3 +203.1 +180.0 +187.7 +187.7 +197.8 +178.7 +186.5 +194.6 +194.8 +176.4 +202.5 +195.4 +180.5 +220.0 +191.0 +217.1 +178.4 +196.1 +182.5 +190.8 +191.7 +202.0 +196.8 +199.4 +183.8 +183.2 +186.5 +178.1 +198.6 +186.1 +195.4 +196.6 +199.3 +196.5 +177.9 +186.3 +180.5 +198.9 +190.2 +188.5 +184.1 +188.6 +201.4 +185.4 +176.4 +182.0 +176.9 +184.4 +179.0 +178.4 +183.8 +190.9 +185.5 +184.4 +179.6 +197.5 +184.3 +179.1 +193.4 +194.2 +179.1 +189.0 +191.2 +180.8 +184.5 +187.9 +191.7 +177.2 +185.4 +183.6 +190.7 +187.5 +188.8 +199.7 +190.0 +176.0 +204.4 +191.1 +188.3 +182.1 +181.6 +191.4 +191.8 +193.1 +186.0 +188.1 +178.4 +193.7 +175.1 +179.0 +186.7 +185.3 +179.7 +182.7 +198.0 +180.2 +189.5 +183.1 +177.1 +182.3 +174.7 +175.6 +186.4 +189.2 +178.9 +180.4 +181.3 +182.3 +180.3 +180.8 +180.1 +197.2 +204.7 +181.6 +182.3 +184.0 +176.5 +214.9 +194.7 +178.3 +175.1 +174.3 +176.5 +190.9 +183.1 +176.8 +177.1 +183.8 +178.5 +189.2 +180.8 +177.6 +175.3 +185.3 +183.4 +175.2 +183.6 +177.0 +181.9 +180.3 +185.1 +179.1 +188.1 +184.4 +181.6 +188.0 +181.6 +190.6 +177.9 +182.7 +177.4 +183.2 +189.3 +187.6 +179.8 +192.3 +188.1 +194.6 +186.7 +181.6 +191.8 +195.6 +187.4 +185.5 +190.0 +184.0 +178.5 +178.0 +200.8 +199.2 +186.5 +174.9 +181.3 +181.0 +187.6 +186.8 +183.3 +183.8 +195.6 +190.6 +193.8 +176.4 +179.1 +182.9 +193.4 +184.4 +186.2 +180.3 +194.7 +182.6 +186.7 +196.4 +196.7 +196.2 +192.3 +189.1 +178.4 +182.3 +195.2 +184.1 +189.0 +184.7 +189.3 +184.1 +177.3 +189.5 +186.1 +186.4 +180.5 +196.1 +187.5 +188.4 +195.8 +192.0 +192.9 +182.4 +199.6 +189.6 +180.2 +176.1 +220.4 +206.6 +195.5 +176.5 +202.2 +187.5 +185.0 +205.6 +195.7 +192.8 +192.8 +174.9 +187.3 +181.9 +180.4 +183.9 +187.6 +192.5 +202.1 +185.2 +200.2 +184.1 +189.9 +194.7 +185.1 +191.9 +176.0 +197.7 +177.5 +178.6 +179.3 +206.0 +193.3 +196.2 +179.3 +181.3 +205.5 +178.1 +186.9 +183.5 +178.5 +193.1 +179.4 +186.1 +174.3 +194.4 +187.1 +195.8 +176.0 +187.6 +176.1 +183.7 +182.0 +183.7 +176.3 +201.0 +185.8 +180.6 +186.1 +180.1 +204.4 +186.1 +182.9 +181.7 +184.6 +177.3 +186.2 +188.7 +180.5 +182.1 +185.3 +177.7 +185.4 +188.2 +194.1 +185.6 +177.8 +192.7 +198.1 +196.0 +187.5 +186.4 +206.8 +207.4 +177.3 +181.3 +185.0 +175.7 +174.2 +191.5 +194.0 +190.8 +199.2 +176.4 +202.7 +193.1 +182.6 +186.0 +196.0 +186.1 +189.4 +203.4 +184.8 +194.3 +187.2 +183.5 +199.4 +196.7 +180.2 +195.2 +180.9 +182.7 +193.5 +200.0 +193.3 +184.4 +199.1 +206.2 +199.0 +195.8 +189.4 +194.1 +196.4 +201.5 +180.7 +189.7 +193.3 +200.3 +205.5 +189.9 +192.6 +187.1 +205.7 +194.9 +188.0 +189.2 +197.9 +188.5 +185.0 +182.7 +205.8 +186.7 +189.2 +185.1 +182.4 +205.1 +181.4 +185.6 +188.5 +195.9 +199.8 +185.4 +194.7 +201.2 +185.0 +202.1 +200.0 +181.9 +186.8 +195.9 +182.2 +186.6 +187.1 +184.9 +181.1 +190.1 +191.0 +186.7 +184.6 +181.8 +190.4 +185.7 +183.4 +187.0 +182.6 +183.5 +173.9 +181.8 +194.1 +182.2 +182.6 +181.8 +187.0 +192.2 +178.9 +206.9 +197.1 +180.7 +183.4 +176.7 +187.9 +194.5 +182.0 +182.0 +190.9 +177.9 +180.3 +190.8 +187.7 +179.1 +187.9 +183.7 +187.9 +183.5 +175.9 +186.5 +178.2 +180.7 +194.3 +184.8 +181.4 +182.8 +179.8 +187.0 +179.1 +178.8 +187.2 +185.9 +182.0 +187.7 +181.3 +186.0 +181.0 +183.2 +186.5 +189.3 +187.8 +182.8 +179.4 +199.0 +180.9 +193.8 +202.6 +185.8 +177.5 +182.7 +202.4 +213.8 +190.6 +189.8 +190.5 +185.0 +183.7 +191.0 +185.7 +182.2 +194.8 +183.1 +191.8 +186.6 +185.2 +181.7 +182.1 +185.7 +195.0 +181.5 +185.1 +181.5 +187.3 +186.5 +188.7 +185.2 +180.7 +178.3 +191.8 +185.4 +188.6 +184.5 +187.5 +191.5 +191.5 +195.8 +197.9 +194.2 +184.9 +182.7 +182.1 +185.6 +188.4 +178.8 +190.4 +179.3 +186.2 +206.0 +191.2 +195.1 +198.4 +189.9 +180.8 +181.1 +198.5 +203.4 +190.8 +177.1 +184.0 +181.0 +192.2 +175.9 +200.4 +192.2 +181.1 +202.0 +198.6 +203.6 +191.7 +193.4 +192.6 +186.5 +182.4 +195.8 +184.9 +191.0 +176.7 +182.7 +194.8 +181.9 +191.1 +205.5 +176.3 +200.1 +184.1 +208.8 +184.3 +196.2 +190.2 +197.1 +186.8 +181.7 +188.5 +188.6 +187.2 +195.3 +198.6 +205.7 +185.4 +187.5 +176.2 +186.1 +199.4 +294.6 +232.7 +200.5 +181.8 +189.1 +186.9 +190.7 +179.7 +181.3 +181.5 +200.5 +181.2 +181.7 +185.4 +195.5 +201.9 +199.7 +194.2 +179.6 +181.4 +205.5 +183.1 +499.4 +188.5 +184.5 +194.5 +195.5 +193.6 +189.0 +196.4 +188.3 +186.7 +190.6 +176.9 +188.1 +189.4 +198.2 +178.8 +196.5 +203.1 +192.3 +193.8 +188.4 +180.2 +192.3 +187.1 +195.2 +181.6 +185.5 +190.9 +185.2 +200.5 +190.0 +180.2 +194.4 +196.5 +187.7 +184.3 +189.5 +188.2 +189.5 +194.5 +192.4 +202.1 +177.9 +193.8 +191.7 +182.2 +190.1 +197.4 +196.6 +193.7 +195.9 +200.9 +178.8 +181.4 +193.6 +193.5 +177.7 +192.9 +190.9 +201.2 +184.9 +175.8 +197.9 +202.4 +192.3 +202.5 +198.3 +196.8 +185.2 +184.8 +198.1 +191.3 +174.0 +180.5 +193.4 +183.0 +185.1 +177.0 +190.4 +181.3 +179.0 +180.6 +179.1 +188.8 +207.2 +183.7 +186.6 +198.3 +176.6 +186.5 +187.4 +179.5 +189.2 +197.7 +181.5 +194.6 +195.5 +181.6 +197.3 +190.7 +202.0 +183.9 +193.0 +183.9 +196.9 +203.5 +189.4 +187.1 +185.2 +184.8 +199.8 +175.7 +180.6 +187.6 +185.4 +178.5 +185.5 +178.7 +187.5 +185.7 +191.9 +180.2 +186.7 +183.0 +196.7 +197.6 +197.1 +187.4 +194.8 +194.7 +191.0 +185.1 +187.0 +185.8 +197.6 +204.2 +187.9 +189.9 +217.5 +202.8 +192.4 +176.4 +178.8 +199.8 +192.5 +199.9 +190.8 +202.8 +187.0 +180.2 +196.6 +176.5 +187.6 +181.3 +191.0 +180.6 +184.7 +195.8 +191.5 +182.7 +181.3 +189.3 +182.5 +184.2 +187.9 +202.4 +195.7 +189.5 +195.4 +201.2 +186.1 +208.8 +199.6 +189.4 +180.5 +194.2 +184.5 +187.5 +196.6 +188.0 +210.1 +193.3 +187.6 +191.6 +188.5 +179.2 +192.5 +189.7 +181.6 +180.9 +187.9 +180.6 +181.1 +178.7 +175.7 +176.2 +187.1 +185.0 +186.9 +186.5 +190.0 +185.6 +201.2 +189.3 +189.3 +189.7 +188.2 +187.2 +175.0 +185.5 +185.0 +205.2 +187.3 +177.2 +179.2 +190.2 +183.0 +192.9 +183.2 +185.4 +179.2 +186.2 +183.1 +183.8 +186.6 +185.7 +192.4 +179.6 +196.2 +180.2 +185.9 +197.4 +192.2 +177.5 +180.7 +190.2 +176.9 +193.6 +186.2 +184.9 +182.0 +186.1 +187.4 +184.3 +196.2 +203.1 +180.6 +192.5 +196.2 +195.9 +181.6 +189.9 +183.3 +192.7 +187.3 +191.0 +200.2 +180.1 +187.0 +175.7 +179.9 +193.1 +190.2 +202.1 +174.1 +179.5 +185.1 +182.7 +196.7 +179.8 +186.7 +192.5 +189.2 +193.7 +184.1 +185.6 +184.6 +193.6 +181.0 +189.2 +179.9 +181.4 +183.7 +184.5 +193.1 +191.5 +182.9 +190.2 +185.2 +185.9 +216.6 +184.1 +195.7 +181.2 +182.8 +199.3 +187.4 +184.9 +177.6 +206.2 +184.1 +201.5 +200.7 +192.2 +187.0 +182.1 +196.6 +181.6 +189.2 +191.6 +188.8 +186.9 +193.6 +184.2 +191.3 +194.9 +194.6 +188.7 +179.2 +205.1 +181.0 +177.3 +183.7 +184.8 +183.7 +204.3 +190.7 +181.6 +191.6 +189.5 +195.5 +176.0 +193.0 +192.1 +195.9 +205.8 +192.2 +190.6 +198.3 +185.8 +186.7 +198.6 +184.2 +203.1 +199.6 +187.0 +204.3 +207.2 +190.8 +196.7 +188.8 +193.2 +188.7 +199.8 +204.3 +188.6 +192.0 +209.8 +180.3 +203.7 +196.4 +185.7 +182.1 +193.3 +200.9 +196.6 +181.9 +182.2 +179.2 +197.3 +189.3 +200.9 +184.5 +185.8 +185.6 +187.5 +187.5 +189.5 +186.9 +187.2 +195.7 +182.0 +179.0 +191.4 +188.5 +177.7 +185.7 +182.0 +189.0 +198.7 +188.4 +186.6 +180.4 +185.1 +184.2 +207.6 +184.2 +190.0 +181.5 +190.9 +191.2 +187.8 +177.8 +182.7 +191.8 +206.2 +201.4 +182.2 +189.2 +195.4 +187.9 +195.7 +190.4 +183.3 +183.2 +178.2 +189.0 +179.2 +175.5 +187.3 +181.1 +180.1 +188.1 +184.4 +183.9 +200.2 +185.4 +180.4 +184.0 +183.8 +181.2 +203.6 +201.1 +197.1 +192.3 +182.7 +182.4 +183.0 +177.3 +181.3 +185.3 +184.6 +188.7 +179.1 +179.1 +184.6 +176.0 +177.8 +186.9 +189.8 +180.3 +177.2 +184.2 +179.2 +177.4 +182.0 +186.6 +188.7 +194.2 +184.0 +178.9 +175.3 +191.6 +182.9 +188.3 +178.7 +201.6 +186.4 +180.8 +184.7 +186.7 +196.9 +177.7 +178.8 +175.3 +182.7 +184.0 +182.4 +196.6 +174.7 +178.0 +181.5 +198.1 +185.1 +177.5 +181.2 +188.1 +196.0 +183.6 +196.2 +185.9 +193.2 +186.7 +179.7 +195.3 +190.1 +206.8 +186.2 +178.5 +192.0 +187.6 +176.9 +191.8 +190.8 +193.6 +200.0 +192.3 +178.0 +184.6 +179.4 +178.2 +183.2 +179.9 +184.9 +183.7 +189.1 +176.4 +185.5 +184.8 +192.5 +185.3 +193.9 +179.6 +181.7 +179.1 +210.8 +185.6 +184.2 +181.3 +198.8 +180.2 +182.5 +191.9 +196.4 +186.6 +185.3 +182.5 +175.8 +182.0 +186.9 +186.6 +176.0 +175.4 +186.5 +175.4 +182.4 +199.5 +192.7 +182.0 +199.9 +181.1 +196.7 +186.2 +182.0 +175.9 +184.4 +183.8 +178.4 +191.3 +177.8 +196.1 +200.8 +200.6 +191.1 +190.4 +180.3 +200.6 +201.4 +181.3 +176.9 +183.2 +185.0 +178.4 +187.8 +172.7 +182.8 +186.3 +193.9 +179.2 +194.9 +176.8 +188.1 +190.7 +192.3 +178.4 +191.9 +196.6 +183.5 +197.8 +192.0 +176.0 +192.1 +191.5 +193.3 +209.5 +185.2 +187.6 +186.3 +190.9 +183.9 +190.2 +186.3 +184.7 +177.3 +185.0 +178.3 +193.0 +181.7 +190.9 +185.3 +174.9 +192.3 +194.5 +194.1 +177.0 +185.2 +193.2 +202.4 +202.0 +197.2 +199.3 +176.9 +185.7 +188.1 +186.8 +184.4 +204.5 +196.4 +181.3 +190.9 +177.2 +192.4 +179.9 +177.0 +194.1 +203.5 +189.1 +194.7 +181.5 +183.6 +199.8 +187.3 +185.1 +195.7 +177.8 +178.3 +189.8 +200.1 +176.9 +177.9 +188.9 +190.9 +198.7 +197.7 +186.9 +190.8 +201.4 +188.5 +180.4 +196.2 +185.8 +194.9 +193.3 +181.3 +195.4 +197.5 +201.3 +179.5 +177.7 +201.9 +199.9 +183.5 +177.6 +190.4 +183.7 +178.4 +203.6 +195.9 +199.5 +193.2 +175.0 +184.5 +190.6 +194.3 +178.2 +177.5 +188.4 +179.7 +183.8 +174.2 +186.5 +179.8 +196.9 +194.9 +178.4 +185.7 +190.1 +181.3 +180.0 +179.9 +181.7 +187.4 +185.9 +178.9 +184.6 +189.5 +186.6 +185.8 +188.4 +188.3 +174.7 +184.9 +192.5 +185.3 +174.9 +180.6 +179.5 +183.4 +177.7 +177.5 +187.4 +183.7 +188.0 +177.6 +180.7 +180.9 +181.9 +188.9 +201.1 +186.6 +176.3 +177.3 +177.6 +175.6 +201.5 +175.5 +188.2 +181.8 +176.9 +185.3 +179.6 +192.7 +203.8 +183.4 +179.4 +177.9 +182.8 +182.3 +174.9 +180.5 +180.2 +187.1 +190.4 +185.5 +184.6 +185.4 +190.3 +183.9 +180.0 +201.8 +198.1 +184.2 +192.8 +184.2 +195.7 +191.4 +188.8 +192.9 +184.8 +174.3 +191.8 +185.4 +183.8 +192.7 +192.6 +194.8 +177.9 +178.7 +181.5 +177.3 +192.5 +176.0 +178.9 +199.7 +181.0 +178.8 +177.1 +183.0 +176.7 +182.3 +180.9 +183.1 +183.9 +174.2 +178.7 +173.8 +174.4 +179.1 +178.4 +175.4 +181.4 +180.9 +178.2 +182.5 +184.2 +183.7 +182.6 +176.2 +177.2 +180.5 +183.9 +193.4 +174.9 +178.8 +176.0 +178.1 +176.1 +177.3 +178.7 +182.2 +191.0 +186.8 +188.4 +180.3 +177.8 +198.8 +176.7 +198.3 +178.5 +177.9 +179.9 +186.7 +178.0 +175.4 +186.6 +190.8 +190.7 +183.5 +196.3 +182.1 +184.4 +188.8 +186.3 +184.8 +191.3 +186.2 +191.0 +188.3 +178.8 +176.3 +181.2 +183.1 +180.4 +178.6 +176.2 +176.1 +177.5 +180.6 +178.1 +176.4 +195.4 +178.8 +179.6 +178.1 +179.7 +178.3 +181.9 +177.4 +181.4 +186.7 +174.6 +179.4 +177.5 +197.6 +196.5 +193.1 +179.2 +182.2 +178.1 +186.3 +182.1 +175.4 +177.5 +188.1 +182.9 +179.8 +179.2 +180.0 +176.9 +177.4 +192.5 +177.5 +177.4 +181.8 +175.6 +178.9 +180.6 +176.8 +182.8 +186.7 +182.5 +188.3 +183.7 +185.1 +195.4 +179.2 +180.7 +190.0 +187.5 +180.3 +183.0 +184.2 +184.3 +189.4 +180.1 +179.6 +182.3 +177.6 +186.3 +188.0 +182.1 +187.1 +186.2 +184.2 +202.3 +187.0 +188.0 +180.5 +194.6 +188.8 +190.7 +181.9 +182.1 +185.3 +183.1 +197.3 +195.0 +208.3 +195.7 +179.1 +196.3 +185.5 +185.4 +186.9 +193.9 +187.1 +184.2 +180.0 +184.8 +189.6 +192.3 +185.6 +185.8 +185.9 +187.9 +186.2 +177.3 +192.2 +201.9 +185.9 +181.7 +192.8 +185.4 +187.3 +187.5 +178.9 +187.6 +181.3 +186.9 +179.6 +179.3 +189.4 +177.5 +184.5 +176.4 +193.3 +178.7 +191.6 +182.7 +194.6 +187.4 +190.4 +199.1 +191.6 +193.1 +187.0 +181.7 +180.3 +196.6 +180.5 +180.0 +184.9 +176.9 +176.3 +179.3 +189.1 +180.4 +176.0 +174.9 +181.2 +177.6 +177.4 +177.6 +179.0 +178.7 +178.7 +184.8 +177.9 +179.4 +183.8 +181.9 +177.1 +175.8 +182.9 +183.2 +176.8 +181.7 +183.7 +192.1 +178.2 +181.4 +180.0 +176.8 +179.8 +194.3 +181.6 +180.7 +177.5 +187.4 +179.0 +180.0 +186.0 +182.0 +175.4 +178.9 +181.9 +192.0 +179.5 +178.5 +175.3 +184.1 +178.3 +177.1 +186.2 +196.9 +182.0 +178.0 +181.4 +181.4 +179.6 +180.2 +180.4 +183.1 +176.6 +182.9 +176.3 +191.5 +179.9 +178.1 +178.4 +189.3 +196.8 +188.2 +180.5 +185.2 +188.0 +176.3 +181.0 +187.4 +178.9 +179.1 +185.3 +183.4 +185.8 +178.5 +176.1 +188.9 +200.9 +193.9 +193.4 +184.0 +187.2 +178.1 +193.5 +182.0 +180.1 +182.8 +176.8 +177.0 +184.0 +185.2 +197.3 +180.8 +190.4 +177.3 +183.4 +191.3 +175.4 +176.8 +192.3 +177.3 +191.1 +185.1 +179.7 +190.8 +175.9 +181.2 +194.0 +176.5 +180.3 +175.1 +185.1 +181.3 +200.0 +187.0 +182.6 +182.0 +176.8 +190.1 +182.1 +181.7 +192.3 +177.0 +185.1 +181.3 +179.2 +182.1 +176.6 +185.0 +177.4 +183.5 +196.5 +184.2 +179.2 +180.5 +180.8 +181.9 +176.9 +178.6 +175.9 +192.0 +198.7 +189.6 +190.5 +186.3 +192.6 +196.0 +176.2 +175.9 +176.5 +195.4 +174.8 +177.4 +186.7 +174.9 +176.1 +183.0 +178.1 +177.4 +192.9 +179.7 +198.5 +178.1 +178.6 +195.4 +181.6 +179.9 +179.5 +186.4 +180.6 +180.3 +187.3 +193.9 +192.5 +178.1 +184.8 +194.9 +194.6 +181.4 +177.6 +180.4 +175.5 +180.5 +177.2 +186.6 +176.8 +176.3 +186.0 +179.8 +178.2 +177.9 +178.9 +190.1 +180.8 +180.1 +181.3 +193.5 +185.0 +198.7 +199.9 +183.4 +177.3 +176.2 +182.7 +178.2 +175.2 +177.7 +186.9 +182.1 +195.1 +181.3 +182.4 +175.7 +185.5 +180.8 +181.4 +181.6 +190.1 +183.7 +186.4 +194.3 +176.4 +176.7 +189.9 +175.6 +206.6 +178.9 +180.0 +178.8 +180.1 +184.1 +176.3 +177.9 +194.3 +188.5 +182.6 +178.6 +177.5 +190.9 +178.9 +180.3 +193.9 +187.4 +186.4 +190.1 +184.7 +199.2 +187.9 +177.7 +180.5 +189.4 +176.5 +192.8 +181.4 +178.0 +193.6 +175.8 +184.4 +184.1 +191.9 +176.2 +184.5 +180.4 +185.1 +180.0 +192.6 +187.6 +190.2 +183.0 +176.5 +194.4 +178.4 +184.5 +176.6 +179.0 +182.6 +187.2 +180.4 +183.3 +180.8 +179.1 +193.9 +181.9 +188.8 +180.0 +175.2 +194.5 +194.8 +187.7 +182.5 +199.3 +175.0 +175.9 +177.9 +179.6 +179.4 +176.0 +176.4 +175.8 +177.2 +176.8 +179.0 +180.4 +177.1 +174.9 +177.9 +180.6 +187.3 +176.6 +177.3 +184.4 +179.2 +177.2 +175.7 +183.3 +177.4 +180.7 +175.6 +173.6 +179.6 +177.2 +180.8 +176.4 +187.7 +178.4 +176.0 +192.5 +181.4 +183.5 +178.5 +186.6 +179.4 +180.1 +181.6 +178.3 +178.2 +184.1 +180.6 +178.3 +184.1 +181.6 +177.4 +183.4 +180.6 +178.8 +179.0 +175.1 +177.0 +180.8 +179.9 +177.7 +176.3 +177.3 +184.2 +176.9 +190.8 +183.0 +181.0 +184.0 +178.6 +175.2 +177.2 +177.9 +183.2 +180.9 +176.3 +177.0 +182.8 +178.5 +176.9 +178.1 +175.5 +184.7 +176.6 +177.2 +182.4 +189.5 +197.3 +179.9 +190.4 +182.4 +188.7 +185.5 +181.1 +182.6 +187.0 +184.4 +179.1 +180.1 +180.9 +187.2 +175.9 +194.1 +177.9 +177.4 +200.7 +201.4 +183.5 +180.8 +195.5 +181.0 +180.2 +181.5 +175.9 +175.3 +180.1 +182.1 +180.1 +176.5 +178.9 +200.5 +178.6 +176.6 +175.8 +175.8 +176.0 +199.3 +177.5 +180.9 +185.1 +200.3 +194.0 +177.7 +181.2 +182.1 +185.1 +183.4 +180.7 +188.8 +187.3 +179.1 +177.5 +185.5 +185.5 +177.2 +175.3 +178.6 +184.5 +180.0 +179.9 +177.1 +178.0 +187.8 +183.6 +189.6 +181.5 +179.7 +179.0 +188.8 +185.1 +177.0 +177.1 +176.3 +176.4 +199.6 +178.1 +188.2 +176.9 +191.5 +194.0 +182.6 +184.0 +186.4 +184.1 +180.0 +181.1 +187.3 +176.1 +174.5 +189.3 +188.9 +178.7 +177.2 +191.0 +194.3 +187.7 +177.0 +176.4 +183.3 +176.2 +181.3 +181.0 +176.8 +187.8 +178.2 +202.3 +191.6 +180.3 +174.8 +187.8 +176.9 +176.9 +181.1 +177.5 +186.1 +179.3 +187.2 +176.6 +178.6 +178.7 +179.0 +188.7 +188.5 +183.8 +192.2 +178.8 +180.4 +182.1 +177.0 +175.1 +180.1 +176.7 +176.0 +188.1 +191.3 +186.6 +202.0 +182.5 +175.9 +182.1 +195.6 +174.8 +194.5 +181.2 +191.8 +180.4 +178.7 +186.1 +180.9 +186.4 +177.0 +183.6 +179.7 +208.1 +179.4 +179.1 +178.5 +179.2 +180.7 +183.3 +180.4 +187.6 +176.9 +185.4 +187.5 +184.3 +183.5 +181.0 +190.8 +178.6 +180.0 +180.4 +191.0 +182.8 +186.9 +179.6 +189.1 +186.9 +202.3 +181.1 +192.1 +194.3 +176.7 +185.8 +177.3 +179.5 +177.2 +177.8 +190.5 +190.1 +184.1 +191.8 +178.7 +185.9 +178.3 +177.4 +176.4 +177.8 +179.0 +177.6 +179.7 +178.1 +190.5 +177.8 +180.4 +181.6 +181.3 +177.3 +182.1 +189.7 +178.2 +179.7 +181.3 +178.8 +194.0 +200.6 +179.6 +177.9 +181.0 +184.7 +180.8 +181.1 +187.8 +183.4 +186.2 +181.8 +175.8 +176.8 +176.3 +177.0 +186.7 +178.5 +183.0 +177.1 +182.3 +182.2 +180.6 +180.6 +185.2 +178.9 +192.3 +177.6 +212.1 +180.5 +186.4 +187.4 +196.4 +181.9 +194.5 +188.5 +179.4 +179.3 +181.4 +185.7 +176.9 +182.6 +185.0 +181.7 +178.9 +184.4 +180.4 +189.2 +187.5 +204.6 +189.7 +182.6 +182.1 +180.0 +182.3 +180.1 +189.3 +178.1 +183.8 +180.9 +184.8 +182.2 +177.7 +176.1 +197.5 +195.8 +183.3 +183.4 +199.0 +205.4 +183.6 +182.5 +182.2 +199.1 +187.1 +179.5 +182.9 +180.2 +177.5 +183.4 +189.4 +196.2 +181.2 +183.3 +178.6 +181.5 +185.5 +179.8 +184.3 +198.8 +184.9 +178.0 +177.7 +181.0 +186.0 +196.3 +178.1 +192.4 +178.7 +181.4 +192.5 +181.4 +192.5 +182.2 +178.9 +183.9 +185.3 +192.2 +186.0 +193.4 +178.4 +194.5 +179.9 +187.6 +185.9 +179.2 +185.0 +188.1 +189.4 +187.9 +190.0 +191.9 +187.5 +186.0 +189.6 +182.4 +185.6 +187.0 +192.4 +183.3 +189.2 +194.6 +195.9 +190.7 +186.4 +185.9 +185.3 +177.1 +184.6 +186.4 +183.1 +186.2 +180.0 +181.8 +184.7 +176.6 +178.0 +178.5 +182.2 +200.3 +184.2 +186.9 +178.2 +190.3 +194.9 +193.1 +193.5 +188.5 +177.9 +185.4 +177.6 +179.7 +193.0 +200.3 +182.0 +192.3 +183.3 +183.6 +183.2 +190.8 +182.6 +177.7 +177.1 +183.7 +177.5 +175.4 +180.3 +181.3 +178.8 +182.3 +176.5 +177.4 +176.0 +178.8 +184.3 +183.0 +180.2 +184.9 +188.1 +187.6 +182.8 +183.0 +180.0 +177.5 +196.1 +185.1 +175.5 +175.9 +178.6 +201.5 +177.1 +191.7 +184.8 +176.2 +184.4 +175.9 +182.6 +182.0 +180.8 +178.7 +191.3 +179.5 +178.7 +174.8 +185.1 +174.9 +189.8 +191.3 +191.9 +190.7 +175.8 +177.3 +183.6 +178.0 +185.1 +177.2 +178.6 +188.7 +179.0 +186.0 +202.6 +177.2 +179.8 +196.0 +195.8 +186.8 +183.6 +202.2 +182.0 +175.8 +186.6 +178.2 +181.3 +180.8 +183.6 +181.9 +192.4 +190.8 +179.2 +184.6 +186.2 +183.0 +196.5 +179.6 +183.4 +187.2 +179.5 +185.3 +182.2 +177.8 +176.6 +177.2 +179.3 +177.1 +181.9 +178.9 +180.2 +179.8 +175.0 +176.8 +180.5 +175.7 +176.3 +180.6 +181.6 +176.6 +175.4 +177.4 +176.2 +180.8 +188.0 +178.2 +205.6 +177.8 +176.5 +179.4 +176.4 +183.3 +177.9 +174.2 +176.1 +182.8 +180.8 +186.9 +179.4 +195.8 +178.8 +181.6 +199.7 +175.8 +186.7 +179.1 +182.4 +179.1 +176.3 +180.5 +174.7 +180.8 +178.2 +185.2 +183.3 +192.7 +184.9 +205.8 +177.0 +182.5 +187.3 +185.9 +184.3 +177.0 +193.2 +179.0 +177.5 +181.3 +176.2 +178.8 +197.3 +180.8 +180.8 +189.7 +188.3 +179.5 +179.3 +185.3 +184.8 +192.3 +180.4 +186.7 +180.0 +178.9 +177.9 +179.4 +177.3 +181.9 +175.4 +174.1 +180.2 +176.9 +178.6 +177.5 +176.4 +177.7 +180.6 +182.2 +178.0 +179.3 +176.9 +188.6 +180.4 +179.8 +180.7 +177.2 +176.7 +175.3 +176.4 +178.4 +177.8 +180.1 +193.5 +181.0 +183.6 +177.9 +181.9 +186.9 +197.3 +175.7 +177.6 +184.7 +179.6 +182.2 +181.9 +181.2 +176.7 +185.5 +196.8 +185.9 +182.4 +178.1 +180.2 +178.3 +181.7 +186.4 +175.6 +182.6 +179.6 +181.0 +188.0 +178.1 +176.9 +181.0 +179.6 +185.0 +177.7 +179.4 +178.0 +185.7 +179.0 +194.2 +175.4 +178.5 +175.0 +176.7 +179.3 +188.8 +181.0 +177.3 +188.0 +178.5 +180.0 +175.9 +180.7 +181.2 +177.0 +179.2 +179.3 +193.1 +176.0 +179.4 +185.4 +177.4 +177.2 +177.9 +176.4 +186.7 +178.0 +178.7 +180.5 +186.7 +176.4 +185.8 +182.5 +177.3 +180.3 +179.5 +174.6 +184.2 +184.0 +176.3 +176.0 +211.4 +184.5 +182.1 +180.5 +188.1 +177.9 +179.7 +177.5 +184.5 +180.6 +191.1 +187.4 +176.3 +180.4 +183.2 +184.8 +178.6 +189.8 +180.9 +176.6 +176.9 +176.4 +182.0 +178.2 +177.3 +189.9 +191.1 +178.0 +179.5 +176.7 +194.2 +211.7 +175.9 +177.9 +176.0 +179.7 +180.6 +176.4 +180.3 +177.2 +183.6 +180.6 +187.4 +189.4 +189.6 +177.1 +179.4 +184.3 +186.4 +181.9 +180.1 +184.8 +182.0 +188.2 +181.4 +184.8 +177.5 +180.3 +195.4 +185.7 +176.8 +176.0 +187.3 +202.2 +194.5 +181.1 +182.9 +197.6 +186.9 +181.3 +180.9 +183.9 +191.0 +182.5 +186.9 +179.7 +179.2 +185.6 +184.3 +180.4 +190.9 +183.6 +183.1 +175.0 +180.9 +186.3 +174.8 +176.4 +176.1 +175.7 +178.1 +178.0 +180.5 +182.6 +180.0 +179.2 +186.8 +188.3 +179.8 +179.6 +180.5 +176.9 +204.2 +195.2 +180.2 +177.9 +187.9 +191.3 +183.9 +185.3 +186.8 +182.5 +189.5 +176.8 +180.5 +189.3 +185.9 +178.0 +176.0 +179.2 +181.3 +175.6 +179.9 +179.4 +183.8 +178.8 +177.9 +180.1 +182.3 +180.9 +175.7 +178.1 +173.7 +177.9 +180.0 +176.2 +177.7 +179.4 +192.3 +175.7 +185.7 +188.6 +195.5 +182.5 +184.1 +177.9 +177.8 +183.0 +174.9 +174.9 +179.7 +186.4 +185.9 +175.2 +179.2 +176.8 +181.5 +178.3 +180.6 +176.8 +175.8 +175.9 +180.3 +179.7 +175.8 +174.1 +180.6 +176.7 +179.7 +175.7 +179.2 +178.3 +203.4 +177.9 +186.9 +175.7 +177.0 +178.8 +180.2 +180.1 +185.5 +175.0 +178.6 +183.3 +177.2 +178.3 +180.1 +175.9 +175.6 +179.3 +179.8 +178.0 +186.5 +176.4 +187.2 +181.6 +175.4 +177.5 +179.1 +179.2 +175.9 +174.3 +175.5 +177.4 +178.5 +182.0 +181.4 +184.4 +183.4 +174.4 +177.5 +175.0 +175.4 +174.1 +182.8 +184.9 +178.3 +182.9 +182.3 +186.6 +180.9 +191.6 +185.6 +184.8 +190.2 +175.8 +185.2 +175.5 +176.9 +176.7 +176.9 +176.5 +179.2 +179.9 +179.2 +177.8 +188.9 +180.3 +186.8 +182.0 +180.8 +176.6 +180.5 +175.3 +185.0 +181.1 +181.1 +176.5 +176.5 +178.0 +175.9 +180.2 +178.3 +188.0 +185.9 +178.6 +179.5 +187.6 +180.2 +181.3 +182.5 +182.8 +175.0 +184.9 +181.7 +178.5 +177.5 +177.1 +176.8 +179.0 +179.2 +175.5 +177.1 +178.9 +174.8 +179.0 +177.3 +179.2 +178.2 +177.5 +191.8 +177.6 +182.1 +178.7 +177.2 +182.5 +181.5 +185.6 +179.5 +183.9 +177.6 +179.6 +193.9 +175.9 +216.0 +180.4 +177.3 +178.8 +176.7 +177.3 +178.3 +188.8 +175.8 +175.7 +176.0 +178.3 +185.1 +179.0 +191.3 +180.0 +178.8 +189.8 +182.7 +176.7 +178.0 +184.2 +185.0 +190.3 +178.0 +178.7 +177.4 +186.0 +176.4 +179.6 +178.0 +182.2 +175.9 +175.7 +178.3 +174.7 +193.6 +177.2 +186.6 +183.7 +181.0 +192.1 +176.4 +188.6 +205.9 +199.0 +194.6 +193.4 +190.4 +178.1 +188.0 +183.8 +180.5 +180.5 +190.5 +194.5 +180.8 +185.3 +193.4 +183.1 +178.1 +197.4 +190.2 +181.0 +184.9 +189.0 +184.6 +183.7 +187.7 +187.9 +182.5 +192.6 +184.3 +178.7 +179.8 +196.8 +184.4 +195.5 +183.9 +186.6 +181.0 +188.7 +185.0 +197.1 +204.2 +176.8 +179.6 +185.3 +182.1 +178.6 +180.6 +183.0 +199.1 +180.5 +180.7 +189.2 +194.5 +187.1 +192.3 +179.3 +196.8 +197.3 +180.3 +177.0 +178.8 +183.2 +183.1 +176.6 +201.6 +183.8 +182.7 +177.0 +185.2 +180.1 +199.4 +203.2 +187.4 +185.7 +184.1 +183.0 +198.7 +186.3 +187.9 +199.4 +185.9 +190.3 +202.8 +204.6 +178.1 +187.6 +196.5 +180.3 +181.7 +182.9 +199.2 +198.8 +190.5 +219.6 +180.0 +203.5 +182.0 +184.1 +176.8 +200.9 +201.9 +180.9 +177.9 +183.4 +179.5 +197.8 +188.0 +204.1 +185.6 +179.3 +190.6 +184.3 +195.2 +188.0 +200.9 +187.7 +183.0 +180.9 +180.0 +184.7 +176.8 +181.9 +180.6 +180.0 +177.7 +182.2 +175.2 +182.9 +186.3 +203.4 +183.3 +175.8 +186.2 +179.8 +178.4 +178.2 +420.1 +177.9 +193.8 +183.8 +180.0 +188.8 +187.2 +192.0 +180.8 +182.3 +191.8 +183.2 +176.6 +179.5 +215.2 +193.8 +188.3 +180.3 +186.4 +181.8 +205.8 +175.1 +186.5 +205.9 +185.5 +187.1 +191.7 +189.4 +188.3 +180.1 +179.8 +178.9 +179.3 +181.9 +184.9 +188.4 +176.2 +177.0 +181.8 +179.5 +177.3 +176.0 +208.3 +185.6 +180.6 +180.3 +195.0 +177.7 +175.7 +214.3 +188.2 +177.7 +187.1 +197.1 +184.8 +178.2 +182.1 +178.4 +176.3 +190.6 +187.3 +177.8 +177.2 +191.7 +200.2 +188.4 +177.3 +177.0 +178.5 +180.3 +177.0 +182.2 +184.4 +175.8 +185.6 +179.6 +180.8 +179.9 +199.4 +175.1 +196.3 +185.5 +182.5 +179.4 +191.2 +192.4 +180.3 +186.8 +194.7 +180.5 +178.0 +180.8 +184.9 +180.7 +179.0 +184.5 +180.5 +176.2 +182.9 +178.8 +183.5 +194.6 +182.0 +198.5 +176.5 +185.0 +187.6 +196.9 +194.2 +185.0 +190.8 +179.6 +175.8 +198.5 +180.0 +177.9 +203.3 +183.3 +181.4 +183.8 +176.5 +175.1 +193.9 +178.9 +183.0 +180.2 +178.0 +178.9 +188.1 +181.3 +179.3 +181.6 +183.1 +175.0 +178.8 +175.7 +179.4 +180.6 +185.9 +176.7 +190.8 +189.6 +180.1 +176.7 +181.7 +180.7 +191.9 +175.5 +180.1 +185.1 +182.0 +181.0 +184.3 +196.6 +188.8 +191.0 +184.2 +184.1 +180.1 +189.0 +178.2 +182.6 +190.4 +182.5 +186.3 +182.9 +191.5 +183.6 +193.2 +183.3 +201.4 +215.5 +182.4 +196.6 +183.6 +190.7 +180.8 +190.9 +178.2 +189.8 +194.8 +180.9 +184.2 +184.1 +185.3 +176.7 +181.5 +181.0 +191.1 +195.9 +192.0 +178.0 +179.9 +184.7 +181.3 +188.5 +187.6 +184.7 +193.9 +198.5 +188.5 +186.5 +184.4 +188.1 +183.0 +190.8 +184.9 +183.8 +184.5 +185.5 +180.2 +181.3 +181.8 +177.5 +178.6 +205.8 +188.5 +188.1 +183.9 +182.9 +192.0 +181.9 +181.5 +190.2 +185.2 +188.1 +174.3 +190.1 +185.6 +183.3 +182.1 +192.2 +179.6 +179.7 +187.5 +187.3 +187.1 +180.0 +184.2 +180.6 +311.7 +185.9 +185.9 +185.1 +176.8 +176.4 +177.7 +177.1 +188.2 +174.8 +184.0 +178.6 +180.2 +183.4 +182.9 +189.8 +187.3 +176.8 +192.5 +186.1 +212.1 +179.4 +181.3 +188.4 +177.7 +200.4 +182.7 +177.5 +183.5 +187.5 +182.6 +194.0 +179.6 +181.6 +201.6 +183.0 +185.5 +177.8 +200.1 +182.7 +182.1 +192.5 +185.6 +188.8 +180.8 +179.5 +181.1 +185.1 +178.1 +178.9 +183.1 +180.8 +178.7 +184.8 +180.1 +179.7 +187.2 +187.1 +182.5 +187.0 +185.6 +191.9 +187.7 +186.3 +188.0 +180.1 +179.3 +181.3 +193.7 +176.8 +184.4 +176.7 +183.1 +189.4 +178.8 +211.4 +182.2 +190.8 +194.1 +179.4 +176.3 +178.6 +175.5 +182.9 +186.0 +184.1 +187.8 +181.3 +188.4 +178.3 +178.7 +200.4 +176.0 +187.7 +197.2 +188.2 +188.6 +192.3 +180.3 +186.3 +187.6 +201.5 +183.8 +182.9 +185.8 +185.9 +181.9 +180.2 +177.3 +183.3 +186.4 +181.3 +195.4 +194.0 +182.7 +178.3 +190.5 +178.6 +194.5 +175.4 +176.8 +177.4 +191.1 +180.6 +175.5 +175.2 +186.9 +176.1 +183.6 +187.1 +178.4 +196.0 +201.0 +183.0 +179.7 +179.9 +184.1 +179.9 +193.1 +180.8 +187.2 +182.0 +176.1 +183.4 +188.4 +177.6 +177.3 +183.8 +179.5 +184.4 +179.8 +181.5 +178.6 +186.5 +183.1 +175.7 +177.3 +177.5 +194.3 +190.9 +175.4 +198.2 +182.6 +180.9 +182.7 +177.0 +190.4 +186.8 +199.4 +183.6 +180.4 +196.3 +205.0 +178.0 +182.0 +185.0 +180.0 +200.6 +185.4 +186.6 +188.4 +199.1 +183.3 +211.8 +182.2 +186.0 +186.5 +185.4 +183.8 +182.3 +179.9 +182.0 +186.9 +184.6 +187.9 +182.4 +187.3 +199.6 +187.9 +196.6 +189.9 +190.9 +180.9 +177.5 +180.2 +188.4 +181.4 +178.0 +190.8 +198.7 +191.5 +189.8 +191.4 +188.6 +186.2 +198.8 +207.4 +192.2 +185.0 +194.7 +198.7 +191.4 +197.4 +184.9 +202.5 +178.6 +180.3 +186.9 +184.5 +178.3 +179.4 +201.3 +193.5 +184.2 +193.9 +177.1 +185.8 +188.5 +183.6 +201.0 +183.6 +190.9 +190.3 +185.4 +180.2 +185.9 +182.6 +184.7 +182.1 +188.7 +180.5 +178.0 +198.6 +188.8 +183.8 +178.1 +176.4 +179.4 +179.5 +186.5 +180.4 +179.8 +180.2 +195.0 +177.2 +180.0 +195.5 +181.7 +183.6 +187.1 +180.7 +180.7 +181.2 +184.5 +181.0 +199.1 +197.4 +183.6 +189.0 +183.2 +196.4 +183.7 +191.4 +185.3 +202.8 +177.9 +182.1 +179.5 +176.3 +193.1 +184.6 +178.8 +191.8 +224.8 +180.5 +182.0 +178.8 +178.2 +178.8 +185.4 +186.5 +178.6 +188.4 +179.7 +190.9 +181.6 +179.8 +190.1 +188.0 +179.7 +204.8 +184.8 +179.7 +182.5 +184.1 +176.1 +181.1 +177.2 +185.8 +177.7 +179.1 +186.7 +183.8 +177.5 +180.2 +200.9 +181.0 +182.7 +177.7 +191.7 +176.8 +190.9 +178.6 +182.3 +178.8 +186.1 +176.8 +180.0 +182.5 +175.0 +183.2 +177.4 +189.5 +177.7 +179.1 +180.8 +177.9 +180.0 +181.6 +178.6 +178.1 +184.7 +176.8 +176.0 +175.7 +175.9 +180.3 +176.3 +180.4 +178.8 +182.5 +177.9 +187.7 +182.2 +187.2 +178.5 +179.9 +181.1 +180.9 +182.5 +179.8 +178.6 +176.0 +179.0 +189.5 +183.5 +182.5 +183.6 +186.0 +181.7 +191.1 +187.4 +194.0 +197.2 +197.3 +177.9 +179.4 +178.0 +176.9 +178.6 +189.0 +183.3 +188.5 +184.0 +184.0 +185.5 +180.7 +176.3 +180.2 +179.9 +186.7 +177.5 +181.0 +189.3 +190.3 +178.3 +188.1 +189.5 +183.6 +190.7 +178.9 +179.1 +179.5 +196.1 +180.8 +178.9 +177.9 +184.6 +178.0 +181.9 +185.3 +187.5 +183.9 +187.0 +180.1 +198.9 +182.6 +181.1 +191.1 +190.7 +183.6 +181.2 +184.5 +177.3 +179.5 +178.7 +181.5 +180.1 +185.0 +178.6 +182.8 +182.7 +192.6 +181.5 +176.5 +178.4 +187.0 +180.2 +187.5 +182.5 +178.0 +184.6 +180.3 +187.2 +177.6 +187.5 +190.6 +188.2 +187.8 +182.6 +186.8 +179.0 +211.0 +176.2 +180.5 +183.4 +215.3 +181.6 +208.2 +215.6 +207.8 +189.1 +176.5 +190.9 +187.0 +178.8 +190.2 +183.0 +193.6 +175.3 +184.4 +184.2 +181.7 +175.4 +177.4 +176.1 +187.1 +194.1 +181.3 +193.1 +182.1 +184.7 +183.1 +186.0 +180.6 +181.7 +181.7 +184.9 +191.4 +201.1 +181.7 +192.9 +177.6 +187.6 +177.6 +183.6 +195.4 +187.2 +184.2 +185.4 +178.0 +201.0 +180.0 +205.9 +201.9 +199.2 +185.2 +182.7 +181.0 +187.4 +181.5 +186.6 +180.4 +176.7 +177.9 +178.2 +179.3 +186.5 +175.7 +185.5 +195.1 +181.9 +183.3 +182.0 +176.7 +181.0 +176.1 +179.4 +186.2 +194.1 +191.1 +184.0 +185.1 +188.6 +183.2 +176.1 +179.3 +183.6 +177.5 +192.7 +180.6 +191.3 +182.6 +189.3 +183.6 +182.4 +178.8 +175.5 +179.3 +193.2 +181.3 +183.9 +185.1 +183.3 +184.9 +188.6 +185.7 +194.9 +199.5 +175.7 +202.6 +194.2 +185.3 +178.0 +182.5 +202.3 +187.5 +192.9 +181.8 +183.2 +183.5 +187.8 +179.6 +178.7 +197.1 +182.2 +202.2 +194.8 +184.9 +192.6 +175.5 +178.0 +184.7 +177.8 +178.7 +179.7 +181.4 +186.6 +179.3 +178.9 +182.3 +180.8 +180.5 +182.4 +184.0 +183.2 +181.2 +183.6 +186.2 +179.7 +178.6 +200.2 +189.2 +177.5 +188.0 +191.1 +183.2 +183.4 +191.0 +186.9 +198.9 +180.0 +175.3 +261.0 +178.7 +181.1 +181.2 +178.7 +179.1 +201.6 +178.4 +181.6 +176.2 +181.7 +192.2 +184.5 +179.1 +191.8 +181.3 +184.4 +185.0 +186.6 +178.6 +180.2 +185.2 +190.0 +177.3 +178.5 +193.7 +178.7 +184.8 +179.9 +192.0 +186.2 +194.7 +181.5 +183.9 +188.0 +183.4 +181.3 +187.2 +184.6 +188.7 +185.3 +180.1 +183.4 +182.4 +176.9 +177.6 +191.1 +180.8 +176.0 +175.4 +180.0 +179.6 +188.3 +191.3 +183.2 +181.7 +177.9 +180.0 +182.0 +181.3 +180.1 +187.7 +177.5 +186.7 +183.4 +189.1 +199.9 +203.9 +184.1 +179.2 +187.4 +185.7 +181.8 +179.7 +179.1 +175.6 +200.8 +181.2 +175.9 +179.3 +176.9 +183.8 +195.7 +185.6 +181.9 +184.0 +190.1 +188.6 +179.0 +181.7 +179.2 +212.9 +181.3 +192.8 +180.8 +180.4 +190.8 +178.1 +181.2 +183.4 +189.4 +191.6 +196.7 +179.2 +203.5 +197.4 +180.5 +184.2 +194.9 +182.6 +183.3 +177.9 +200.4 +191.9 +175.4 +214.3 +184.1 +180.8 +182.9 +181.3 +182.4 +181.4 +191.0 +178.9 +188.3 +195.6 +182.1 +193.5 +180.3 +196.3 +177.9 +198.8 +190.9 +179.1 +185.4 +188.7 +181.0 +177.8 +178.0 +203.4 +179.4 +180.8 +183.6 +179.0 +184.8 +178.0 +188.3 +203.1 +194.9 +196.8 +187.9 +185.7 +178.0 +180.8 +176.8 +181.0 +180.9 +187.0 +179.5 +186.9 +175.9 +174.7 +177.1 +187.9 +193.2 +177.3 +177.9 +181.2 +180.1 +176.1 +179.4 +176.7 +183.2 +191.8 +184.7 +180.8 +183.2 +176.9 +182.8 +187.5 +187.3 +185.7 +184.6 +178.8 +183.0 +176.7 +177.6 +198.0 +194.7 +188.8 +180.9 +187.9 +175.9 +193.1 +183.4 +186.2 +184.4 +182.3 +175.6 +224.3 +186.0 +184.0 +179.0 +176.6 +183.9 +190.9 +188.5 +183.9 +184.7 +198.6 +184.1 +182.0 +181.3 +183.3 +181.6 +182.8 +186.6 +181.4 +179.0 +192.0 +186.3 +202.8 +190.0 +182.1 +186.4 +186.0 +187.0 +192.8 +185.4 +185.4 +181.2 +182.3 +183.2 +190.4 +184.9 +186.4 +180.9 +181.4 +179.8 +191.3 +182.4 +193.0 +202.1 +187.9 +222.0 +196.9 +201.9 +197.8 +198.7 +184.1 +192.7 +203.2 +197.1 +193.7 +196.5 +186.3 +198.4 +181.2 +192.3 +190.4 +183.3 +181.9 +201.8 +198.5 +194.7 +182.3 +184.9 +186.0 +185.9 +190.8 +179.4 +182.2 +181.7 +180.6 +179.2 +180.9 +189.7 +195.7 +200.7 +191.6 +191.1 +187.6 +181.6 +185.9 +188.3 +183.5 +187.7 +190.3 +186.1 +181.1 +178.7 +178.0 +179.0 +191.5 +186.8 +184.9 +181.0 +183.4 +181.3 +180.1 +188.2 +204.3 +180.1 +185.8 +184.9 +187.4 +176.0 +184.2 +177.6 +179.0 +178.9 +188.3 +179.8 +180.9 +177.5 +188.9 +191.1 +184.7 +189.5 +178.4 +189.3 +184.7 +186.2 +178.9 +176.6 +180.8 +177.3 +183.3 +180.0 +188.8 +186.8 +179.5 +179.6 +181.6 +177.4 +176.5 +178.0 +182.6 +184.7 +189.3 +183.6 +180.6 +186.6 +180.7 +185.6 +178.2 +176.3 +181.2 +177.6 +175.7 +177.3 +184.0 +178.2 +182.9 +201.6 +175.0 +179.3 +181.6 +176.2 +180.4 +176.0 +183.7 +177.5 +183.5 +193.5 +192.7 +179.0 +182.2 +179.6 +179.3 +176.8 +177.9 +177.2 +176.5 +177.4 +177.9 +186.5 +178.9 +181.0 +178.0 +176.6 +176.5 +179.2 +187.2 +178.2 +184.6 +178.9 +177.8 +182.1 +187.2 +188.8 +184.5 +181.0 +188.4 +193.1 +178.7 +186.2 +181.1 +183.2 +181.4 +177.6 +176.7 +177.1 +179.0 +181.0 +177.9 +183.5 +181.6 +178.9 +179.4 +175.8 +175.6 +179.0 +180.1 +175.9 +179.9 +178.1 +186.0 +208.0 +178.6 +182.7 +186.3 +174.5 +178.6 +184.3 +185.4 +178.3 +178.5 +190.5 +180.8 +188.2 +177.3 +190.1 +175.5 +188.5 +185.4 +179.6 +181.4 +177.4 +217.5 +196.9 +180.8 +179.9 +192.5 +178.0 +178.4 +175.8 +175.3 +184.8 +197.2 +186.0 +176.9 +176.5 +178.4 +180.8 +185.5 +203.5 +183.3 +180.0 +177.6 +181.2 +180.2 +179.7 +194.4 +180.8 +181.3 +176.9 +187.2 +178.5 +178.7 +185.4 +183.0 +181.3 +261.7 +178.1 +175.9 +175.9 +189.8 +185.9 +184.1 +181.7 +182.7 +177.6 +176.6 +183.6 +178.4 +178.5 +185.8 +184.5 +178.5 +178.0 +181.7 +177.4 +179.3 +177.5 +175.7 +176.6 +176.4 +180.8 +184.9 +177.6 +178.0 +178.7 +178.0 +177.8 +176.7 +183.6 +196.4 +182.4 +182.2 +178.0 +175.9 +179.2 +175.6 +178.3 +176.2 +178.8 +183.1 +175.2 +182.4 +180.6 +177.6 +176.6 +180.2 +180.4 +181.1 +176.2 +188.7 +187.2 +181.6 +181.5 +177.2 +176.9 +177.4 +176.3 +185.2 +178.3 +184.6 +178.3 +189.7 +179.5 +201.1 +176.2 +185.5 +182.2 +183.4 +174.5 +176.8 +180.1 +179.8 +182.2 +178.8 +186.5 +188.2 +180.4 +182.1 +182.8 +194.7 +182.3 +177.6 +176.2 +180.7 +193.6 +178.9 +177.8 +178.7 +179.8 +176.3 +180.8 +184.2 +181.5 +179.2 +178.2 +179.9 +176.7 +180.0 +183.0 +180.6 +177.3 +186.7 +180.8 +181.5 +190.3 +186.8 +184.2 +180.4 +181.6 +182.2 +183.0 +173.3 +187.5 +185.7 +180.1 +177.8 +189.2 +194.9 +176.8 +194.9 +187.0 +209.2 +185.4 +177.7 +178.0 +175.4 +178.0 +183.1 +177.9 +182.4 +177.2 +177.7 +178.9 +183.5 +178.1 +179.2 +191.5 +182.6 +179.8 +182.5 +176.9 +180.2 +176.0 +178.6 +184.7 +279.2 +179.0 +176.5 +180.0 +182.9 +178.3 +188.1 +179.6 +177.8 +175.6 +180.0 +178.6 +181.0 +184.0 +178.2 +191.6 +176.8 +187.7 +179.1 +183.3 +203.5 +178.7 +178.6 +175.4 +187.5 +179.7 +181.6 +177.9 +180.5 +186.7 +182.3 +187.9 +182.2 +187.8 +181.1 +266.6 +186.3 +185.9 +178.0 +180.6 +176.9 +176.5 +177.0 +184.1 +182.7 +179.0 +182.6 +180.8 +179.9 +179.0 +183.1 +178.6 +176.4 +177.3 +176.4 +180.2 +177.2 +177.0 +177.5 +178.3 +175.2 +177.8 +176.7 +181.4 +177.7 +177.1 +176.5 +180.8 +177.2 +178.1 +177.9 +187.1 +183.8 +187.0 +184.4 +180.2 +179.2 +179.2 +178.6 +188.0 +184.7 +178.0 +176.3 +175.3 +181.6 +191.8 +176.9 +182.0 +183.3 +177.5 +185.0 +181.5 +182.6 +179.0 +188.4 +203.6 +184.3 +183.8 +188.0 +189.7 +184.7 +185.9 +195.0 +185.3 +178.8 +190.6 +181.6 +180.1 +187.1 +182.3 +176.8 +179.1 +185.8 +180.1 +180.3 +182.7 +185.3 +175.6 +182.8 +188.5 +175.6 +182.7 +182.0 +179.5 +180.9 +179.4 +183.2 +182.7 +182.5 +182.0 +190.1 +199.7 +177.8 +177.6 +180.9 +181.6 +183.3 +186.1 +188.0 +184.9 +185.4 +182.6 +182.5 +178.7 +185.6 +175.4 +176.7 +182.5 +180.6 +178.3 +179.7 +184.4 +180.9 +184.5 +187.2 +181.6 +192.7 +185.9 +179.6 +184.3 +182.3 +175.0 +180.3 +177.2 +177.9 +178.4 +179.7 +174.6 +190.4 +193.1 +186.8 +180.6 +176.0 +177.4 +188.4 +179.2 +180.9 +178.7 +180.9 +178.2 +176.6 +185.0 +182.1 +181.7 +175.2 +175.1 +183.2 +192.0 +188.3 +182.1 +176.7 +177.6 +177.3 +192.6 +189.3 +178.2 +177.5 +183.1 +183.4 +181.2 +179.5 +175.4 +176.8 +178.2 +175.8 +179.4 +184.7 +184.6 +191.9 +177.4 +176.7 +179.3 +181.6 +183.3 +176.6 +218.6 +175.7 +174.4 +186.4 +185.1 +176.0 +195.2 +180.3 +178.1 +179.2 +178.0 +181.5 +182.9 +179.0 +191.3 +185.1 +186.5 +177.2 +180.3 +191.0 +175.7 +179.8 +182.0 +181.6 +185.0 +183.2 +174.8 +184.5 +182.1 +178.7 +186.7 +186.9 +176.5 +184.5 +188.9 +180.4 +190.1 +181.4 +188.0 +193.5 +187.7 +180.0 +183.5 +176.7 +177.5 +179.2 +206.2 +178.4 +183.2 +181.7 +200.9 +193.1 +176.1 +181.8 +183.6 +193.5 +201.7 +188.0 +178.9 +180.5 +180.8 +176.5 +178.0 +180.6 +181.4 +184.7 +192.4 +177.4 +182.4 +202.8 +189.7 +177.3 +181.5 +178.7 +180.0 +180.6 +178.0 +178.3 +183.9 +177.4 +180.0 +189.7 +175.4 +185.2 +179.3 +184.1 +177.5 +182.1 +174.8 +179.8 +187.5 +216.9 +182.9 +183.5 +203.3 +181.5 +184.0 +181.3 +183.4 +180.3 +176.1 +180.3 +180.1 +180.2 +181.2 +182.3 +176.2 +189.5 +219.7 +180.1 +176.6 +177.3 +178.8 +181.2 +175.1 +183.3 +182.1 +178.7 +180.9 +183.3 +188.1 +225.2 +185.8 +184.3 +187.5 +187.3 +190.7 +181.0 +180.2 +184.6 +177.9 +180.1 +187.6 +181.1 +177.5 +184.5 +176.5 +182.7 +174.5 +183.2 +176.5 +183.0 +177.0 +179.2 +182.9 +184.9 +176.4 +179.9 +183.2 +178.1 +177.0 +205.5 +182.5 +175.2 +185.0 +182.7 +178.2 +189.0 +190.5 +178.8 +183.5 +185.1 +181.3 +181.3 +194.5 +187.8 +184.9 +177.2 +179.6 +195.1 +181.2 +188.8 +180.4 +180.8 +180.0 +177.6 +198.3 +180.6 +179.5 +178.4 +179.3 +177.5 +176.8 +181.6 +183.8 +181.8 +175.6 +181.0 +178.4 +179.4 +179.8 +181.3 +181.1 +193.3 +180.1 +182.8 +179.3 +181.1 +188.3 +177.6 +178.5 +178.8 +177.1 +179.2 +181.6 +185.6 +191.8 +176.3 +187.8 +184.5 +197.3 +183.1 +179.7 +178.1 +181.3 +177.6 +178.2 +179.5 +177.5 +185.4 +184.4 +186.4 +177.4 +178.0 +179.7 +177.9 +184.6 +189.1 +184.5 +197.2 +192.6 +188.0 +186.5 +177.3 +183.7 +183.7 +192.8 +181.2 +199.4 +179.7 +187.7 +192.8 +208.5 +184.1 +175.6 +185.3 +188.6 +183.7 +180.7 +182.1 +176.0 +183.1 +179.6 +175.7 +187.0 +179.9 +184.8 +179.4 +187.6 +181.6 +201.1 +193.5 +186.2 +190.3 +197.9 +208.2 +192.9 +193.1 +184.9 +210.5 +178.6 +198.2 +177.9 +192.2 +181.6 +185.1 +189.1 +188.3 +204.7 +186.2 +179.6 +189.2 +176.9 +180.5 +183.3 +193.1 +178.4 +203.8 +185.2 +180.6 +183.3 +186.7 +188.8 +184.4 +181.7 +186.4 +185.8 +180.1 +182.7 +179.8 +198.9 +180.4 +178.3 +195.6 +184.0 +179.5 +181.8 +187.4 +181.7 +181.8 +175.1 +200.0 +181.6 +203.0 +176.0 +184.9 +199.3 +178.2 +184.3 +176.6 +192.9 +188.1 +185.7 +181.0 +179.5 +183.4 +186.7 +178.6 +183.2 +179.6 +182.6 +177.3 +179.0 +197.0 +182.0 +174.8 +179.4 +180.7 +210.3 +211.3 +235.3 +255.7 +275.7 +286.8 +306.7 +347.2 +381.2 +184.5 +186.7 +190.1 +193.3 +177.9 +188.0 +184.0 +180.6 +178.5 +235.8 +179.6 +178.1 +196.0 +180.5 +179.0 +185.8 +230.8 +211.1 +178.3 +179.4 +186.5 +180.6 +181.4 +183.2 +188.7 +184.8 +175.0 +179.2 +185.4 +180.8 +182.7 +183.1 +177.3 +177.4 +181.3 +190.9 +182.7 +198.2 +180.9 +200.5 +188.4 +207.5 +190.2 +178.9 +184.1 +193.6 +321.3 +183.8 +181.4 +174.6 +185.1 +198.9 +202.2 +183.3 +187.3 +183.0 +184.5 +178.3 +183.8 +176.7 +178.3 +186.0 +181.0 +176.0 +181.3 +202.8 +179.7 +180.3 +191.6 +176.8 +182.6 +183.7 +181.3 +186.2 +205.7 +186.4 +181.9 +189.9 +188.9 +193.6 +203.2 +212.3 +178.9 +181.5 +189.6 +178.9 +177.6 +189.0 +177.3 +177.2 +178.0 +178.5 +177.9 +197.8 +182.0 +185.7 +193.6 +185.8 +183.1 +183.1 +192.7 +183.0 +187.0 +193.4 +186.9 +182.3 +183.9 +201.6 +199.5 +182.7 +184.6 +181.8 +179.1 +189.8 +183.1 +187.8 +185.7 +192.9 +179.3 +179.7 +180.2 +177.6 +180.8 +178.0 +190.2 +177.5 +199.2 +192.7 +181.2 +185.2 +183.5 +198.1 +178.9 +183.7 +191.6 +187.4 +180.5 +211.9 +206.2 +183.2 +182.1 +176.7 +187.4 +176.6 +183.2 +195.5 +178.7 +180.7 +181.2 +189.1 +187.8 +183.1 +181.9 +186.3 +197.1 +184.7 +177.2 +180.5 +179.5 +181.6 +177.9 +187.7 +196.2 +189.5 +180.5 +183.3 +180.6 +186.7 +186.2 +177.7 +192.1 +182.4 +177.5 +179.7 +193.6 +200.1 +179.9 +188.4 +182.3 +190.4 +179.4 +182.4 +181.7 +195.7 +177.2 +181.3 +182.9 +188.3 +183.6 +180.3 +192.7 +183.1 +210.5 +185.1 +183.4 +191.6 +187.9 +183.1 +175.5 +177.4 +183.7 +182.9 +188.5 +180.7 +186.2 +182.4 +186.3 +182.4 +188.1 +180.2 +195.4 +181.8 +192.9 +176.3 +177.7 +178.8 +179.4 +182.6 +180.3 +187.4 +185.3 +183.5 +180.5 +180.8 +182.0 +178.9 +176.2 +197.0 +181.5 +177.1 +189.1 +188.0 +176.5 +181.7 +184.9 +192.2 +181.5 +189.0 +204.6 +177.5 +176.6 +202.2 +176.9 +181.9 +177.9 +180.6 +197.2 +175.9 +175.6 +183.4 +182.7 +191.2 +178.8 +187.2 +178.9 +192.4 +178.6 +178.4 +184.5 +216.4 +190.7 +182.7 +181.6 +199.5 +182.0 +179.1 +189.1 +180.3 +182.0 +185.6 +196.1 +182.5 +180.4 +190.9 +178.9 +186.3 +209.4 +181.7 +186.6 +192.0 +200.0 +205.7 +191.0 +186.0 +185.5 +176.2 +191.4 +184.4 +186.1 +186.6 +180.0 +180.8 +178.0 +180.0 +198.8 +183.9 +183.5 +176.4 +176.7 +181.4 +186.4 +186.8 +186.0 +179.5 +187.2 +179.5 +185.2 +179.4 +183.6 +188.6 +179.8 +179.0 +184.4 +178.4 +177.4 +195.2 +207.0 +184.3 +179.6 +183.1 +184.0 +187.2 +181.4 +178.4 +182.7 +189.4 +183.1 +179.7 +181.8 +181.5 +181.9 +182.6 +178.2 +189.2 +189.1 +190.3 +184.1 +195.3 +176.1 +177.3 +186.8 +183.7 +188.5 +179.7 +177.4 +181.4 +180.7 +206.9 +179.2 +177.8 +180.5 +180.6 +191.9 +181.7 +190.3 +183.5 +186.2 +175.8 +179.1 +181.6 +181.6 +183.0 +179.1 +179.3 +183.7 +181.2 +185.2 +176.8 +177.8 +175.3 +184.6 +179.2 +176.9 +188.0 +184.6 +184.6 +180.9 +189.9 +396.3 +176.1 +183.1 +179.2 +184.6 +181.5 +181.3 +176.2 +177.4 +174.3 +179.4 +178.8 +184.6 +182.1 +183.4 +178.4 +184.1 +182.7 +181.4 +177.7 +185.0 +177.2 +179.4 +183.5 +183.1 +187.6 +181.1 +179.7 +189.6 +183.6 +177.1 +181.5 +193.1 +196.8 +180.2 +177.3 +178.8 +176.6 +177.8 +190.0 +176.3 +180.1 +177.5 +187.2 +186.7 +176.5 +179.6 +177.2 +177.5 +196.4 +181.9 +179.9 +178.2 +181.5 +177.5 +179.9 +183.1 +181.5 +181.8 +177.4 +177.6 +174.5 +178.4 +183.8 +181.6 +186.4 +178.1 +177.3 +179.1 +183.6 +191.2 +181.7 +187.6 +186.1 +183.5 +185.0 +186.5 +187.5 +190.5 +224.7 +177.3 +189.2 +191.7 +188.0 +182.8 +187.8 +182.2 +241.9 +197.6 +188.3 +181.7 +195.5 +184.4 +190.9 +187.4 +195.0 +190.0 +180.5 +179.1 +180.3 +195.0 +176.7 +191.8 +200.2 +180.3 +180.6 +186.1 +178.2 +182.5 +183.2 +178.9 +177.6 +185.6 +203.5 +179.7 +179.6 +190.8 +187.4 +184.7 +185.1 +195.1 +191.9 +181.1 +183.1 +177.1 +177.9 +178.5 +178.8 +186.0 +189.9 +184.7 +190.4 +192.9 +178.6 +176.1 +189.2 +186.1 +178.6 +186.9 +181.5 +181.5 +181.2 +178.1 +181.8 +187.6 +183.3 +188.5 +179.6 +183.5 +187.9 +179.6 +192.4 +182.6 +192.1 +255.4 +266.2 +191.5 +194.8 +179.7 +190.8 +182.8 +182.3 +186.7 +186.4 +178.1 +182.5 +175.0 +187.7 +199.4 +182.1 +180.5 +182.9 +175.9 +219.6 +207.0 +196.5 +190.7 +199.1 +185.4 +174.5 +180.5 +182.7 +181.8 +189.2 +181.0 +198.2 +191.9 +185.3 +182.9 +188.5 +178.7 +179.2 +188.7 +188.5 +178.2 +197.6 +178.3 +192.7 +181.9 +191.2 +181.7 +176.1 +196.5 +187.7 +190.5 +187.8 +182.9 +189.9 +192.3 +177.4 +184.7 +178.3 +185.8 +182.0 +180.1 +183.1 +182.0 +176.1 +185.2 +196.0 +185.5 +179.1 +198.1 +197.5 +182.0 +177.0 +178.3 +202.9 +177.0 +180.6 +181.6 +191.6 +179.6 +180.9 +183.7 +188.4 +182.6 +185.7 +191.5 +178.6 +188.4 +184.0 +183.8 +189.3 +185.2 +192.9 +195.4 +197.1 +190.4 +186.5 +196.0 +189.8 +191.9 +187.3 +185.7 +198.0 +175.8 +183.7 +176.0 +179.9 +183.1 +186.7 +177.9 +179.2 +181.7 +184.9 +181.6 +178.5 +176.5 +176.7 +178.4 +184.7 +181.3 +182.3 +182.8 +182.6 +190.2 +177.0 +187.9 +188.1 +186.4 +187.8 +181.3 +187.7 +191.8 +182.5 +182.0 +180.1 +185.8 +177.8 +192.5 +185.2 +179.7 +179.3 +191.0 +181.3 +192.9 +180.3 +194.5 +187.6 +184.3 +194.7 +182.7 +177.7 +181.2 +177.2 +184.0 +181.8 +188.5 +187.8 +184.7 +181.7 +184.6 +177.4 +193.0 +178.7 +176.5 +176.9 +180.9 +180.8 +187.8 +179.8 +175.2 +177.5 +180.5 +186.6 +177.0 +177.8 +177.8 +182.5 +180.0 +182.4 +188.6 +192.5 +177.5 +179.8 +178.8 +178.9 +182.0 +180.6 +184.5 +179.9 +175.4 +183.1 +177.8 +177.2 +191.7 +184.6 +183.1 +175.6 +200.6 +221.5 +177.2 +177.6 +184.0 +183.4 +199.2 +184.6 +186.1 +180.3 +188.1 +184.1 +185.9 +178.0 +183.7 +186.7 +186.1 +177.8 +183.5 +178.0 +177.0 +185.6 +187.0 +178.9 +178.9 +200.5 +180.2 +181.6 +187.8 +179.8 +182.0 +184.3 +176.3 +182.8 +184.3 +181.9 +185.3 +189.2 +177.3 +183.6 +186.7 +184.0 +243.9 +175.9 +184.3 +176.0 +179.6 +178.5 +182.3 +189.7 +179.6 +190.3 +188.8 +175.2 +179.8 +181.0 +175.6 +175.3 +187.5 +175.3 +186.8 +176.6 +177.3 +175.7 +178.5 +178.8 +178.5 +178.9 +183.7 +182.2 +182.3 +178.9 +182.0 +178.6 +184.2 +178.8 +176.7 +176.9 +176.2 +181.1 +177.9 +177.2 +178.6 +194.8 +185.4 +188.3 +178.9 +187.1 +184.3 +184.2 +190.3 +193.8 +185.0 +181.0 +181.4 +181.0 +191.8 +177.4 +178.6 +179.6 +180.2 +179.0 +187.4 +177.1 +182.7 +181.9 +177.9 +184.8 +195.1 +181.2 +182.1 +187.4 +186.8 +184.7 +181.9 +188.7 +191.8 +184.7 +185.5 +183.5 +180.8 +206.1 +179.1 +194.3 +178.0 +177.4 +181.0 +181.7 +190.6 +189.3 +176.3 +183.5 +178.9 +179.4 +187.3 +182.5 +181.7 +183.3 +188.9 +181.3 +195.6 +180.4 +194.0 +182.6 +176.6 +190.8 +176.3 +186.1 +185.1 +181.5 +179.2 +178.3 +179.6 +181.5 +181.0 +183.2 +182.6 +202.6 +183.7 +188.2 +200.6 +177.9 +184.2 +192.0 +180.9 +177.3 +177.2 +188.1 +184.2 +199.4 +185.3 +189.7 +199.8 +176.6 +189.2 +186.3 +182.8 +180.0 +190.8 +188.5 +194.6 +184.8 +202.7 +189.8 +183.2 +206.2 +175.3 +202.1 +193.5 +210.1 +200.5 +194.8 +190.0 +189.3 +182.5 +183.3 +187.1 +186.1 +208.1 +191.4 +207.5 +188.6 +176.7 +190.8 +189.1 +177.2 +189.3 +177.3 +196.2 +195.3 +188.0 +194.1 +181.3 +177.8 +186.2 +184.4 +182.0 +182.5 +187.1 +179.8 +177.4 +184.0 +180.5 +182.9 +176.4 +175.9 +176.8 +182.8 +208.0 +196.3 +183.4 +178.5 +189.7 +183.3 +182.5 +182.7 +182.5 +195.7 +183.8 +182.6 +190.0 +191.0 +191.7 +177.2 +184.1 +202.9 +189.8 +185.0 +183.3 +194.2 +182.8 +178.9 +184.9 +189.1 +192.8 +181.1 +190.5 +205.2 +179.1 +191.3 +186.8 +188.9 +178.8 +188.0 +184.1 +183.1 +182.1 +181.9 +191.6 +183.4 +189.9 +180.2 +186.6 +180.3 +182.7 +184.8 +178.4 +180.3 +193.7 +181.5 +186.6 +179.1 +184.3 +192.1 +189.5 +182.2 +296.4 +194.8 +179.0 +183.4 +194.3 +199.5 +182.7 +181.0 +181.8 +186.9 +186.4 +188.3 +179.2 +190.1 +184.9 +187.8 +186.3 +183.4 +183.1 +191.3 +188.5 +188.5 +187.9 +188.7 +175.3 +178.4 +197.5 +185.3 +201.3 +190.9 +210.1 +179.0 +176.3 +179.4 +195.0 +179.0 +176.4 +188.5 +180.5 +179.5 +179.4 +177.5 +185.2 +179.9 +178.3 +183.8 +193.9 +183.1 +187.7 +192.7 +181.3 +184.1 +182.5 +178.2 +184.0 +195.4 +179.7 +191.7 +183.7 +185.6 +188.4 +195.0 +184.8 +181.8 +186.2 +177.4 +189.8 +192.1 +192.9 +187.5 +191.5 +189.1 +195.7 +188.0 +192.7 +190.1 +190.1 +196.3 +192.4 +193.1 +178.9 +181.9 +179.2 +188.4 +179.2 +187.5 +179.7 +185.3 +177.2 +180.5 +182.2 +203.8 +186.1 +181.7 +180.8 +187.9 +177.4 +189.5 +189.3 +176.5 +179.2 +190.5 +180.0 +177.1 +183.7 +188.8 +180.6 +220.6 +190.9 +197.9 +182.8 +183.0 +177.9 +182.2 +182.4 +201.5 +181.1 +177.8 +189.6 +193.4 +180.3 +180.2 +175.3 +177.8 +178.3 +178.8 +190.7 +181.8 +185.1 +190.2 +179.1 +179.9 +179.8 +180.6 +181.6 +180.8 +184.6 +184.9 +181.7 +176.3 +182.2 +186.9 +227.1 +184.9 +184.5 +177.5 +180.2 +187.5 +186.1 +183.8 +177.4 +184.1 +188.1 +180.4 +197.4 +209.6 +198.3 +179.5 +183.9 +183.5 +181.8 +178.9 +195.5 +184.7 +191.3 +193.5 +196.8 +197.0 +184.2 +181.9 +187.5 +188.7 +177.7 +186.7 +191.6 +192.8 +194.8 +193.6 +194.7 +187.6 +197.2 +187.5 +176.0 +194.4 +199.9 +188.2 +184.5 +183.2 +186.0 +183.6 +210.5 +183.7 +190.4 +188.6 +179.8 +183.9 +210.2 +194.0 +190.8 +188.3 +186.2 +178.0 +178.3 +194.4 +184.5 +182.3 +179.1 +183.4 +182.9 +185.1 +180.6 +182.1 +189.4 +181.6 +180.5 +190.4 +182.1 +204.0 +192.7 +180.4 +190.7 +179.5 +179.2 +181.5 +186.1 +195.1 +189.7 +189.9 +185.9 +206.2 +187.5 +178.5 +206.2 +208.5 +187.4 +182.0 +192.9 +193.0 +196.5 +190.1 +179.7 +205.9 +187.7 +211.5 +177.5 +185.1 +205.3 +178.9 +179.6 +178.8 +185.9 +180.1 +179.0 +178.2 +181.9 +182.9 +191.5 +194.8 +184.9 +177.9 +186.0 +178.7 +175.2 +178.1 +183.9 +176.2 +196.3 +195.0 +214.4 +177.2 +175.2 +181.4 +191.4 +189.0 +179.6 +181.8 +177.8 +175.9 +176.1 +183.8 +182.1 +178.4 +182.1 +180.6 +177.4 +178.9 +178.7 +182.0 +176.5 +182.1 +189.2 +188.3 +184.6 +177.6 +184.6 +183.6 +182.6 +181.5 +198.4 +179.6 +179.3 +185.1 +189.8 +183.5 +180.9 +175.7 +182.6 +179.1 +181.3 +181.4 +189.8 +183.7 +185.8 +185.9 +178.1 +182.3 +196.3 +220.0 +199.4 +191.3 +186.7 +181.7 +180.7 +180.9 +176.2 +185.3 +180.3 +182.5 +177.5 +182.4 +184.8 +191.3 +185.5 +184.4 +193.8 +186.5 +178.9 +180.6 +182.7 +180.1 +184.8 +179.7 +187.8 +183.7 +190.9 +192.5 +205.5 +193.1 +180.7 +186.4 +179.3 +194.2 +187.8 +190.6 +178.8 +176.7 +181.2 +187.6 +191.3 +182.4 +204.9 +191.0 +193.8 +194.0 +198.4 +181.7 +186.5 +186.4 +182.5 +182.2 +179.0 +180.6 +180.0 +181.1 +178.6 +178.6 +183.3 +189.1 +179.9 +190.2 +187.3 +185.3 +185.0 +185.9 +189.3 +182.6 +183.5 +190.8 +183.4 +185.2 +197.1 +187.5 +182.2 +180.4 +181.3 +178.8 +182.5 +183.2 +174.8 +180.4 +200.9 +181.5 +185.5 +176.6 +179.0 +187.4 +173.8 +192.6 +179.3 +188.7 +198.9 +177.8 +185.5 +177.1 +213.1 +185.7 +189.7 +186.6 +189.2 +188.1 +184.0 +184.5 +178.7 +183.9 +186.3 +199.0 +188.2 +191.9 +181.6 +177.4 +183.8 +179.2 +182.7 +182.1 +178.2 +187.3 +189.0 +188.1 +190.8 +184.1 +188.0 +178.9 +195.8 +179.3 +204.6 +183.3 +178.8 +178.8 +176.9 +179.9 +186.0 +181.6 +199.5 +179.6 +189.0 +204.2 +189.7 +181.2 +184.5 +193.4 +183.7 +180.2 +186.2 +191.0 +195.1 +179.9 +182.6 +188.7 +184.5 +178.9 +194.1 +191.5 +179.9 +198.8 +183.4 +212.7 +179.0 +181.8 +176.5 +181.9 +179.7 +186.2 +187.0 +181.6 +184.9 +199.7 +185.4 +186.1 +179.3 +200.6 +187.7 +195.2 +185.5 +182.2 +189.4 +184.6 +177.9 +182.5 +182.9 +191.4 +192.5 +186.2 +178.4 +197.8 +175.7 +183.2 +187.5 +192.9 +175.8 +195.1 +185.7 +188.6 +191.6 +177.0 +181.8 +188.2 +202.4 +183.8 +184.5 +198.6 +186.4 +180.7 +188.6 +181.4 +178.9 +186.5 +185.5 +187.7 +187.7 +179.1 +184.2 +185.1 +178.0 +182.0 +197.0 +194.9 +193.6 +177.0 +186.6 +179.8 +186.1 +177.7 +182.1 +181.3 +178.0 +184.8 +208.3 +193.1 +187.2 +207.7 +188.2 +186.1 +178.1 +193.3 +182.1 +199.8 +182.7 +196.4 +197.9 +178.8 +191.4 +180.9 +180.5 +184.6 +177.2 +182.4 +183.7 +178.2 +187.1 +178.5 +181.3 +190.5 +179.0 +181.0 +178.0 +185.5 +179.1 +184.9 +184.7 +177.7 +176.7 +183.5 +196.3 +184.2 +190.0 +189.4 +186.0 +181.7 +186.0 +188.7 +187.5 +184.6 +176.3 +177.4 +185.9 +183.4 +179.4 +175.8 +186.1 +178.8 +179.0 +181.6 +175.2 +177.4 +176.8 +177.9 +197.6 +184.9 +176.0 +177.6 +178.4 +184.6 +178.1 +183.2 +180.2 +191.3 +180.0 +176.2 +180.3 +181.8 +187.4 +181.7 +177.1 +182.9 +190.6 +183.9 +177.9 +187.4 +174.9 +186.4 +192.0 +190.2 +198.4 +183.6 +206.3 +184.2 +181.0 +195.6 +277.2 +189.9 +187.0 +196.6 +180.6 +183.0 +208.8 +193.1 +188.9 +186.4 +179.9 +181.7 +185.2 +189.5 +188.0 +181.2 +182.5 +183.5 +190.3 +179.3 +182.3 +179.5 +184.2 +193.3 +178.5 +178.5 +182.3 +184.7 +181.9 +185.0 +181.6 +180.0 +202.6 +178.9 +176.4 +185.0 +190.3 +179.4 +182.9 +194.1 +185.2 +198.6 +177.6 +181.1 +179.8 +183.1 +187.7 +187.5 +185.9 +191.2 +183.1 +184.6 +193.0 +197.6 +183.3 +184.4 +181.6 +184.9 +178.0 +177.4 +181.0 +178.9 +180.4 +181.4 +188.0 +178.2 +181.2 +179.0 +188.1 +176.0 +176.4 +179.6 +179.1 +182.2 +185.6 +176.2 +180.0 +195.4 +177.4 +185.2 +177.5 +181.3 +187.8 +176.7 +200.8 +179.8 +183.8 +179.9 +190.4 +180.3 +178.5 +182.7 +180.3 +179.1 +178.8 +184.3 +176.7 +181.8 +179.9 +175.7 +181.6 +184.5 +183.3 +214.3 +175.7 +188.8 +180.1 +179.9 +182.8 +175.0 +177.4 +217.5 +183.7 +178.3 +184.0 +182.2 +175.4 +184.0 +181.2 +184.5 +174.6 +176.0 +180.1 +173.6 +190.4 +177.5 +181.3 +176.1 +181.5 +199.2 +176.2 +186.0 +178.6 +186.9 +187.1 +172.8 +175.5 +182.1 +175.9 +200.4 +193.1 +178.0 +177.1 +180.6 +182.2 +179.0 +175.8 +182.0 +188.0 +180.1 +207.1 +187.0 +180.6 +183.8 +192.9 +175.7 +193.6 +178.3 +182.2 +179.9 +177.2 +181.3 +177.0 +201.1 +176.2 +175.5 +183.1 +181.3 +176.0 +187.7 +185.7 +179.2 +183.4 +184.8 +176.3 +179.3 +175.2 +185.4 +181.2 +177.2 +179.9 +175.1 +175.7 +193.8 +179.7 +184.6 +181.2 +187.8 +184.9 +182.3 +184.9 +181.7 +186.0 +187.0 +178.5 +174.9 +180.7 +188.9 +181.4 +181.3 +176.2 +182.0 +183.4 +181.3 +181.3 +175.0 +179.7 +186.6 +183.7 +185.1 +180.8 +179.6 +180.6 +193.6 +187.6 +181.0 +176.3 +191.0 +177.5 +186.9 +173.8 +184.1 +178.1 +183.2 +180.2 +194.2 +175.8 +183.6 +175.4 +176.6 +183.7 +177.9 +176.4 +180.8 +179.9 +177.9 +183.0 +176.9 +181.3 +182.2 +182.3 +183.8 +181.1 +180.6 +189.9 +185.6 +184.5 +185.3 +175.3 +192.4 +195.5 +188.2 +180.3 +196.3 +184.1 +178.7 +179.7 +187.0 +177.1 +180.7 +177.5 +177.1 +180.8 +182.0 +177.0 +177.0 +193.1 +194.6 +190.0 +183.5 +180.5 +178.4 +177.4 +178.7 +184.6 +189.6 +188.1 +186.1 +184.2 +178.9 +176.7 +195.8 +178.1 +187.6 +175.4 +180.3 +185.3 +189.0 +177.2 +188.9 +179.0 +184.0 +179.1 +187.3 +199.7 +178.2 +179.1 +175.8 +179.8 +178.8 +196.3 +185.0 +196.3 +181.1 +185.5 +175.8 +189.2 +179.4 +192.0 +182.6 +177.9 +181.1 +179.6 +184.2 +194.6 +185.9 +181.4 +176.5 +178.3 +176.3 +179.1 +175.3 +180.5 +186.7 +177.5 +192.3 +187.7 +178.4 +189.4 +180.4 +180.5 +189.9 +183.8 +190.3 +195.5 +185.6 +177.2 +177.7 +178.7 +189.9 +183.8 +193.1 +185.2 +180.5 +180.5 +191.6 +175.0 +176.6 +178.6 +188.8 +180.7 +185.5 +181.2 +199.5 +193.2 +193.8 +185.8 +182.0 +178.5 +188.7 +190.1 +178.7 +181.5 +180.9 +186.6 +189.8 +194.4 +180.1 +193.0 +178.2 +183.2 +182.6 +177.8 +212.2 +177.5 +178.5 +181.9 +185.8 +177.2 +182.9 +176.7 +175.1 +180.4 +186.0 +179.1 +179.5 +177.2 +182.1 +177.8 +180.1 +180.1 +177.9 +183.6 +189.2 +193.2 +179.6 +176.9 +181.3 +178.4 +177.4 +189.2 +182.1 +195.2 +193.0 +184.5 +180.4 +184.1 +176.2 +180.1 +179.0 +174.8 +181.1 +190.0 +179.9 +192.2 +204.9 +179.2 +180.5 +174.7 +181.6 +181.5 +185.1 +176.7 +184.9 +191.9 +182.2 +182.7 +210.5 +186.1 +181.1 +179.3 +186.4 +176.0 +177.4 +180.9 +187.0 +192.2 +182.9 +182.2 +184.0 +180.3 +177.4 +178.7 +176.8 +176.4 +176.1 +182.0 +178.8 +175.2 +187.6 +184.9 +184.3 +188.7 +178.3 +190.9 +186.3 +175.2 +185.8 +175.6 +184.8 +181.1 +176.8 +179.0 +179.2 +196.5 +175.0 +182.6 +194.4 +173.7 +180.0 +178.3 +181.1 +179.5 +177.6 +185.1 +183.2 +188.6 +186.8 +182.5 +176.4 +175.1 +176.9 +182.4 +180.1 +181.7 +182.8 +184.1 +175.7 +178.7 +177.1 +178.3 +177.3 +176.7 +179.8 +176.8 +177.2 +200.5 +175.7 +181.1 +174.5 +179.3 +183.1 +178.4 +189.2 +186.0 +177.4 +178.3 +187.1 +181.0 +183.3 +191.4 +182.9 +182.6 +199.2 +181.4 +180.5 +180.4 +176.7 +184.4 +189.0 +188.1 +197.0 +181.2 +176.1 +181.0 +180.5 +176.5 +214.9 +178.3 +178.7 +181.4 +180.9 +178.0 +185.8 +182.5 +181.8 +177.4 +186.7 +183.2 +183.2 +183.1 +173.9 +181.1 +179.1 +180.3 +177.7 +181.5 +176.5 +177.1 +189.6 +191.5 +183.2 +180.0 +180.9 +183.3 +188.3 +178.4 +185.4 +176.1 +183.2 +181.1 +180.3 +176.4 +177.5 +186.8 +176.3 +338.9 +184.9 +177.0 +187.1 +175.8 +180.0 +182.5 +184.9 +175.2 +179.6 +176.9 +178.7 +177.5 +178.3 +178.9 +180.7 +182.6 +179.3 +177.0 +180.5 +179.2 +177.2 +188.0 +194.8 +183.6 +177.3 +179.1 +185.3 +177.5 +187.6 +180.9 +188.0 +174.8 +183.6 +183.0 +182.9 +178.1 +182.0 +185.9 +176.5 +182.1 +188.5 +179.9 +176.0 +184.3 +185.9 +179.2 +184.3 +181.3 +186.3 +186.7 +177.5 +188.9 +178.8 +211.3 +188.0 +178.5 +192.6 +176.4 +187.1 +189.5 +195.7 +188.5 +185.1 +182.5 +176.6 +182.5 +187.3 +180.5 +191.9 +178.3 +177.1 +188.3 +181.0 +178.0 +187.1 +175.1 +182.3 +177.4 +177.6 +181.2 +179.2 +183.8 +183.2 +173.3 +177.6 +178.6 +179.4 +180.5 +183.9 +176.3 +178.4 +178.9 +175.9 +179.2 +175.4 +183.5 +188.9 +176.5 +185.8 +179.7 +196.8 +185.9 +178.4 +176.4 +177.6 +176.0 +177.9 +181.7 +176.2 +180.6 +179.9 +186.0 +177.2 +178.9 +179.1 +186.6 +176.0 +194.6 +179.2 +181.5 +176.2 +177.2 +179.3 +181.3 +179.2 +181.7 +177.5 +186.6 +179.5 +177.4 +179.4 +177.9 +181.9 +181.3 +177.4 +176.0 +177.8 +185.2 +180.8 +181.3 +182.1 +178.2 +179.5 +185.6 +179.5 +182.5 +186.1 +188.7 +178.7 +186.8 +179.2 +197.7 +180.8 +181.4 +178.1 +207.1 +182.7 +185.5 +186.1 +180.1 +176.1 +177.6 +174.9 +176.1 +180.6 +184.5 +179.2 +186.1 +180.5 +179.5 +179.3 +182.2 +181.8 +184.6 +180.0 +177.1 +176.7 +177.6 +180.8 +176.5 +177.3 +176.8 +176.4 +188.7 +187.6 +178.5 +178.0 +179.3 +181.4 +183.2 +193.8 +178.3 +181.2 +180.4 +183.5 +179.8 +177.4 +183.2 +183.3 +176.6 +176.2 +216.9 +177.8 +177.0 +178.0 +175.3 +189.4 +182.6 +182.9 +183.6 +180.4 +177.2 +176.0 +177.9 +175.1 +182.1 +178.3 +178.5 +186.2 +177.7 +178.0 +183.3 +180.4 +197.6 +181.7 +179.9 +184.0 +194.8 +184.8 +189.4 +191.8 +176.6 +186.2 +180.3 +177.7 +197.0 +187.0 +188.1 +190.1 +176.8 +179.3 +185.4 +174.7 +189.6 +184.2 +178.0 +180.4 +183.4 +179.5 +177.7 +178.7 +186.9 +183.7 +177.2 +173.6 +184.7 +180.4 +183.1 +175.6 +179.6 +184.8 +185.6 +176.2 +177.7 +180.0 +174.7 +183.1 +181.6 +178.0 +178.8 +185.4 +181.2 +178.0 +188.7 +177.1 +189.3 +177.6 +180.6 +181.6 +175.9 +180.7 +179.1 +182.4 +186.7 +175.8 +185.1 +176.7 +180.3 +195.7 +191.3 +191.1 +181.3 +180.5 +179.3 +184.3 +177.1 +182.4 +179.4 +188.8 +174.5 +177.2 +178.4 +179.8 +183.3 +175.4 +184.1 +176.8 +182.4 +177.8 +178.7 +178.6 +189.6 +182.0 +191.2 +179.8 +179.3 +178.6 +183.0 +188.8 +205.3 +180.1 +190.6 +184.4 +179.9 +178.6 +174.7 +180.6 +193.3 +175.3 +175.5 +193.9 +180.4 +177.2 +175.6 +177.0 +182.3 +183.5 +181.9 +179.6 +182.8 +180.9 +180.6 +183.7 +185.0 +176.7 +190.5 +176.2 +179.0 +178.8 +180.0 +181.5 +174.3 +177.3 +179.2 +178.3 +184.7 +174.2 +177.3 +178.0 +178.9 +182.9 +189.8 +183.3 +183.0 +182.5 +176.3 +176.8 +177.5 +176.2 +183.1 +175.4 +175.0 +182.5 +174.9 +185.8 +181.4 +178.4 +183.8 +178.0 +265.0 +178.5 +185.0 +177.4 +179.2 +175.8 +183.0 +184.5 +195.3 +178.5 +178.2 +179.7 +175.0 +187.5 +182.7 +180.3 +181.0 +177.5 +181.5 +179.9 +179.0 +180.0 +182.6 +178.5 +179.6 +181.4 +188.0 +188.2 +179.1 +180.8 +178.5 +181.3 +179.9 +182.0 +181.8 +181.3 +186.6 +197.8 +205.8 +188.2 +194.9 +182.2 +184.6 +188.9 +189.4 +183.4 +179.2 +182.6 +184.6 +183.6 +184.3 +182.5 +181.1 +181.6 +178.9 +179.7 +176.3 +179.4 +180.2 +178.4 +179.6 +190.1 +178.5 +174.4 +177.3 +176.1 +175.3 +175.0 +178.9 +175.1 +181.3 +175.0 +176.0 +192.2 +189.2 +182.4 +179.2 +173.4 +177.9 +178.4 +175.5 +175.9 +199.0 +196.6 +181.7 +181.2 +177.6 +174.7 +174.3 +181.3 +178.9 +203.2 +176.3 +175.8 +192.3 +180.8 +184.8 +179.9 +182.4 +180.0 +174.3 +187.2 +181.4 +176.0 +174.4 +182.4 +186.8 +191.1 +177.1 +178.2 +176.8 +182.6 +184.6 +181.5 +185.4 +178.9 +176.6 +179.6 +175.1 +179.9 +176.9 +176.7 +186.2 +192.9 +183.5 +182.4 +175.5 +176.9 +180.6 +185.7 +174.2 +174.2 +178.9 +178.8 +179.2 +194.9 +177.5 +174.0 +179.0 +180.7 +174.0 +180.0 +178.3 +179.8 +180.0 +175.6 +174.9 +180.9 +177.3 +212.6 +196.0 +183.1 +186.5 +178.4 +185.9 +185.5 +176.8 +178.7 +174.0 +176.6 +179.0 +182.3 +176.6 +185.0 +175.7 +178.5 +176.4 +176.3 +186.3 +222.5 +185.7 +177.1 +183.6 +176.9 +184.0 +175.7 +174.4 +184.2 +269.8 +182.5 +174.6 +178.5 +176.1 +178.9 +177.9 +181.9 +176.8 +174.2 +181.6 +191.2 +179.1 +175.3 +182.2 +180.1 +175.2 +182.3 +175.6 +177.1 +175.1 +174.7 +188.2 +176.9 +183.6 +175.9 +175.5 +174.7 +182.4 +181.2 +179.5 +190.3 +184.8 +237.1 +178.3 +178.7 +184.1 +174.5 +178.3 +178.6 +181.8 +182.2 +190.2 +179.9 +181.0 +180.3 +177.2 +179.2 +179.2 +178.2 +183.0 +177.7 +180.1 +182.6 +196.6 +178.9 +177.0 +176.1 +178.3 +182.9 +176.5 +209.4 +181.1 +199.5 +186.3 +177.6 +182.7 +185.4 +180.2 +186.2 +187.4 +176.4 +182.8 +221.0 +179.0 +178.9 +178.4 +186.8 +174.1 +174.9 +176.8 +190.0 +178.9 +182.4 +178.7 +177.6 +179.4 +173.6 +178.1 +179.8 +177.8 +177.5 +187.5 +209.5 +178.0 +177.2 +179.3 +179.8 +179.8 +174.4 +177.1 +180.2 +182.5 +181.1 +184.7 +179.6 +193.0 +182.7 +181.6 +177.7 +178.2 +181.6 +186.6 +177.2 +178.1 +189.3 +174.3 +180.2 +186.2 +189.8 +185.9 +176.8 +178.8 +175.1 +175.5 +179.1 +180.9 +186.1 +187.0 +186.2 +187.6 +187.5 +197.8 +180.4 +196.7 +176.5 +177.6 +176.2 +180.8 +175.9 +182.8 +178.8 +184.6 +179.5 +175.6 +187.5 +175.7 +201.6 +188.2 +183.4 +184.2 +178.7 +177.0 +174.9 +181.9 +177.6 +177.7 +179.6 +176.0 +178.3 +181.5 +177.5 +184.7 +183.9 +180.9 +178.9 +177.8 +176.6 +188.4 +191.0 +189.6 +178.1 +177.9 +186.1 +178.7 +184.3 +176.5 +179.6 +174.1 +184.4 +176.8 +182.3 +179.5 +188.8 +180.2 +176.1 +198.4 +178.4 +188.6 +191.0 +180.4 +179.5 +176.0 +188.1 +189.6 +192.6 +177.0 +189.1 +177.5 +178.8 +186.8 +178.6 +180.5 +184.0 +178.7 +179.0 +176.2 +176.7 +185.2 +192.3 +178.0 +175.7 +181.0 +183.0 +175.7 +174.6 +176.7 +177.8 +179.0 +177.4 +176.7 +182.5 +194.0 +181.4 +178.7 +176.2 +176.7 +204.2 +178.3 +190.6 +188.5 +177.5 +180.6 +178.1 +175.2 +276.8 +353.6 +297.8 +235.1 +211.7 +213.4 +213.5 +191.5 +178.8 +186.2 +175.4 +178.2 +184.5 +179.9 +182.0 +179.3 +178.6 +192.5 +177.7 +179.2 +194.9 +183.1 +178.6 +179.9 +192.5 +184.1 +186.6 +181.2 +175.4 +197.2 +179.6 +184.6 +197.6 +180.6 +189.6 +175.3 +185.6 +174.5 +178.9 +174.2 +179.6 +173.9 +182.5 +185.6 +177.4 +191.4 +183.8 +177.0 +181.2 +176.3 +177.3 +173.7 +177.5 +178.9 +178.2 +177.3 +177.7 +187.8 +181.6 +178.4 +176.4 +180.8 +178.5 +189.5 +186.0 +188.4 +182.2 +184.0 +176.5 +179.5 +185.3 +173.8 +190.0 +180.2 +189.1 +184.9 +175.8 +182.2 +177.9 +176.4 +184.1 +179.5 +181.8 +175.7 +179.7 +178.7 +185.9 +180.5 +180.9 +175.5 +176.1 +174.4 +175.1 +195.2 +174.9 +190.5 +183.8 +188.7 +182.5 +179.5 +177.0 +174.9 +185.6 +180.8 +192.8 +185.2 +184.0 +177.7 +191.7 +185.3 +185.4 +186.6 +182.9 +181.1 +191.2 +183.0 +176.6 +187.7 +181.9 +179.5 +178.7 +176.9 +178.5 +182.1 +175.4 +175.9 +183.8 +180.6 +191.1 +177.0 +184.6 +188.7 +176.0 +181.4 +188.2 +181.1 +180.0 +178.9 +182.1 +179.6 +179.5 +174.5 +177.6 +179.9 +175.3 +185.2 +175.4 +185.1 +184.9 +182.0 +178.4 +177.0 +182.3 +193.8 +181.6 +197.4 +180.4 +180.9 +182.0 +181.0 +185.8 +178.5 +180.5 +182.3 +175.7 +176.5 +182.8 +176.8 +178.3 +179.5 +181.8 +175.1 +180.2 +179.6 +176.7 +174.6 +180.5 +179.7 +178.9 +177.1 +184.2 +178.4 +175.0 +183.7 +178.3 +179.1 +175.3 +187.5 +189.2 +176.7 +205.6 +179.6 +179.6 +179.1 +182.1 +178.6 +182.2 +181.6 +181.9 +188.8 +184.7 +185.3 +182.5 +174.2 +176.5 +190.1 +180.6 +182.7 +182.2 +179.5 +179.0 +184.4 +175.4 +178.2 +174.6 +179.0 +188.4 +175.9 +173.8 +180.2 +187.3 +186.3 +176.3 +174.9 +178.6 +178.2 +177.0 +180.0 +177.2 +173.5 +195.4 +180.5 +181.6 +177.2 +176.7 +175.3 +174.8 +179.7 +175.0 +176.4 +181.8 +183.0 +180.0 +177.0 +185.8 +183.2 +181.0 +176.8 +177.9 +186.1 +182.5 +174.4 +177.1 +176.7 +176.3 +180.3 +178.9 +178.3 +175.8 +179.9 +190.6 +182.8 +183.4 +177.9 +177.7 +180.0 +202.0 +184.5 +179.5 +181.5 +174.8 +194.2 +185.8 +177.0 +183.9 +177.6 +202.4 +179.9 +175.4 +174.8 +176.7 +177.9 +176.6 +176.5 +181.2 +181.3 +177.4 +179.3 +177.8 +176.2 +197.2 +180.9 +178.2 +184.6 +184.7 +191.0 +182.9 +179.9 +180.3 +177.4 +182.1 +181.9 +182.2 +174.9 +193.4 +175.1 +203.2 +176.0 +180.3 +180.0 +186.3 +174.8 +177.9 +196.1 +177.6 +183.5 +176.2 +187.6 +178.5 +180.6 +178.2 +180.7 +177.6 +176.6 +173.5 +178.7 +175.0 +179.0 +175.5 +174.2 +178.2 +176.6 +176.3 +179.4 +178.6 +175.2 +175.9 +175.0 +179.5 +176.9 +184.0 +184.6 +179.9 +175.3 +176.2 +179.1 +182.8 +180.5 +173.4 +176.0 +198.9 +177.0 +189.7 +183.6 +179.5 +176.5 +175.5 +175.5 +176.4 +182.9 +178.7 +177.5 +180.6 +175.0 +178.9 +177.7 +178.0 +178.6 +176.0 +177.6 +180.8 +179.6 +184.3 +190.2 +177.7 +178.1 +175.0 +178.6 +179.6 +185.1 +193.7 +179.8 +179.3 +178.6 +180.4 +174.9 +177.8 +187.4 +183.7 +181.6 +177.7 +185.4 +180.6 +178.5 +182.8 +179.9 +177.8 +178.0 +184.3 +191.0 +181.1 +197.9 +181.1 +197.8 +175.6 +176.9 +177.9 +187.4 +180.8 +180.6 +174.9 +180.8 +175.7 +176.4 +179.9 +178.3 +178.3 +178.4 +196.4 +177.3 +184.3 +177.3 +176.5 +181.0 +175.0 +179.2 +178.1 +183.3 +177.0 +175.2 +177.2 +173.5 +176.5 +177.0 +178.6 +181.3 +189.2 +186.4 +177.4 +181.7 +176.2 +194.1 +177.9 +186.6 +182.6 +176.6 +175.4 +250.3 +178.7 +183.0 +182.7 +183.1 +183.6 +178.8 +178.3 +179.1 +177.7 +177.9 +186.2 +195.4 +179.2 +183.3 +183.8 +186.3 +182.7 +178.9 +196.0 +177.1 +184.5 +188.3 +188.4 +181.5 +190.1 +179.0 +177.2 +189.1 +184.1 +184.2 +178.7 +187.4 +182.5 +189.8 +181.1 +186.0 +178.8 +178.0 +176.9 +180.6 +174.3 +180.4 +176.7 +175.5 +193.7 +189.7 +196.6 +178.9 +186.3 +182.5 +185.7 +175.7 +176.0 +194.1 +181.0 +178.6 +178.4 +186.8 +175.6 +185.4 +180.7 +184.7 +188.1 +180.4 +183.0 +178.4 +185.1 +181.8 +175.8 +178.9 +177.9 +177.9 +176.5 +290.3 +181.8 +180.8 +183.9 +190.8 +179.0 +183.7 +181.9 +177.5 +178.4 +179.4 +179.9 +174.2 +182.7 +182.9 +178.8 +179.2 +183.2 +181.8 +175.6 +179.0 +175.7 +178.1 +179.2 +183.8 +187.8 +187.3 +175.7 +175.0 +185.5 +175.3 +181.9 +176.7 +184.6 +185.8 +181.1 +186.6 +178.8 +183.4 +175.1 +178.4 +179.6 +176.7 +177.1 +176.5 +181.7 +199.1 +194.8 +176.5 +182.2 +189.9 +177.6 +179.3 +184.9 +178.1 +176.4 +184.6 +174.6 +188.6 +183.7 +178.1 +177.7 +186.1 +176.3 +180.9 +180.3 +181.0 +180.0 +180.0 +180.4 +174.8 +179.5 +176.5 +179.9 +175.6 +184.0 +176.4 +191.6 +178.4 +184.8 +186.7 +197.0 diff --git a/netem/experimental.dist b/netem/experimental.dist new file mode 100644 index 0000000..025c198 --- /dev/null +++ b/netem/experimental.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the experimental distribution. +-10576 -10197 -10134 -10071 -10008 -9945 -9882 -9819 +-9788 -9756 -9725 -9693 -9672 -9651 -9630 -9615 +-9599 -9583 -9567 -9546 -9525 -9504 -9483 -9462 +-9441 -9420 -9399 -9378 -9366 -9353 -9341 -9328 +-9315 -9303 -9290 -9278 -9265 -9252 -9245 -9237 +-9229 -9221 -9213 -9205 -9197 -9189 -9183 -9177 +-9171 -9164 -9158 -9152 -9145 -9139 -9133 -9126 +-9119 -9111 -9103 -9095 -9087 -9079 -9071 -9063 +-9054 -9045 -9036 -9027 -9018 -9009 -9000 -8993 +-8986 -8979 -8972 -8965 -8958 -8951 -8944 -8937 +-8932 -8927 -8922 -8916 -8911 -8906 -8901 -8895 +-8890 -8885 -8880 -8874 -8867 -8859 -8851 -8843 +-8835 -8827 -8819 -8811 -8804 -8797 -8790 -8783 +-8776 -8769 -8762 -8755 -8748 -8744 -8740 -8736 +-8732 -8727 -8723 -8719 -8715 -8711 -8706 -8702 +-8698 -8694 -8690 -8685 -8681 -8676 -8671 -8666 +-8661 -8656 -8652 -8647 -8642 -8637 -8632 -8627 +-8622 -8618 -8613 -8609 -8604 -8600 -8595 -8591 +-8586 -8582 -8577 -8573 -8568 -8564 -8559 -8556 +-8552 -8548 -8545 -8541 -8537 -8534 -8530 -8526 +-8522 -8519 -8515 -8511 -8508 -8504 -8500 -8496 +-8492 -8487 -8482 -8477 -8472 -8467 -8463 -8458 +-8453 -8448 -8443 -8438 -8433 -8429 -8425 -8421 +-8416 -8412 -8408 -8404 -8399 -8395 -8391 -8387 +-8382 -8378 -8374 -8369 -8366 -8362 -8358 -8354 +-8350 -8346 -8342 -8338 -8334 -8330 -8326 -8322 +-8318 -8314 -8310 -8306 -8303 -8299 -8295 -8292 +-8288 -8284 -8281 -8277 -8273 -8269 -8266 -8262 +-8258 -8255 -8251 -8247 -8243 -8240 -8236 -8232 +-8229 -8225 -8221 -8218 -8214 -8210 -8206 -8203 +-8199 -8195 -8192 -8188 -8184 -8180 -8176 -8172 +-8168 -8164 -8159 -8155 -8151 -8147 -8143 -8138 +-8134 -8130 -8126 -8122 -8117 -8114 -8111 -8108 +-8105 -8102 -8099 -8095 -8092 -8089 -8086 -8083 +-8080 -8077 -8073 -8070 -8067 -8064 -8061 -8058 +-8054 -8051 -8047 -8043 -8040 -8036 -8032 -8029 +-8025 -8021 -8017 -8014 -8010 -8006 -8003 -7999 +-7995 -7991 -7988 -7984 -7981 -7977 -7974 -7970 +-7967 -7963 -7960 -7956 -7953 -7949 -7946 -7942 +-7939 -7935 -7932 -7928 -7925 -7921 -7917 -7914 +-7910 -7906 -7903 -7899 -7895 -7891 -7888 -7884 +-7880 -7877 -7873 -7869 -7865 -7861 -7857 -7853 +-7849 -7844 -7840 -7836 -7832 -7828 -7823 -7819 +-7815 -7811 -7807 -7802 -7800 -7797 -7794 -7792 +-7789 -7786 -7783 -7781 -7778 -7775 -7772 -7770 +-7767 -7764 -7761 -7759 -7756 -7753 -7750 -7748 +-7745 -7742 -7739 -7736 -7733 -7730 -7727 -7724 +-7721 -7717 -7714 -7711 -7708 -7705 -7702 -7699 +-7695 -7692 -7689 -7686 -7683 -7680 -7676 -7674 +-7671 -7668 -7665 -7662 -7659 -7656 -7654 -7651 +-7648 -7645 -7642 -7639 -7636 -7634 -7631 -7628 +-7625 -7622 -7619 -7616 -7613 -7611 -7609 -7606 +-7604 -7601 -7599 -7597 -7594 -7592 -7589 -7587 +-7584 -7582 -7580 -7577 -7575 -7572 -7570 -7567 +-7565 -7563 -7560 -7558 -7555 -7553 -7550 -7547 +-7544 -7541 -7537 -7534 -7531 -7527 -7524 -7521 +-7517 -7514 -7511 -7507 -7504 -7501 -7497 -7494 +-7491 -7487 -7484 -7480 -7477 -7473 -7470 -7466 +-7463 -7459 -7456 -7452 -7449 -7445 -7442 -7438 +-7435 -7431 -7428 -7424 -7420 -7416 -7412 -7408 +-7403 -7399 -7395 -7391 -7387 -7382 -7378 -7374 +-7370 -7366 -7361 -7359 -7357 -7354 -7352 -7349 +-7347 -7345 -7342 -7340 -7337 -7335 -7332 -7330 +-7328 -7325 -7323 -7320 -7318 -7315 -7313 -7311 +-7308 -7306 -7303 -7301 -7298 -7296 -7294 -7291 +-7289 -7286 -7284 -7282 -7279 -7277 -7274 -7272 +-7269 -7267 -7265 -7262 -7260 -7257 -7255 -7252 +-7250 -7248 -7245 -7243 -7240 -7238 -7235 -7232 +-7229 -7226 -7222 -7219 -7216 -7212 -7209 -7206 +-7202 -7199 -7196 -7192 -7189 -7186 -7182 -7179 +-7176 -7172 -7169 -7165 -7162 -7158 -7155 -7151 +-7148 -7144 -7141 -7137 -7134 -7130 -7127 -7123 +-7120 -7116 -7113 -7109 -7107 -7104 -7101 -7098 +-7095 -7092 -7089 -7087 -7084 -7081 -7078 -7075 +-7072 -7069 -7067 -7064 -7061 -7058 -7055 -7052 +-7049 -7046 -7043 -7040 -7037 -7034 -7031 -7028 +-7025 -7022 -7019 -7016 -7013 -7010 -7007 -7004 +-7001 -6998 -6995 -6992 -6989 -6986 -6983 -6980 +-6977 -6974 -6970 -6967 -6964 -6960 -6957 -6954 +-6950 -6947 -6944 -6940 -6937 -6934 -6930 -6927 +-6924 -6920 -6918 -6915 -6912 -6909 -6906 -6903 +-6900 -6898 -6895 -6892 -6889 -6886 -6883 -6880 +-6878 -6875 -6872 -6869 -6866 -6863 -6860 -6857 +-6855 -6852 -6850 -6847 -6844 -6842 -6839 -6836 +-6834 -6831 -6829 -6826 -6823 -6821 -6818 -6815 +-6813 -6810 -6808 -6805 -6802 -6800 -6797 -6794 +-6792 -6789 -6786 -6783 -6780 -6777 -6774 -6772 +-6769 -6766 -6763 -6760 -6757 -6754 -6752 -6749 +-6746 -6743 -6740 -6737 -6734 -6731 -6729 -6727 +-6725 -6722 -6720 -6718 -6715 -6713 -6711 -6709 +-6706 -6704 -6702 -6699 -6697 -6695 -6693 -6690 +-6688 -6686 -6683 -6681 -6679 -6677 -6674 -6672 +-6670 -6667 -6665 -6662 -6659 -6656 -6653 -6651 +-6648 -6645 -6642 -6639 -6636 -6634 -6631 -6628 +-6625 -6622 -6620 -6617 -6614 -6611 -6608 -6605 +-6602 -6599 -6596 -6593 -6589 -6586 -6583 -6580 +-6577 -6573 -6570 -6567 -6564 -6561 -6557 -6554 +-6551 -6548 -6545 -6541 -6539 -6537 -6534 -6532 +-6530 -6527 -6525 -6523 -6520 -6518 -6516 -6513 +-6511 -6509 -6506 -6504 -6502 -6499 -6497 -6495 +-6492 -6490 -6488 -6485 -6483 -6481 -6478 -6475 +-6472 -6469 -6465 -6462 -6459 -6455 -6452 -6449 +-6445 -6442 -6439 -6435 -6432 -6429 -6425 -6422 +-6419 -6415 -6412 -6408 -6405 -6401 -6398 -6394 +-6391 -6387 -6384 -6380 -6377 -6373 -6370 -6366 +-6363 -6359 -6356 -6352 -6349 -6346 -6343 -6340 +-6337 -6334 -6331 -6328 -6325 -6322 -6319 -6316 +-6313 -6310 -6307 -6304 -6301 -6298 -6295 -6292 +-6289 -6287 -6284 -6282 -6279 -6276 -6274 -6271 +-6268 -6266 -6263 -6261 -6258 -6255 -6253 -6250 +-6247 -6245 -6242 -6240 -6237 -6234 -6232 -6229 +-6226 -6223 -6219 -6216 -6212 -6209 -6205 -6202 +-6198 -6195 -6191 -6188 -6184 -6181 -6177 -6174 +-6170 -6167 -6163 -6160 -6156 -6152 -6149 -6145 +-6141 -6138 -6134 -6130 -6126 -6123 -6119 -6115 +-6112 -6108 -6104 -6100 -6098 -6095 -6092 -6089 +-6086 -6083 -6080 -6078 -6075 -6072 -6069 -6066 +-6063 -6060 -6058 -6055 -6052 -6049 -6046 -6043 +-6040 -6037 -6035 -6032 -6029 -6027 -6024 -6021 +-6018 -6016 -6013 -6010 -6007 -6005 -6002 -5999 +-5996 -5994 -5991 -5988 -5985 -5983 -5980 -5977 +-5974 -5971 -5968 -5965 -5962 -5959 -5956 -5952 +-5949 -5946 -5943 -5940 -5937 -5934 -5930 -5927 +-5924 -5921 -5918 -5915 -5911 -5909 -5906 -5903 +-5900 -5897 -5894 -5891 -5889 -5886 -5883 -5880 +-5877 -5874 -5871 -5869 -5866 -5863 -5860 -5857 +-5854 -5851 -5848 -5846 -5843 -5840 -5837 -5834 +-5831 -5828 -5826 -5823 -5820 -5817 -5814 -5811 +-5808 -5806 -5803 -5800 -5797 -5794 -5791 -5788 +-5785 -5782 -5779 -5776 -5772 -5769 -5766 -5762 +-5759 -5756 -5752 -5749 -5746 -5742 -5739 -5736 +-5732 -5729 -5726 -5722 -5720 -5717 -5714 -5711 +-5708 -5705 -5702 -5700 -5697 -5694 -5691 -5688 +-5685 -5682 -5680 -5677 -5674 -5671 -5668 -5665 +-5662 -5659 -5657 -5655 -5652 -5650 -5647 -5645 +-5643 -5640 -5638 -5635 -5633 -5630 -5628 -5626 +-5623 -5621 -5618 -5616 -5613 -5611 -5609 -5606 +-5604 -5601 -5599 -5596 -5592 -5587 -5583 -5578 +-5574 -5569 -5565 -5560 -5556 -5551 -5547 -5542 +-5538 -5533 -5531 -5528 -5526 -5523 -5521 -5518 +-5516 -5513 -5511 -5508 -5506 -5503 -5501 -5498 +-5496 -5493 -5491 -5488 -5486 -5483 -5481 -5478 +-5476 -5473 -5470 -5467 -5464 -5461 -5457 -5454 +-5451 -5447 -5444 -5441 -5437 -5434 -5431 -5427 +-5424 -5421 -5417 -5414 -5411 -5407 -5404 -5401 +-5398 -5394 -5391 -5388 -5384 -5381 -5378 -5374 +-5371 -5368 -5364 -5361 -5358 -5354 -5351 -5348 +-5344 -5341 -5338 -5335 -5331 -5328 -5325 -5321 +-5318 -5315 -5311 -5308 -5305 -5301 -5298 -5295 +-5291 -5288 -5285 -5281 -5278 -5274 -5270 -5267 +-5263 -5259 -5256 -5252 -5248 -5244 -5241 -5237 +-5233 -5230 -5226 -5222 -5218 -5216 -5214 -5212 +-5210 -5208 -5205 -5203 -5201 -5199 -5197 -5195 +-5192 -5190 -5188 -5186 -5184 -5182 -5179 -5177 +-5175 -5173 -5171 -5169 -5166 -5164 -5162 -5160 +-5158 -5155 -5152 -5148 -5144 -5140 -5136 -5132 +-5128 -5124 -5120 -5116 -5112 -5108 -5104 -5100 +-5096 -5092 -5089 -5086 -5083 -5079 -5076 -5073 +-5069 -5066 -5063 -5059 -5056 -5053 -5049 -5046 +-5043 -5039 -5036 -5033 -5029 -5027 -5024 -5022 +-5019 -5017 -5014 -5012 -5009 -5007 -5004 -5002 +-4999 -4997 -4994 -4992 -4989 -4987 -4984 -4982 +-4979 -4977 -4974 -4972 -4969 -4966 -4963 -4960 +-4957 -4954 -4951 -4948 -4944 -4941 -4938 -4935 +-4932 -4929 -4926 -4922 -4919 -4916 -4913 -4910 +-4907 -4903 -4899 -4895 -4891 -4887 -4882 -4878 +-4874 -4870 -4866 -4861 -4857 -4853 -4849 -4845 +-4840 -4837 -4833 -4829 -4825 -4822 -4818 -4814 +-4810 -4807 -4803 -4799 -4795 -4792 -4788 -4784 +-4780 -4776 -4774 -4771 -4768 -4765 -4762 -4759 +-4756 -4753 -4750 -4747 -4744 -4741 -4738 -4735 +-4732 -4729 -4726 -4723 -4720 -4717 -4714 -4711 +-4708 -4705 -4702 -4699 -4696 -4693 -4690 -4687 +-4684 -4681 -4678 -4675 -4672 -4669 -4666 -4663 +-4660 -4657 -4654 -4650 -4647 -4643 -4639 -4636 +-4632 -4628 -4625 -4621 -4617 -4613 -4610 -4606 +-4602 -4599 -4595 -4591 -4587 -4584 -4580 -4576 +-4572 -4568 -4564 -4560 -4556 -4552 -4548 -4544 +-4540 -4536 -4532 -4528 -4524 -4520 -4516 -4512 +-4508 -4503 -4499 -4495 -4491 -4487 -4482 -4478 +-4474 -4470 -4466 -4461 -4459 -4456 -4453 -4450 +-4447 -4444 -4441 -4439 -4436 -4433 -4430 -4427 +-4424 -4421 -4419 -4416 -4413 -4410 -4407 -4404 +-4401 -4398 -4395 -4391 -4388 -4384 -4381 -4377 +-4374 -4370 -4367 -4363 -4360 -4356 -4353 -4349 +-4346 -4342 -4339 -4335 -4332 -4329 -4326 -4323 +-4320 -4317 -4314 -4311 -4308 -4305 -4302 -4299 +-4296 -4293 -4290 -4287 -4284 -4281 -4278 -4275 +-4272 -4268 -4263 -4258 -4253 -4248 -4243 -4239 +-4234 -4229 -4224 -4219 -4214 -4209 -4206 -4202 +-4199 -4195 -4192 -4188 -4185 -4181 -4178 -4174 +-4171 -4167 -4164 -4160 -4157 -4153 -4150 -4146 +-4142 -4138 -4134 -4130 -4125 -4121 -4117 -4113 +-4109 -4104 -4100 -4096 -4092 -4088 -4083 -4080 +-4076 -4073 -4069 -4066 -4062 -4059 -4055 -4052 +-4048 -4045 -4041 -4038 -4034 -4031 -4027 -4024 +-4020 -4017 -4013 -4010 -4006 -4003 -3999 -3996 +-3992 -3989 -3985 -3982 -3978 -3975 -3971 -3968 +-3964 -3961 -3957 -3954 -3950 -3947 -3943 -3940 +-3936 -3933 -3929 -3926 -3922 -3919 -3915 -3912 +-3908 -3905 -3901 -3898 -3894 -3891 -3887 -3884 +-3880 -3877 -3873 -3870 -3866 -3863 -3859 -3856 +-3852 -3849 -3845 -3842 -3838 -3835 -3831 -3827 +-3823 -3819 -3815 -3810 -3806 -3802 -3798 -3794 +-3789 -3785 -3781 -3777 -3773 -3768 -3765 -3761 +-3757 -3754 -3750 -3746 -3743 -3739 -3735 -3731 +-3728 -3724 -3720 -3717 -3713 -3709 -3705 -3702 +-3699 -3696 -3692 -3689 -3686 -3682 -3679 -3676 +-3672 -3669 -3666 -3662 -3659 -3656 -3652 -3649 +-3646 -3642 -3639 -3635 -3631 -3628 -3624 -3620 +-3617 -3613 -3609 -3605 -3602 -3598 -3594 -3591 +-3587 -3583 -3579 -3575 -3570 -3566 -3561 -3557 +-3552 -3548 -3543 -3539 -3534 -3530 -3525 -3521 +-3516 -3513 -3509 -3505 -3501 -3497 -3493 -3489 +-3485 -3481 -3477 -3473 -3469 -3465 -3461 -3457 +-3453 -3450 -3446 -3442 -3439 -3435 -3431 -3428 +-3424 -3420 -3416 -3413 -3409 -3405 -3402 -3398 +-3394 -3390 -3387 -3383 -3380 -3376 -3373 -3369 +-3366 -3362 -3359 -3355 -3352 -3348 -3345 -3341 +-3338 -3334 -3331 -3327 -3323 -3318 -3314 -3309 +-3305 -3300 -3296 -3291 -3287 -3282 -3278 -3273 +-3269 -3264 -3260 -3255 -3250 -3245 -3240 -3235 +-3231 -3226 -3221 -3216 -3211 -3206 -3201 -3198 +-3194 -3190 -3186 -3182 -3178 -3174 -3170 -3166 +-3162 -3158 -3154 -3150 -3146 -3142 -3138 -3135 +-3131 -3128 -3124 -3121 -3117 -3114 -3110 -3107 +-3103 -3100 -3096 -3093 -3089 -3086 -3082 -3079 +-3075 -3072 -3068 -3065 -3061 -3058 -3054 -3051 +-3047 -3044 -3040 -3037 -3033 -3030 -3026 -3023 +-3019 -3016 -3012 -3008 -3004 -3000 -2995 -2991 +-2987 -2983 -2978 -2974 -2970 -2966 -2961 -2957 +-2953 -2948 -2945 -2942 -2939 -2935 -2932 -2929 +-2925 -2922 -2919 -2915 -2912 -2909 -2905 -2902 +-2899 -2895 -2892 -2889 -2885 -2882 -2878 -2874 +-2870 -2866 -2862 -2858 -2854 -2850 -2846 -2842 +-2838 -2834 -2830 -2826 -2822 -2819 -2815 -2811 +-2807 -2803 -2799 -2795 -2791 -2787 -2783 -2779 +-2775 -2771 -2767 -2763 -2759 -2755 -2750 -2745 +-2740 -2735 -2730 -2726 -2721 -2716 -2711 -2706 +-2701 -2696 -2692 -2688 -2684 -2680 -2675 -2671 +-2667 -2663 -2659 -2654 -2650 -2646 -2642 -2638 +-2633 -2630 -2626 -2622 -2618 -2614 -2610 -2606 +-2602 -2598 -2594 -2590 -2586 -2582 -2578 -2574 +-2570 -2566 -2561 -2557 -2552 -2548 -2543 -2539 +-2534 -2530 -2525 -2521 -2516 -2512 -2507 -2502 +-2497 -2492 -2486 -2481 -2476 -2471 -2465 -2460 +-2455 -2450 -2444 -2439 -2434 -2429 -2423 -2418 +-2413 -2408 -2402 -2397 -2392 -2387 -2381 -2376 +-2371 -2366 -2360 -2355 -2350 -2345 -2339 -2334 +-2329 -2324 -2318 -2314 -2309 -2305 -2300 -2296 +-2291 -2287 -2282 -2278 -2273 -2269 -2264 -2260 +-2255 -2251 -2246 -2242 -2237 -2233 -2228 -2224 +-2219 -2215 -2210 -2206 -2201 -2197 -2192 -2188 +-2183 -2179 -2174 -2170 -2165 -2161 -2156 -2152 +-2147 -2143 -2138 -2134 -2129 -2125 -2121 -2117 +-2113 -2108 -2104 -2100 -2096 -2092 -2087 -2083 +-2079 -2075 -2071 -2066 -2062 -2057 -2052 -2047 +-2042 -2037 -2033 -2028 -2023 -2018 -2013 -2008 +-2003 -1999 -1994 -1989 -1984 -1979 -1974 -1970 +-1965 -1960 -1955 -1950 -1945 -1940 -1936 -1931 +-1926 -1921 -1916 -1911 -1907 -1902 -1897 -1892 +-1887 -1882 -1877 -1873 -1869 -1865 -1861 -1856 +-1852 -1848 -1844 -1840 -1835 -1831 -1827 -1823 +-1819 -1814 -1811 -1807 -1803 -1800 -1796 -1792 +-1789 -1785 -1781 -1777 -1774 -1770 -1766 -1763 +-1759 -1755 -1751 -1747 -1742 -1737 -1732 -1727 +-1722 -1718 -1713 -1708 -1703 -1698 -1693 -1688 +-1684 -1679 -1674 -1669 -1664 -1659 -1655 -1650 +-1645 -1640 -1635 -1630 -1625 -1620 -1615 -1610 +-1604 -1599 -1594 -1589 -1583 -1578 -1573 -1568 +-1562 -1557 -1552 -1547 -1541 -1536 -1531 -1526 +-1520 -1515 -1510 -1505 -1499 -1495 -1490 -1485 +-1480 -1475 -1470 -1466 -1461 -1456 -1451 -1446 +-1441 -1436 -1431 -1426 -1421 -1415 -1410 -1405 +-1400 -1394 -1389 -1384 -1379 -1373 -1369 -1364 +-1360 -1355 -1351 -1346 -1342 -1337 -1333 -1328 +-1324 -1319 -1315 -1310 -1307 -1303 -1299 -1295 +-1292 -1288 -1284 -1280 -1277 -1273 -1269 -1265 +-1262 -1258 -1254 -1250 -1246 -1242 -1238 -1233 +-1229 -1224 -1220 -1215 -1211 -1207 -1202 -1198 +-1193 -1189 -1184 -1180 -1175 -1171 -1166 -1162 +-1157 -1153 -1148 -1144 -1139 -1135 -1130 -1126 +-1121 -1116 -1110 -1104 -1098 -1092 -1087 -1081 +-1075 -1069 -1063 -1057 -1052 -1047 -1042 -1036 +-1031 -1026 -1021 -1015 -1010 -1005 -1000 -994 +-990 -985 -981 -976 -972 -967 -963 -958 +-954 -949 -945 -940 -936 -931 -927 -923 +-919 -915 -910 -906 -902 -898 -894 -889 +-885 -881 -877 -873 -868 -861 -854 -847 +-840 -833 -826 -819 -812 -805 -800 -794 +-788 -783 -777 -771 -765 -760 -754 -748 +-742 -737 -731 -725 -720 -714 -708 -702 +-697 -691 -685 -679 -675 -670 -666 -661 +-657 -652 -648 -643 -639 -634 -630 -625 +-621 -616 -611 -605 -599 -594 -588 -582 +-576 -571 -565 -559 -553 -548 -542 -536 +-531 -525 -519 -513 -508 -502 -496 -490 +-485 -480 -475 -469 -464 -459 -454 -448 +-443 -438 -433 -427 -420 -412 -404 -396 +-388 -380 -372 -364 -355 -346 -337 -328 +-319 -310 -301 -294 -287 -280 -273 -266 +-259 -252 -245 -238 -234 -229 -224 -219 +-214 -209 -205 -200 -195 -190 -185 -180 +-175 -168 -161 -154 -147 -140 -133 -126 +-119 -112 -107 -101 -95 -90 -84 -78 +-72 -67 -61 -55 -49 -43 -37 -31 +-24 -18 -12 -5 1 7 14 21 +28 35 42 49 56 63 70 77 +83 89 95 102 108 114 121 127 +133 140 147 154 161 168 175 182 +189 196 203 210 217 224 231 238 +245 252 259 266 272 278 284 291 +297 303 310 316 322 329 334 339 +344 350 355 360 365 371 376 381 +386 392 399 406 413 420 427 434 +441 448 455 462 470 478 486 494 +502 510 518 523 529 535 540 546 +552 558 563 569 575 581 590 599 +608 617 626 635 645 652 659 666 +673 680 687 694 701 708 714 720 +726 733 739 745 752 758 764 771 +775 780 784 789 793 798 802 807 +811 816 820 825 829 834 839 845 +851 856 862 868 874 879 885 891 +897 902 907 912 918 923 928 933 +939 944 949 954 960 969 978 987 +996 1005 1014 1023 1033 1044 1054 1065 +1075 1086 1093 1101 1109 1117 1125 1133 +1141 1149 1158 1167 1176 1185 1194 1203 +1212 1218 1224 1230 1237 1243 1249 1256 +1262 1268 1275 1281 1287 1293 1300 1306 +1312 1319 1325 1331 1338 1347 1356 1365 +1374 1383 1392 1401 1406 1412 1418 1423 +1429 1435 1441 1446 1452 1458 1464 1469 +1475 1481 1486 1492 1498 1504 1509 1515 +1521 1527 1536 1545 1554 1563 1572 1581 +1590 1599 1608 1617 1626 1635 1644 1653 +1660 1667 1674 1681 1688 1695 1702 1709 +1716 1722 1728 1734 1741 1747 1753 1760 +1766 1772 1779 1789 1800 1810 1821 1831 +1842 1847 1853 1859 1864 1870 1876 1882 +1887 1893 1899 1905 1915 1926 1936 1947 +1957 1968 1975 1983 1991 1999 2007 2015 +2023 2031 2038 2046 2054 2062 2070 2078 +2086 2094 2101 2108 2115 2122 2129 2136 +2143 2150 2157 2164 2172 2180 2188 2196 +2204 2212 2220 2225 2231 2237 2242 2248 +2254 2260 2265 2271 2277 2283 2292 2301 +2310 2319 2328 2337 2347 2352 2358 2363 +2369 2375 2380 2386 2392 2397 2403 2409 +2417 2425 2433 2441 2449 2457 2465 2473 +2480 2488 2496 2504 2512 2520 2528 2536 +2542 2548 2554 2561 2567 2573 2580 2586 +2592 2599 2611 2624 2636 2649 2662 2669 +2676 2683 2690 2697 2704 2711 2718 2725 +2732 2740 2748 2756 2764 2772 2780 2788 +2795 2803 2811 2819 2827 2835 2843 2851 +2857 2863 2869 2876 2882 2888 2895 2901 +2907 2914 2921 2929 2937 2945 2953 2961 +2969 2977 2986 2995 3004 3013 3022 3031 +3040 3050 3061 3071 3082 3092 3103 3110 +3117 3124 3131 3138 3145 3152 3159 3166 +3173 3180 3187 3194 3201 3208 3215 3222 +3229 3235 3241 3247 3254 3260 3266 3273 +3279 3285 3292 3298 3304 3310 3317 3323 +3329 3336 3342 3348 3355 3361 3367 3373 +3380 3386 3392 3399 3405 3411 3418 3427 +3436 3445 3454 3463 3472 3481 3491 3502 +3512 3523 3533 3544 3551 3559 3567 3575 +3583 3591 3599 3607 3619 3632 3644 3657 +3670 3676 3682 3688 3695 3701 3707 3714 +3720 3726 3733 3738 3744 3750 3755 3761 +3767 3773 3778 3784 3790 3796 3803 3811 +3819 3827 3835 3843 3851 3859 3866 3874 +3882 3890 3898 3906 3914 3922 3932 3943 +3953 3964 3974 3985 3992 3999 4006 4013 +4020 4027 4034 4041 4048 4058 4069 4079 +4090 4100 4111 4120 4129 4138 4147 4156 +4165 4174 4180 4186 4193 4199 4206 4212 +4218 4225 4231 4238 4245 4253 4261 4269 +4276 4284 4292 4300 4308 4316 4324 4332 +4340 4348 4356 4364 4371 4379 4387 4395 +4403 4411 4419 4427 4434 4441 4448 4455 +4462 4469 4476 4483 4490 4505 4521 4537 +4553 4560 4567 4574 4581 4588 4595 4602 +4609 4616 4625 4634 4643 4652 4661 4670 +4679 4688 4697 4706 4715 4724 4733 4742 +4751 4760 4769 4778 4787 4796 4805 4812 +4819 4826 4833 4840 4847 4854 4861 4868 +4875 4882 4889 4896 4903 4910 4917 4924 +4931 4941 4952 4962 4973 4983 4994 5004 +5015 5025 5036 5046 5057 5064 5071 5078 +5085 5092 5099 5106 5113 5120 5129 5138 +5147 5156 5165 5174 5183 5193 5204 5214 +5225 5235 5246 5258 5271 5283 5296 5309 +5318 5327 5336 5345 5354 5363 5372 5382 +5393 5403 5414 5424 5435 5445 5456 5466 +5477 5487 5498 5505 5513 5521 5529 5537 +5545 5553 5561 5568 5576 5584 5592 5600 +5608 5616 5624 5634 5645 5655 5666 5676 +5687 5696 5705 5714 5723 5732 5741 5750 +5757 5764 5771 5778 5785 5792 5799 5806 +5813 5818 5824 5830 5835 5841 5847 5853 +5858 5864 5870 5876 5886 5897 5907 5918 +5928 5939 5951 5964 5976 5989 6002 6011 +6020 6029 6038 6047 6056 6066 6075 6084 +6093 6102 6111 6120 6129 6138 6147 6156 +6165 6174 6183 6192 6199 6207 6215 6223 +6231 6239 6247 6255 6262 6269 6276 6283 +6290 6297 6304 6311 6318 6325 6333 6341 +6349 6357 6365 6373 6381 6391 6402 6412 +6423 6433 6444 6453 6462 6471 6480 6489 +6498 6507 6514 6521 6528 6535 6542 6549 +6556 6563 6570 6579 6588 6597 6606 6615 +6624 6633 6640 6648 6656 6664 6672 6680 +6688 6696 6705 6714 6723 6732 6741 6750 +6759 6771 6784 6796 6809 6822 6837 6853 +6869 6885 6892 6900 6908 6916 6924 6932 +6940 6948 6957 6966 6975 6984 6993 7002 +7011 7021 7032 7042 7053 7063 7074 7083 +7092 7101 7110 7119 7128 7137 7142 7148 +7154 7159 7165 7171 7177 7182 7188 7194 +7200 7210 7221 7231 7242 7252 7263 7269 +7275 7281 7288 7294 7300 7307 7313 7319 +7326 7341 7357 7373 7389 7399 7410 7420 +7431 7441 7452 7459 7466 7473 7480 7487 +7494 7501 7508 7515 7522 7530 7538 7546 +7554 7562 7570 7578 7588 7599 7609 7620 +7630 7641 7651 7662 7672 7683 7693 7704 +7714 7725 7735 7746 7756 7767 7777 7788 +7798 7809 7819 7830 7839 7848 7857 7866 +7875 7884 7893 7902 7911 7920 7929 7938 +7947 7957 7966 7975 7984 7993 8002 8011 +8020 8030 8041 8051 8062 8072 8083 8090 +8098 8106 8114 8122 8130 8138 8146 8158 +8171 8183 8196 8209 8219 8230 8240 8251 +8261 8272 8281 8290 8299 8308 8317 8326 +8335 8345 8356 8366 8377 8387 8398 8413 +8429 8445 8461 8471 8482 8492 8503 8513 +8524 8531 8539 8547 8555 8563 8571 8579 +8587 8597 8608 8618 8629 8639 8650 8657 +8665 8673 8681 8689 8697 8705 8713 8722 +8731 8740 8749 8758 8767 8776 8783 8790 +8797 8804 8811 8818 8825 8832 8839 8849 +8860 8870 8881 8891 8902 8914 8927 8939 +8952 8965 8974 8983 8992 9001 9010 9019 +9028 9038 9049 9059 9070 9080 9091 9101 +9112 9122 9133 9143 9154 9166 9179 9191 +9204 9217 9223 9229 9235 9242 9248 9254 +9261 9267 9273 9280 9290 9301 9311 9322 +9332 9343 9353 9364 9374 9385 9395 9406 +9421 9437 9453 9469 9481 9494 9506 9519 +9532 9542 9553 9563 9574 9584 9595 9607 +9620 9633 9646 9659 9669 9679 9690 9700 +9710 9721 9733 9746 9759 9772 9785 9795 +9806 9816 9827 9837 9848 9858 9869 9879 +9890 9900 9911 9921 9932 9942 9953 9963 +9974 9981 9989 9997 10005 10013 10021 10029 +10037 10046 10055 10064 10073 10082 10091 10100 +10115 10131 10147 10163 10173 10184 10194 10205 +10215 10226 10235 10244 10253 10262 10271 10280 +10289 10299 10310 10320 10331 10341 10352 10373 +10394 10415 10430 10446 10462 10478 10487 10496 +10505 10514 10523 10532 10541 10553 10566 10578 +10591 10604 10619 10635 10651 10667 10676 10685 +10694 10703 10712 10721 10730 10742 10755 10767 +10780 10793 10803 10814 10824 10835 10845 10856 +10868 10881 10893 10906 10919 10929 10940 10950 +10961 10971 10982 10992 11003 11013 11024 11034 +11045 11060 11076 11092 11108 11123 11139 11155 +11171 11181 11192 11202 11213 11223 11234 11244 +11255 11265 11276 11286 11297 11307 11318 11329 +11339 11350 11361 11371 11381 11392 11402 11412 +11423 11433 11444 11454 11465 11475 11486 11518 +11550 11560 11571 11581 11592 11602 11613 11628 +11644 11660 11676 11697 11718 11739 11749 11760 +11770 11781 11791 11802 11817 11833 11849 11865 +11875 11886 11896 11907 11917 11928 11959 11991 +12006 12022 12038 12054 12069 12085 12101 12117 +12127 12138 12148 12159 12169 12180 12201 12222 +12243 12274 12306 12321 12337 12353 12369 12384 +12400 12416 12432 12453 12474 12495 12507 12520 +12532 12545 12558 12579 12600 12621 12642 12663 +12684 12705 12726 12747 12759 12772 12784 12797 +12810 12822 12835 12847 12860 12873 12894 12915 +12936 12948 12961 12973 12986 12999 13020 13041 +13062 13093 13125 13140 13156 13172 13188 13209 +13230 13252 13283 13314 13378 13399 13420 13441 +13451 13462 13472 13483 13493 13504 13519 13535 +13551 13567 13630 13651 13672 13693 13714 13735 +13756 13787 13819 13882 13897 13913 13929 13945 +13966 13987 14008 14029 14050 14071 14092 14113 +14134 14197 14212 14228 14244 14260 14281 14302 +14323 14354 14386 14417 14449 14480 14512 14533 +14554 14575 14606 14638 14669 14701 14764 14795 +14827 14842 14858 14874 14890 14953 14984 15016 +15048 15080 15111 15143 15174 15205 15226 15247 +15269 15284 15300 15316 15332 15395 15458 15479 +15500 15521 15584 15615 15647 15668 15689 15710 +15741 15773 15836 15899 15930 15962 15993 16025 +16056 16088 16151 16214 16245 16277 16340 16403 +16466 16529 16592 16718 16781 16844 16907 16971 +17034 17065 17097 17160 17223 17254 17286 17412 +17443 17475 17538 17601 17664 17727 17916 18042 +18105 18136 18168 18294 18325 18357 18546 18577 +18609 18735 18830 18925 18956 18988 19114 19177 +19240 19303 19366 19429 19555 19618 19681 19807 +19870 19933 20185 20374 20564 20753 20942 21068 +21509 21761 22139 22455 22644 23148 23400 23652 +24030 24913 25985 26174 26993 28128 28947 29136 +30082 30712 32767 32767 32767 32767 32767 32767 +32767 32767 32767 32767 32767 32767 32767 32767 diff --git a/netem/maketable b/netem/maketable new file mode 100755 index 0000000..9fbd1ca Binary files /dev/null and b/netem/maketable differ diff --git a/netem/maketable.c b/netem/maketable.c new file mode 100644 index 0000000..ce09176 --- /dev/null +++ b/netem/maketable.c @@ -0,0 +1,232 @@ +/* + * Experimental data distribution table generator + * Taken from the uncopyrighted NISTnet code. + * + * Rread in a series of "random" data values, either + * experimentally or generated from some probability distribution. + * From this, create the inverse distribution table used to approximate + * the distribution. + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <malloc.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + + +double * +readdoubles(FILE *fp, int *number) +{ + struct stat info; + double *x; + int limit; + int n=0, i; + + fstat(fileno(fp), &info); + if (info.st_size > 0) { + limit = 2*info.st_size/sizeof(double); /* @@ approximate */ + } else { + limit = 10000; + } + + x = calloc(limit, sizeof(double)); + if (!x) { + perror("double alloc"); + exit(3); + } + + for (i=0; i<limit; ++i){ + fscanf(fp, "%lf", &x[i]); + if (feof(fp)) + break; + ++n; + } + *number = n; + return x; +} + +void +arraystats(double *x, int limit, double *mu, double *sigma, double *rho) +{ + int n=0, i; + double sumsquare=0.0, sum=0.0, top=0.0; + double sigma2=0.0; + + for (i=0; i<limit; ++i){ + sumsquare += x[i]*x[i]; + sum += x[i]; + ++n; + } + *mu = sum/(double)n; + *sigma = sqrt((sumsquare - (double)n*(*mu)*(*mu))/(double)(n-1)); + + for (i=1; i < n; ++i){ + top += ((double)x[i]- *mu)*((double)x[i-1]- *mu); + sigma2 += ((double)x[i-1] - *mu)*((double)x[i-1] - *mu); + + } + *rho = top/sigma2; +} + +/* Create a (normalized) distribution table from a set of observed + * values. The table is fixed to run from (as it happens) -4 to +4, + * with granularity .00002. + */ + +#define TABLESIZE 16384/4 +#define TABLEFACTOR 8192 +#ifndef MINSHORT +#define MINSHORT -32768 +#define MAXSHORT 32767 +#endif + +/* Since entries in the inverse are scaled by TABLEFACTOR, and can't be bigger + * than MAXSHORT, we don't bother looking at a larger domain than this: + */ +#define DISTTABLEDOMAIN ((MAXSHORT/TABLEFACTOR)+1) +#define DISTTABLEGRANULARITY 50000 +#define DISTTABLESIZE (DISTTABLEDOMAIN*DISTTABLEGRANULARITY*2) + +static int * +makedist(double *x, int limit, double mu, double sigma) +{ + int *table; + int i, index, first=DISTTABLESIZE, last=0; + double input; + + table = calloc(DISTTABLESIZE, sizeof(int)); + if (!table) { + perror("table alloc"); + exit(3); + } + + for (i=0; i < limit; ++i) { + /* Normalize value */ + input = (x[i]-mu)/sigma; + + index = (int)rint((input+DISTTABLEDOMAIN)*DISTTABLEGRANULARITY); + if (index < 0) index = 0; + if (index >= DISTTABLESIZE) index = DISTTABLESIZE-1; + ++table[index]; + if (index > last) + last = index +1; + if (index < first) + first = index; + } + return table; +} + +/* replace an array by its cumulative distribution */ +static void +cumulativedist(int *table, int limit, int *total) +{ + int accum=0; + + while (--limit >= 0) { + accum += *table; + *table++ = accum; + } + *total = accum; +} + +static short * +inverttable(int *table, int inversesize, int tablesize, int cumulative) +{ + int i, inverseindex, inversevalue; + short *inverse; + double findex, fvalue; + + inverse = (short *)malloc(inversesize*sizeof(short)); + for (i=0; i < inversesize; ++i) { + inverse[i] = MINSHORT; + } + for (i=0; i < tablesize; ++i) { + findex = ((double)i/(double)DISTTABLEGRANULARITY) - DISTTABLEDOMAIN; + fvalue = (double)table[i]/(double)cumulative; + inverseindex = (int)rint(fvalue*inversesize); + inversevalue = (int)rint(findex*TABLEFACTOR); + if (inversevalue <= MINSHORT) inversevalue = MINSHORT+1; + if (inversevalue > MAXSHORT) inversevalue = MAXSHORT; + inverse[inverseindex] = inversevalue; + } + return inverse; + +} + +/* Run simple linear interpolation over the table to fill in missing entries */ +static void +interpolatetable(short *table, int limit) +{ + int i, j, last, lasti = -1; + + last = MINSHORT; + for (i=0; i < limit; ++i) { + if (table[i] == MINSHORT) { + for (j=i; j < limit; ++j) + if (table[j] != MINSHORT) + break; + if (j < limit) { + table[i] = last + (i-lasti)*(table[j]-last)/(j-lasti); + } else { + table[i] = last + (i-lasti)*(MAXSHORT-last)/(limit-lasti); + } + } else { + last = table[i]; + lasti = i; + } + } +} + +static void +printtable(const short *table, int limit) +{ + int i; + + printf("# This is the distribution table for the experimental distribution.\n"); + + for (i=0 ; i < limit; ++i) { + printf("%d%c", table[i], + (i % 8) == 7 ? '\n' : ' '); + } +} + +int +main(int argc, char **argv) +{ + FILE *fp; + double *x; + double mu, sigma, rho; + int limit; + int *table; + short *inverse; + int total; + + if (argc > 1) { + if (!(fp = fopen(argv[1], "r"))) { + perror(argv[1]); + exit(1); + } + } else { + fp = stdin; + } + x = readdoubles(fp, &limit); + if (limit <= 0) { + fprintf(stderr, "Nothing much read!\n"); + exit(2); + } + arraystats(x, limit, &mu, &sigma, &rho); +#ifdef DEBUG + fprintf(stderr, "%d values, mu %10.4f, sigma %10.4f, rho %10.4f\n", + limit, mu, sigma, rho); +#endif + + table = makedist(x, limit, mu, sigma); + free((void *) x); + cumulativedist(table, DISTTABLESIZE, &total); + inverse = inverttable(table, TABLESIZE, DISTTABLESIZE, total); + interpolatetable(inverse, TABLESIZE); + printtable(inverse, TABLESIZE); + return 0; +} diff --git a/netem/normal b/netem/normal new file mode 100755 index 0000000..a42860d Binary files /dev/null and b/netem/normal differ diff --git a/netem/normal.c b/netem/normal.c new file mode 100644 index 0000000..e6683db --- /dev/null +++ b/netem/normal.c @@ -0,0 +1,56 @@ +/* + * Normal distribution table generator + * Taken from the uncopyrighted NISTnet code. + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <limits.h> + +#include <linux/types.h> +#include <linux/pkt_sched.h> + +#define TABLESIZE 16384 +#define TABLEFACTOR NETEM_DIST_SCALE + +static double +normal(double x, double mu, double sigma) +{ + return .5 + .5*erf((x-mu)/(sqrt(2.0)*sigma)); +} + +int +main(int argc, char **argv) +{ + double x, *table; + int i, n; + + table = calloc(sizeof(double), TABLESIZE+1); + if (!table) { + fprintf(stderr, "Not enough memory\n"); + return 1; + } + + + for (x = -10.0; x < 10.05; x += .00005) { + i = (int)rint(TABLESIZE*normal(x, 0.0, 1.0)); + table[i] = x; + } + + + printf("# This is the distribution table for the normal distribution.\n"); + for (i = n = 0; i < TABLESIZE; i += 4) { + int value = (int) rint(table[i]*TABLEFACTOR); + if (value < SHRT_MIN) value = SHRT_MIN; + if (value > SHRT_MAX) value = SHRT_MAX; + + printf(" %d", value); + if (++n == 8) { + putchar('\n'); + n = 0; + } + } + free(table); + return 0; +} diff --git a/netem/normal.dist b/netem/normal.dist new file mode 100644 index 0000000..8cfe768 --- /dev/null +++ b/netem/normal.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the normal distribution. + -32768 -28307 -26871 -25967 -25298 -24765 -24320 -23937 + -23600 -23298 -23025 -22776 -22546 -22333 -22133 -21946 + -21770 -21604 -21445 -21295 -21151 -21013 -20882 -20755 + -20633 -20516 -20403 -20293 -20187 -20084 -19984 -19887 + -19793 -19702 -19612 -19526 -19441 -19358 -19277 -19198 + -19121 -19045 -18971 -18899 -18828 -18758 -18690 -18623 + -18557 -18492 -18429 -18366 -18305 -18245 -18185 -18127 + -18070 -18013 -17957 -17902 -17848 -17794 -17741 -17690 + -17638 -17588 -17538 -17489 -17440 -17392 -17345 -17298 + -17252 -17206 -17160 -17116 -17071 -17028 -16984 -16942 + -16899 -16857 -16816 -16775 -16735 -16694 -16654 -16615 + -16576 -16538 -16499 -16461 -16424 -16386 -16350 -16313 + -16277 -16241 -16205 -16170 -16135 -16100 -16066 -16031 + -15998 -15964 -15931 -15897 -15865 -15832 -15800 -15768 + -15736 -15704 -15673 -15642 -15611 -15580 -15550 -15519 + -15489 -15460 -15430 -15401 -15371 -15342 -15313 -15285 + -15256 -15228 -15200 -15172 -15144 -15116 -15089 -15062 + -15035 -15008 -14981 -14954 -14928 -14902 -14875 -14850 + -14823 -14798 -14772 -14747 -14722 -14696 -14671 -14647 + -14622 -14597 -14573 -14549 -14524 -14500 -14476 -14453 + -14429 -14405 -14382 -14359 -14335 -14312 -14289 -14266 + -14243 -14221 -14198 -14176 -14153 -14131 -14109 -14087 + -14065 -14043 -14021 -14000 -13978 -13957 -13935 -13914 + -13893 -13872 -13851 -13830 -13809 -13788 -13768 -13747 + -13727 -13706 -13686 -13666 -13646 -13626 -13606 -13586 + -13566 -13547 -13527 -13507 -13488 -13468 -13449 -13430 + -13411 -13392 -13373 -13354 -13335 -13316 -13297 -13278 + -13260 -13242 -13223 -13204 -13186 -13168 -13150 -13131 + -13113 -13095 -13077 -13060 -13042 -13024 -13006 -12988 + -12971 -12954 -12936 -12918 -12901 -12884 -12867 -12850 + -12832 -12815 -12798 -12781 -12764 -12748 -12731 -12714 + -12697 -12681 -12664 -12648 -12631 -12615 -12598 -12582 + -12566 -12549 -12533 -12517 -12501 -12485 -12469 -12453 + -12437 -12422 -12406 -12390 -12374 -12358 -12343 -12327 + -12312 -12296 -12281 -12265 -12250 -12235 -12220 -12204 + -12189 -12174 -12159 -12144 -12129 -12114 -12099 -12084 + -12069 -12054 -12039 -12025 -12010 -11995 -11981 -11966 + -11952 -11937 -11923 -11908 -11894 -11879 -11865 -11851 + -11837 -11822 -11808 -11794 -11780 -11766 -11752 -11737 + -11724 -11710 -11696 -11682 -11668 -11654 -11640 -11627 + -11613 -11599 -11586 -11572 -11559 -11545 -11531 -11518 + -11504 -11491 -11478 -11464 -11451 -11438 -11425 -11411 + -11398 -11385 -11372 -11359 -11346 -11332 -11319 -11306 + -11293 -11280 -11268 -11255 -11242 -11229 -11216 -11203 + -11191 -11178 -11165 -11153 -11140 -11127 -11114 -11102 + -11090 -11077 -11065 -11052 -11040 -11027 -11015 -11002 + -10990 -10978 -10965 -10953 -10941 -10929 -10917 -10904 + -10892 -10880 -10868 -10856 -10844 -10832 -10820 -10808 + -10796 -10784 -10772 -10760 -10748 -10736 -10725 -10713 + -10701 -10689 -10677 -10666 -10654 -10643 -10631 -10619 + -10607 -10596 -10584 -10573 -10562 -10550 -10539 -10527 + -10516 -10504 -10493 -10481 -10470 -10459 -10447 -10436 + -10425 -10414 -10402 -10391 -10380 -10369 -10358 -10346 + -10335 -10324 -10313 -10302 -10291 -10280 -10269 -10258 + -10247 -10236 -10225 -10214 -10203 -10192 -10181 -10171 + -10160 -10149 -10138 -10127 -10117 -10106 -10095 -10085 + -10074 -10063 -10052 -10042 -10031 -10021 -10010 -10000 + -9989 -9978 -9968 -9957 -9947 -9936 -9926 -9916 + -9905 -9895 -9884 -9874 -9864 -9853 -9843 -9833 + -9822 -9812 -9802 -9791 -9781 -9771 -9761 -9751 + -9741 -9730 -9720 -9710 -9700 -9690 -9680 -9670 + -9660 -9650 -9640 -9630 -9619 -9610 -9600 -9590 + -9580 -9570 -9560 -9550 -9540 -9530 -9520 -9511 + -9501 -9491 -9481 -9472 -9462 -9452 -9442 -9432 + -9423 -9413 -9403 -9394 -9384 -9374 -9365 -9355 + -9345 -9336 -9326 -9317 -9307 -9298 -9288 -9278 + -9269 -9259 -9250 -9241 -9231 -9221 -9212 -9202 + -9193 -9184 -9175 -9165 -9156 -9146 -9137 -9128 + -9119 -9109 -9100 -9090 -9081 -9072 -9063 -9053 + -9044 -9035 -9026 -9017 -9008 -8998 -8989 -8980 + -8971 -8962 -8953 -8944 -8934 -8925 -8916 -8907 + -8898 -8889 -8880 -8871 -8862 -8853 -8844 -8835 + -8826 -8817 -8808 -8799 -8790 -8781 -8772 -8764 + -8755 -8746 -8737 -8728 -8719 -8711 -8702 -8693 + -8684 -8675 -8667 -8658 -8649 -8640 -8632 -8623 + -8614 -8605 -8597 -8588 -8579 -8570 -8562 -8553 + -8545 -8536 -8527 -8519 -8510 -8502 -8493 -8484 + -8476 -8467 -8459 -8450 -8442 -8433 -8425 -8416 + -8408 -8399 -8391 -8382 -8374 -8365 -8357 -8348 + -8340 -8332 -8323 -8315 -8306 -8298 -8290 -8281 + -8273 -8264 -8256 -8248 -8240 -8231 -8223 -8215 + -8206 -8198 -8190 -8182 -8174 -8165 -8157 -8149 + -8140 -8132 -8124 -8116 -8108 -8099 -8091 -8083 + -8075 -8067 -8059 -8051 -8042 -8034 -8027 -8018 + -8010 -8002 -7994 -7986 -7978 -7970 -7962 -7954 + -7946 -7938 -7930 -7922 -7913 -7906 -7897 -7890 + -7882 -7874 -7866 -7858 -7850 -7842 -7834 -7826 + -7818 -7810 -7802 -7795 -7787 -7779 -7771 -7763 + -7755 -7748 -7739 -7732 -7724 -7716 -7708 -7700 + -7693 -7685 -7677 -7669 -7662 -7654 -7646 -7638 + -7630 -7623 -7615 -7608 -7600 -7592 -7584 -7577 + -7569 -7561 -7553 -7546 -7538 -7530 -7523 -7515 + -7508 -7500 -7492 -7485 -7477 -7469 -7462 -7454 + -7447 -7439 -7432 -7424 -7417 -7409 -7401 -7394 + -7386 -7379 -7372 -7364 -7356 -7349 -7341 -7334 + -7327 -7319 -7311 -7304 -7297 -7289 -7281 -7274 + -7267 -7259 -7252 -7245 -7237 -7230 -7222 -7215 + -7208 -7200 -7193 -7186 -7178 -7171 -7163 -7156 + -7149 -7141 -7134 -7127 -7119 -7112 -7105 -7098 + -7090 -7083 -7075 -7068 -7061 -7054 -7046 -7039 + -7032 -7025 -7018 -7010 -7003 -6996 -6989 -6981 + -6974 -6967 -6960 -6953 -6946 -6938 -6931 -6924 + -6917 -6910 -6903 -6895 -6888 -6881 -6874 -6867 + -6860 -6853 -6845 -6838 -6831 -6824 -6817 -6810 + -6803 -6796 -6789 -6782 -6775 -6767 -6760 -6753 + -6747 -6740 -6732 -6725 -6718 -6711 -6704 -6697 + -6690 -6683 -6676 -6669 -6662 -6655 -6648 -6641 + -6634 -6627 -6620 -6613 -6607 -6600 -6593 -6586 + -6579 -6572 -6565 -6558 -6551 -6544 -6538 -6531 + -6524 -6517 -6510 -6503 -6496 -6489 -6482 -6476 + -6469 -6462 -6455 -6448 -6441 -6434 -6428 -6421 + -6414 -6407 -6400 -6394 -6387 -6380 -6373 -6366 + -6360 -6353 -6346 -6339 -6333 -6326 -6319 -6312 + -6306 -6299 -6292 -6286 -6279 -6272 -6265 -6259 + -6252 -6245 -6239 -6232 -6225 -6219 -6212 -6205 + -6198 -6192 -6185 -6178 -6172 -6165 -6158 -6152 + -6145 -6139 -6132 -6125 -6119 -6112 -6105 -6099 + -6092 -6085 -6079 -6072 -6066 -6059 -6053 -6046 + -6040 -6033 -6026 -6019 -6013 -6006 -6000 -5993 + -5987 -5980 -5974 -5967 -5961 -5954 -5948 -5941 + -5935 -5928 -5922 -5915 -5908 -5902 -5895 -5889 + -5883 -5876 -5870 -5863 -5857 -5850 -5844 -5837 + -5831 -5825 -5818 -5811 -5805 -5799 -5792 -5786 + -5779 -5773 -5766 -5760 -5754 -5747 -5741 -5734 + -5728 -5722 -5715 -5709 -5702 -5696 -5690 -5683 + -5677 -5671 -5664 -5658 -5651 -5645 -5639 -5632 + -5626 -5620 -5613 -5607 -5600 -5594 -5588 -5582 + -5575 -5569 -5563 -5556 -5550 -5544 -5537 -5531 + -5525 -5519 -5512 -5506 -5500 -5494 -5487 -5481 + -5475 -5468 -5462 -5456 -5450 -5443 -5437 -5431 + -5425 -5418 -5412 -5406 -5400 -5393 -5387 -5381 + -5375 -5369 -5362 -5356 -5350 -5344 -5337 -5331 + -5325 -5319 -5313 -5306 -5300 -5294 -5288 -5282 + -5276 -5270 -5263 -5257 -5251 -5245 -5239 -5233 + -5226 -5220 -5214 -5208 -5202 -5196 -5190 -5183 + -5177 -5171 -5165 -5159 -5153 -5147 -5140 -5135 + -5129 -5122 -5116 -5110 -5104 -5098 -5092 -5086 + -5080 -5074 -5068 -5061 -5055 -5050 -5043 -5037 + -5031 -5025 -5019 -5013 -5007 -5001 -4995 -4989 + -4983 -4977 -4971 -4965 -4959 -4953 -4947 -4941 + -4935 -4929 -4923 -4917 -4911 -4905 -4899 -4893 + -4887 -4881 -4875 -4869 -4863 -4857 -4851 -4845 + -4839 -4833 -4827 -4821 -4815 -4809 -4803 -4797 + -4791 -4785 -4779 -4773 -4767 -4762 -4755 -4750 + -4744 -4738 -4732 -4726 -4720 -4714 -4708 -4702 + -4696 -4690 -4685 -4678 -4673 -4667 -4661 -4655 + -4649 -4643 -4637 -4631 -4626 -4620 -4614 -4608 + -4602 -4596 -4590 -4585 -4579 -4573 -4567 -4561 + -4555 -4549 -4544 -4538 -4532 -4526 -4520 -4514 + -4508 -4503 -4497 -4491 -4485 -4479 -4474 -4468 + -4462 -4456 -4450 -4445 -4439 -4433 -4427 -4421 + -4415 -4410 -4404 -4398 -4392 -4386 -4381 -4375 + -4369 -4363 -4358 -4352 -4346 -4340 -4334 -4329 + -4323 -4317 -4311 -4306 -4300 -4294 -4289 -4283 + -4277 -4271 -4266 -4260 -4254 -4248 -4243 -4237 + -4231 -4225 -4220 -4214 -4208 -4202 -4197 -4191 + -4185 -4180 -4174 -4168 -4162 -4157 -4151 -4146 + -4140 -4134 -4128 -4123 -4117 -4111 -4105 -4100 + -4094 -4089 -4083 -4077 -4071 -4066 -4060 -4055 + -4049 -4043 -4037 -4032 -4026 -4021 -4015 -4009 + -4003 -3998 -3992 -3987 -3981 -3975 -3970 -3964 + -3958 -3953 -3947 -3942 -3936 -3930 -3925 -3919 + -3913 -3908 -3902 -3897 -3891 -3885 -3880 -3874 + -3869 -3863 -3857 -3852 -3846 -3840 -3835 -3829 + -3824 -3818 -3813 -3807 -3801 -3796 -3790 -3785 + -3779 -3774 -3768 -3762 -3757 -3751 -3746 -3740 + -3734 -3729 -3723 -3718 -3712 -3707 -3701 -3696 + -3690 -3684 -3679 -3673 -3668 -3662 -3657 -3651 + -3646 -3640 -3635 -3629 -3624 -3618 -3613 -3607 + -3602 -3596 -3591 -3585 -3579 -3574 -3568 -3563 + -3557 -3552 -3546 -3541 -3535 -3530 -3524 -3519 + -3514 -3508 -3502 -3497 -3491 -3486 -3480 -3475 + -3469 -3464 -3459 -3453 -3448 -3442 -3437 -3431 + -3425 -3420 -3415 -3409 -3404 -3398 -3393 -3387 + -3382 -3376 -3371 -3366 -3360 -3355 -3349 -3344 + -3338 -3333 -3328 -3322 -3317 -3311 -3305 -3300 + -3295 -3289 -3284 -3278 -3273 -3268 -3262 -3257 + -3251 -3246 -3240 -3235 -3230 -3224 -3219 -3213 + -3208 -3203 -3197 -3192 -3186 -3181 -3176 -3170 + -3165 -3159 -3154 -3149 -3143 -3138 -3132 -3127 + -3122 -3116 -3111 -3105 -3100 -3095 -3089 -3084 + -3079 -3073 -3068 -3062 -3057 -3052 -3046 -3041 + -3036 -3030 -3025 -3019 -3014 -3009 -3003 -2998 + -2993 -2987 -2982 -2977 -2971 -2966 -2961 -2955 + -2950 -2944 -2939 -2934 -2928 -2923 -2918 -2912 + -2907 -2902 -2896 -2891 -2886 -2880 -2875 -2870 + -2864 -2859 -2854 -2848 -2843 -2838 -2832 -2827 + -2822 -2816 -2811 -2806 -2800 -2795 -2790 -2784 + -2779 -2774 -2768 -2763 -2758 -2753 -2747 -2742 + -2737 -2732 -2726 -2721 -2716 -2710 -2705 -2700 + -2694 -2689 -2684 -2678 -2673 -2668 -2663 -2657 + -2652 -2647 -2642 -2636 -2631 -2626 -2620 -2615 + -2610 -2605 -2599 -2594 -2589 -2583 -2578 -2573 + -2568 -2562 -2557 -2552 -2546 -2542 -2536 -2531 + -2526 -2520 -2515 -2510 -2505 -2499 -2494 -2489 + -2483 -2478 -2473 -2468 -2463 -2457 -2452 -2447 + -2442 -2436 -2431 -2426 -2421 -2415 -2410 -2405 + -2400 -2395 -2389 -2384 -2379 -2374 -2368 -2363 + -2358 -2353 -2347 -2342 -2337 -2332 -2327 -2321 + -2316 -2311 -2306 -2300 -2295 -2290 -2285 -2279 + -2275 -2269 -2264 -2259 -2254 -2248 -2243 -2238 + -2233 -2227 -2222 -2217 -2212 -2207 -2202 -2196 + -2191 -2186 -2181 -2175 -2170 -2165 -2160 -2155 + -2150 -2144 -2139 -2134 -2129 -2124 -2118 -2113 + -2108 -2103 -2098 -2093 -2087 -2082 -2077 -2072 + -2067 -2062 -2056 -2051 -2046 -2041 -2036 -2030 + -2025 -2020 -2015 -2010 -2005 -2000 -1994 -1989 + -1984 -1979 -1974 -1969 -1963 -1958 -1953 -1948 + -1943 -1937 -1932 -1927 -1922 -1917 -1912 -1907 + -1901 -1896 -1891 -1886 -1881 -1876 -1871 -1865 + -1860 -1855 -1850 -1845 -1840 -1835 -1829 -1824 + -1819 -1814 -1809 -1804 -1799 -1794 -1788 -1783 + -1778 -1773 -1768 -1763 -1758 -1752 -1747 -1742 + -1737 -1732 -1727 -1722 -1717 -1711 -1706 -1701 + -1696 -1691 -1686 -1681 -1676 -1670 -1665 -1660 + -1655 -1650 -1645 -1640 -1635 -1629 -1624 -1619 + -1614 -1609 -1604 -1599 -1594 -1589 -1584 -1579 + -1573 -1568 -1563 -1558 -1553 -1548 -1543 -1538 + -1532 -1527 -1522 -1517 -1512 -1507 -1502 -1497 + -1492 -1486 -1482 -1477 -1471 -1466 -1461 -1456 + -1451 -1446 -1441 -1436 -1431 -1425 -1420 -1415 + -1410 -1405 -1400 -1395 -1390 -1385 -1380 -1375 + -1370 -1364 -1359 -1354 -1349 -1344 -1339 -1334 + -1329 -1324 -1319 -1314 -1309 -1303 -1298 -1294 + -1288 -1283 -1278 -1273 -1268 -1263 -1258 -1253 + -1248 -1243 -1237 -1232 -1228 -1222 -1217 -1212 + -1207 -1202 -1197 -1192 -1187 -1182 -1177 -1171 + -1167 -1162 -1156 -1151 -1146 -1141 -1136 -1131 + -1126 -1121 -1116 -1111 -1106 -1101 -1096 -1091 + -1085 -1081 -1076 -1070 -1065 -1060 -1055 -1050 + -1045 -1040 -1035 -1030 -1025 -1020 -1015 -1010 + -1005 -1000 -995 -990 -985 -979 -974 -970 + -964 -959 -954 -949 -944 -939 -934 -929 + -924 -919 -914 -909 -904 -899 -894 -889 + -884 -879 -874 -868 -863 -859 -853 -848 + -843 -838 -833 -828 -823 -818 -813 -808 + -803 -798 -793 -788 -783 -778 -773 -768 + -763 -758 -752 -748 -743 -738 -732 -727 + -723 -717 -712 -707 -702 -697 -692 -687 + -682 -677 -672 -667 -662 -657 -652 -647 + -642 -637 -632 -627 -622 -617 -612 -607 + -602 -597 -591 -587 -582 -577 -571 -566 + -562 -557 -551 -546 -541 -537 -531 -526 + -521 -516 -511 -506 -501 -496 -491 -486 + -481 -476 -471 -466 -461 -456 -451 -446 + -441 -436 -431 -426 -421 -416 -411 -406 + -401 -396 -391 -386 -381 -376 -371 -366 + -360 -356 -351 -346 -340 -335 -331 -326 + -320 -315 -310 -306 -300 -295 -290 -285 + -281 -275 -270 -265 -261 -255 -250 -245 + -240 -235 -230 -225 -220 -215 -210 -205 + -200 -195 -190 -185 -180 -175 -170 -165 + -160 -155 -150 -145 -140 -135 -130 -125 + -120 -115 -110 -105 -100 -95 -90 -85 + -80 -75 -70 -65 -60 -55 -50 -45 + -40 -35 -29 -25 -20 -15 -9 -5 + 0 5 11 16 20 25 30 36 + 41 45 50 56 61 66 70 76 + 81 86 91 96 101 106 111 116 + 121 126 131 136 141 146 151 156 + 161 166 171 176 181 186 191 196 + 201 206 211 216 221 226 231 236 + 241 246 251 256 261 266 271 276 + 281 286 291 296 301 306 311 316 + 322 326 331 336 342 347 351 356 + 362 367 372 376 382 387 392 396 + 402 407 412 417 422 427 432 437 + 442 447 452 457 462 467 472 477 + 482 487 492 497 502 507 512 517 + 522 527 532 537 542 547 552 557 + 562 567 572 578 582 587 593 598 + 603 607 613 618 623 628 633 638 + 643 648 653 658 663 668 673 678 + 683 688 693 698 703 708 713 718 + 723 728 733 739 743 748 754 759 + 763 768 774 779 784 789 794 799 + 804 809 814 819 824 829 834 839 + 844 849 854 859 864 869 874 879 + 884 890 895 899 905 910 915 920 + 925 930 935 940 945 950 955 960 + 965 970 975 980 985 990 995 1001 + 1006 1010 1016 1021 1026 1031 1036 1041 + 1046 1051 1056 1061 1066 1071 1076 1081 + 1086 1092 1096 1102 1107 1112 1117 1122 + 1127 1132 1137 1142 1147 1152 1157 1162 + 1167 1173 1178 1183 1188 1193 1198 1203 + 1208 1213 1218 1223 1228 1233 1238 1244 + 1248 1254 1259 1264 1269 1274 1279 1284 + 1289 1294 1299 1304 1309 1314 1320 1325 + 1330 1335 1340 1345 1350 1355 1360 1365 + 1371 1375 1381 1386 1391 1396 1401 1406 + 1411 1416 1421 1426 1432 1436 1442 1447 + 1452 1457 1462 1467 1472 1477 1482 1488 + 1493 1497 1503 1508 1513 1518 1523 1528 + 1534 1538 1543 1549 1554 1559 1564 1569 + 1574 1579 1584 1590 1595 1600 1605 1610 + 1615 1620 1625 1630 1636 1640 1646 1651 + 1656 1661 1666 1671 1676 1681 1687 1692 + 1697 1702 1707 1712 1717 1722 1728 1733 + 1738 1743 1748 1753 1758 1764 1769 1774 + 1779 1784 1789 1794 1799 1805 1810 1815 + 1820 1825 1831 1835 1841 1846 1851 1856 + 1861 1866 1871 1877 1882 1887 1892 1897 + 1902 1908 1913 1918 1923 1928 1933 1939 + 1944 1949 1954 1959 1964 1969 1975 1980 + 1985 1990 1995 2000 2005 2011 2016 2021 + 2026 2031 2037 2042 2047 2052 2057 2062 + 2068 2073 2078 2083 2088 2093 2099 2104 + 2109 2114 2119 2125 2130 2135 2140 2145 + 2150 2156 2161 2166 2171 2177 2182 2187 + 2192 2197 2202 2208 2213 2218 2223 2229 + 2234 2239 2244 2249 2254 2260 2265 2270 + 2275 2281 2286 2291 2296 2302 2306 2312 + 2317 2322 2327 2333 2338 2343 2348 2354 + 2359 2364 2369 2374 2380 2385 2390 2395 + 2401 2406 2411 2416 2422 2427 2432 2437 + 2442 2448 2453 2458 2463 2469 2474 2479 + 2485 2490 2495 2500 2506 2511 2516 2521 + 2526 2532 2537 2542 2548 2553 2558 2563 + 2569 2574 2579 2585 2589 2595 2600 2605 + 2611 2616 2621 2627 2632 2637 2642 2648 + 2653 2658 2664 2669 2674 2680 2685 2690 + 2695 2700 2706 2711 2716 2722 2727 2732 + 2738 2743 2748 2754 2759 2764 2769 2775 + 2780 2785 2791 2796 2801 2807 2812 2817 + 2823 2828 2833 2839 2844 2849 2855 2860 + 2865 2870 2876 2881 2886 2892 2897 2902 + 2908 2913 2918 2924 2929 2935 2940 2945 + 2951 2956 2961 2967 2972 2977 2983 2988 + 2993 2999 3004 3010 3015 3020 3026 3031 + 3036 3042 3047 3052 3058 3063 3069 3074 + 3079 3085 3090 3095 3101 3106 3112 3117 + 3122 3128 3133 3139 3144 3149 3155 3160 + 3166 3171 3176 3182 3187 3193 3198 3203 + 3209 3214 3220 3225 3231 3236 3242 3247 + 3252 3258 3263 3269 3274 3279 3285 3290 + 3296 3301 3307 3312 3317 3323 3328 3334 + 3339 3345 3350 3355 3361 3367 3372 3378 + 3383 3388 3394 3399 3405 3410 3416 3421 + 3427 3432 3437 3443 3448 3454 3459 3465 + 3471 3476 3481 3487 3492 3498 3503 3509 + 3514 3520 3525 3531 3536 3542 3548 3553 + 3558 3564 3569 3575 3580 3586 3591 3597 + 3602 3608 3613 3619 3625 3630 3636 3641 + 3647 3652 3658 3663 3669 3675 3680 3686 + 3691 3697 3702 3708 3713 3719 3724 3730 + 3736 3741 3747 3752 3758 3763 3769 3774 + 3780 3786 3791 3797 3802 3808 3813 3819 + 3825 3830 3836 3842 3847 3853 3858 3864 + 3869 3875 3881 3886 3892 3898 3903 3909 + 3915 3920 3926 3931 3937 3942 3948 3954 + 3960 3965 3971 3976 3982 3987 3993 3999 + 4005 4010 4016 4021 4027 4033 4039 4044 + 4050 4055 4061 4067 4073 4078 4084 4089 + 4095 4101 4107 4112 4118 4123 4129 4135 + 4141 4146 4152 4158 4164 4169 4175 4181 + 4187 4192 4198 4203 4209 4215 4221 4226 + 4232 4238 4243 4249 4255 4261 4266 4272 + 4278 4284 4289 4295 4301 4307 4313 4318 + 4324 4330 4336 4341 4347 4353 4359 4364 + 4370 4376 4382 4388 4393 4399 4405 4411 + 4417 4422 4428 4434 4440 4445 4452 4457 + 4463 4469 4474 4481 4486 4492 4498 4504 + 4510 4515 4521 4527 4533 4539 4545 4551 + 4556 4562 4568 4574 4580 4585 4592 4597 + 4603 4609 4615 4621 4627 4633 4638 4644 + 4650 4656 4662 4668 4674 4680 4686 4692 + 4697 4703 4709 4715 4721 4727 4733 4739 + 4745 4751 4757 4762 4769 4774 4780 4786 + 4792 4798 4804 4810 4816 4822 4828 4834 + 4840 4846 4852 4858 4864 4870 4876 4882 + 4888 4894 4900 4906 4912 4918 4924 4930 + 4936 4942 4948 4954 4960 4966 4972 4978 + 4984 4990 4996 5002 5008 5014 5020 5026 + 5032 5038 5045 5050 5057 5063 5069 5075 + 5081 5087 5093 5099 5105 5111 5118 5123 + 5129 5136 5142 5148 5154 5160 5166 5172 + 5179 5185 5191 5197 5203 5209 5215 5221 + 5227 5233 5240 5246 5252 5258 5265 5271 + 5277 5283 5289 5295 5301 5308 5314 5320 + 5326 5333 5339 5345 5351 5357 5363 5369 + 5376 5382 5388 5394 5401 5407 5413 5419 + 5426 5432 5438 5444 5451 5457 5463 5469 + 5476 5482 5488 5494 5501 5507 5513 5520 + 5526 5532 5539 5545 5551 5557 5564 5570 + 5576 5583 5589 5596 5602 5608 5614 5621 + 5627 5634 5640 5646 5652 5659 5665 5672 + 5678 5684 5691 5697 5704 5710 5716 5723 + 5729 5736 5742 5748 5755 5761 5768 5774 + 5780 5787 5793 5800 5806 5813 5819 5826 + 5832 5838 5845 5852 5858 5864 5871 5877 + 5884 5890 5897 5903 5910 5916 5923 5929 + 5936 5942 5949 5956 5962 5968 5975 5981 + 5988 5994 6001 6008 6014 6021 6027 6034 + 6041 6047 6054 6060 6067 6074 6080 6087 + 6093 6100 6107 6113 6120 6126 6133 6140 + 6146 6153 6160 6167 6173 6180 6186 6193 + 6200 6206 6213 6220 6226 6233 6240 6246 + 6253 6260 6266 6273 6280 6287 6294 6300 + 6307 6314 6321 6327 6334 6341 6348 6354 + 6361 6368 6375 6382 6388 6395 6402 6409 + 6416 6422 6429 6436 6443 6450 6457 6463 + 6470 6477 6484 6491 6497 6504 6511 6518 + 6525 6532 6539 6546 6553 6559 6566 6573 + 6580 6587 6594 6601 6608 6615 6622 6629 + 6636 6643 6650 6657 6664 6671 6678 6685 + 6692 6699 6706 6713 6719 6727 6734 6741 + 6748 6755 6762 6769 6776 6783 6790 6797 + 6804 6811 6818 6826 6833 6840 6847 6854 + 6861 6868 6875 6883 6889 6897 6904 6911 + 6918 6925 6932 6939 6947 6954 6961 6969 + 6975 6983 6990 6997 7005 7012 7019 7026 + 7033 7041 7048 7055 7062 7070 7077 7084 + 7091 7099 7106 7114 7121 7128 7135 7143 + 7150 7157 7165 7172 7179 7187 7194 7202 + 7209 7216 7224 7231 7238 7246 7253 7261 + 7268 7276 7283 7290 7298 7306 7313 7320 + 7328 7336 7343 7350 7358 7365 7373 7381 + 7388 7395 7403 7410 7418 7426 7433 7441 + 7448 7456 7463 7471 7479 7486 7494 7501 + 7509 7517 7524 7532 7540 7547 7555 7563 + 7571 7578 7586 7594 7601 7609 7617 7624 + 7632 7640 7648 7655 7663 7671 7679 7687 + 7694 7702 7710 7718 7725 7733 7741 7749 + 7757 7765 7773 7780 7788 7796 7804 7812 + 7820 7828 7836 7843 7852 7859 7868 7875 + 7883 7891 7899 7907 7915 7923 7931 7939 + 7947 7955 7963 7971 7979 7988 7995 8004 + 8012 8020 8028 8036 8044 8052 8061 8069 + 8076 8085 8093 8101 8109 8117 8126 8134 + 8142 8150 8158 8167 8175 8183 8192 8200 + 8208 8217 8225 8233 8241 8250 8258 8266 + 8275 8283 8292 8300 8308 8317 8325 8333 + 8342 8350 8359 8367 8376 8384 8392 8401 + 8409 8418 8426 8435 8443 8452 8461 8469 + 8477 8486 8495 8503 8512 8520 8529 8538 + 8546 8555 8564 8573 8581 8590 8598 8607 + 8616 8625 8633 8642 8651 8659 8668 8677 + 8686 8695 8704 8712 8721 8730 8739 8748 + 8756 8765 8774 8783 8792 8801 8810 8819 + 8828 8837 8846 8855 8864 8873 8882 8891 + 8900 8909 8918 8927 8936 8945 8954 8964 + 8973 8982 8991 9000 9009 9019 9028 9037 + 9046 9055 9064 9074 9083 9092 9102 9111 + 9120 9130 9139 9148 9157 9167 9176 9186 + 9195 9205 9214 9223 9233 9242 9252 9261 + 9271 9280 9290 9300 9309 9318 9328 9338 + 9347 9357 9367 9376 9386 9395 9405 9415 + 9424 9434 9444 9454 9464 9473 9483 9493 + 9503 9513 9522 9532 9542 9552 9562 9572 + 9582 9592 9602 9612 9622 9632 9642 9652 + 9662 9672 9682 9692 9702 9712 9722 9733 + 9743 9753 9763 9773 9783 9794 9804 9814 + 9825 9835 9845 9855 9866 9876 9887 9897 + 9907 9918 9928 9939 9949 9960 9970 9981 + 9991 10002 10012 10023 10034 10044 10055 10066 + 10076 10087 10097 10108 10119 10130 10140 10152 + 10162 10173 10184 10195 10206 10217 10227 10238 + 10249 10260 10271 10282 10293 10304 10315 10326 + 10337 10349 10360 10371 10382 10394 10405 10416 + 10427 10438 10450 10461 10472 10484 10495 10507 + 10518 10530 10541 10553 10564 10575 10587 10598 + 10610 10622 10633 10645 10657 10668 10680 10692 + 10704 10715 10727 10739 10751 10763 10775 10786 + 10798 10811 10822 10834 10847 10858 10870 10883 + 10895 10907 10919 10931 10944 10956 10968 10981 + 10993 11005 11017 11030 11042 11055 11067 11080 + 11092 11105 11117 11130 11142 11155 11168 11180 + 11193 11206 11219 11232 11245 11257 11270 11283 + 11296 11309 11322 11335 11348 11361 11375 11388 + 11401 11414 11427 11441 11454 11467 11481 11494 + 11508 11521 11534 11548 11561 11575 11589 11602 + 11616 11630 11644 11657 11671 11685 11699 11713 + 11727 11741 11755 11769 11783 11797 11811 11826 + 11839 11854 11868 11882 11897 11911 11926 11940 + 11955 11969 11984 11998 12013 12028 12043 12057 + 12072 12087 12102 12117 12132 12147 12162 12177 + 12193 12208 12223 12238 12254 12269 12284 12299 + 12315 12331 12346 12362 12378 12393 12409 12425 + 12441 12457 12473 12489 12505 12521 12537 12553 + 12569 12586 12602 12619 12635 12651 12668 12684 + 12701 12718 12734 12751 12768 12785 12802 12819 + 12836 12853 12870 12888 12905 12922 12940 12957 + 12975 12993 13010 13028 13046 13064 13081 13099 + 13117 13135 13154 13172 13190 13209 13227 13246 + 13264 13283 13301 13320 13339 13358 13377 13396 + 13415 13434 13454 13473 13492 13512 13532 13551 + 13571 13591 13611 13631 13651 13671 13691 13711 + 13732 13752 13773 13793 13814 13835 13856 13877 + 13898 13919 13940 13962 13983 14005 14026 14048 + 14070 14092 14114 14136 14159 14181 14203 14226 + 14249 14272 14294 14318 14341 14364 14387 14411 + 14434 14458 14482 14506 14530 14554 14578 14603 + 14628 14653 14677 14703 14728 14753 14778 14804 + 14830 14855 14882 14908 14934 14961 14987 15014 + 15041 15068 15095 15123 15151 15179 15206 15235 + 15263 15291 15320 15349 15378 15408 15437 15466 + 15496 15527 15557 15587 15618 15649 15680 15712 + 15743 15775 15808 15840 15872 15906 15939 15972 + 16006 16040 16074 16108 16143 16178 16214 16249 + 16285 16322 16358 16395 16433 16470 16508 16547 + 16586 16624 16664 16704 16744 16785 16826 16867 + 16910 16952 16995 17038 17082 17126 17171 17217 + 17263 17309 17356 17403 17452 17501 17550 17600 + 17651 17702 17754 17807 17861 17915 17970 18026 + 18083 18141 18200 18259 18320 18382 18444 18508 + 18573 18639 18706 18775 18845 18917 18989 19064 + 19140 19217 19297 19378 19461 19547 19634 19724 + 19816 19911 20009 20109 20213 20319 20430 20544 + 20663 20786 20914 21047 21186 21331 21484 21644 + 21813 21991 22181 22384 22601 22836 23091 23370 + 23679 24027 24424 24888 25450 26164 27159 28858 diff --git a/netem/pareto b/netem/pareto new file mode 100755 index 0000000..8030cb8 Binary files /dev/null and b/netem/pareto differ diff --git a/netem/pareto.c b/netem/pareto.c new file mode 100644 index 0000000..8aa647b --- /dev/null +++ b/netem/pareto.c @@ -0,0 +1,41 @@ +/* + * Pareto distribution table generator + * Taken from the uncopyrighted NISTnet code. + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <limits.h> + +#include <linux/types.h> +#include <linux/pkt_sched.h> + +static const double a=3.0; +#define TABLESIZE 16384 +#define TABLEFACTOR NETEM_DIST_SCALE + +int +main(int argc, char **argv) +{ + int i, n; + double dvalue; + + printf("# This is the distribution table for the pareto distribution.\n"); + + for (i = 65536, n = 0; i > 0; i -= 16) { + dvalue = (double)i/(double)65536; + dvalue = 1.0/pow(dvalue, 1.0/a); + dvalue -= 1.5; + dvalue *= (4.0/3.0)*(double)TABLEFACTOR; + if (dvalue > 32767) + dvalue = 32767; + + printf(" %d", (int)rint(dvalue)); + if (++n == 8) { + putchar('\n'); + n = 0; + } + } + + return 0; +} diff --git a/netem/pareto.dist b/netem/pareto.dist new file mode 100644 index 0000000..295e6fb --- /dev/null +++ b/netem/pareto.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the pareto distribution. + -5461 -5460 -5460 -5459 -5458 -5457 -5456 -5455 + -5454 -5453 -5452 -5452 -5451 -5450 -5449 -5448 + -5447 -5446 -5445 -5444 -5443 -5443 -5442 -5441 + -5440 -5439 -5438 -5437 -5436 -5435 -5435 -5434 + -5433 -5432 -5431 -5430 -5429 -5428 -5427 -5426 + -5426 -5425 -5424 -5423 -5422 -5421 -5420 -5419 + -5418 -5417 -5417 -5416 -5415 -5414 -5413 -5412 + -5411 -5410 -5409 -5408 -5407 -5407 -5406 -5405 + -5404 -5403 -5402 -5401 -5400 -5399 -5398 -5397 + -5397 -5396 -5395 -5394 -5393 -5392 -5391 -5390 + -5389 -5388 -5387 -5387 -5386 -5385 -5384 -5383 + -5382 -5381 -5380 -5379 -5378 -5377 -5376 -5376 + -5375 -5374 -5373 -5372 -5371 -5370 -5369 -5368 + -5367 -5366 -5365 -5365 -5364 -5363 -5362 -5361 + -5360 -5359 -5358 -5357 -5356 -5355 -5354 -5353 + -5353 -5352 -5351 -5350 -5349 -5348 -5347 -5346 + -5345 -5344 -5343 -5342 -5341 -5340 -5340 -5339 + -5338 -5337 -5336 -5335 -5334 -5333 -5332 -5331 + -5330 -5329 -5328 -5327 -5327 -5326 -5325 -5324 + -5323 -5322 -5321 -5320 -5319 -5318 -5317 -5316 + -5315 -5314 -5313 -5312 -5312 -5311 -5310 -5309 + -5308 -5307 -5306 -5305 -5304 -5303 -5302 -5301 + -5300 -5299 -5298 -5297 -5296 -5296 -5295 -5294 + -5293 -5292 -5291 -5290 -5289 -5288 -5287 -5286 + -5285 -5284 -5283 -5282 -5281 -5280 -5279 -5278 + -5278 -5277 -5276 -5275 -5274 -5273 -5272 -5271 + -5270 -5269 -5268 -5267 -5266 -5265 -5264 -5263 + -5262 -5261 -5260 -5259 -5258 -5258 -5257 -5256 + -5255 -5254 -5253 -5252 -5251 -5250 -5249 -5248 + -5247 -5246 -5245 -5244 -5243 -5242 -5241 -5240 + -5239 -5238 -5237 -5236 -5235 -5234 -5233 -5233 + -5232 -5231 -5230 -5229 -5228 -5227 -5226 -5225 + -5224 -5223 -5222 -5221 -5220 -5219 -5218 -5217 + -5216 -5215 -5214 -5213 -5212 -5211 -5210 -5209 + -5208 -5207 -5206 -5205 -5204 -5203 -5202 -5201 + -5200 -5199 -5199 -5198 -5197 -5196 -5195 -5194 + -5193 -5192 -5191 -5190 -5189 -5188 -5187 -5186 + -5185 -5184 -5183 -5182 -5181 -5180 -5179 -5178 + -5177 -5176 -5175 -5174 -5173 -5172 -5171 -5170 + -5169 -5168 -5167 -5166 -5165 -5164 -5163 -5162 + -5161 -5160 -5159 -5158 -5157 -5156 -5155 -5154 + -5153 -5152 -5151 -5150 -5149 -5148 -5147 -5146 + -5145 -5144 -5143 -5142 -5141 -5140 -5139 -5138 + -5137 -5136 -5135 -5134 -5133 -5132 -5131 -5130 + -5129 -5128 -5127 -5126 -5125 -5124 -5123 -5122 + -5121 -5120 -5119 -5118 -5117 -5116 -5115 -5114 + -5113 -5112 -5111 -5110 -5109 -5108 -5107 -5106 + -5105 -5104 -5103 -5102 -5101 -5100 -5099 -5098 + -5097 -5096 -5095 -5094 -5093 -5092 -5091 -5090 + -5089 -5088 -5087 -5086 -5085 -5084 -5083 -5082 + -5081 -5080 -5079 -5078 -5077 -5076 -5075 -5074 + -5073 -5072 -5071 -5069 -5068 -5067 -5066 -5065 + -5064 -5063 -5062 -5061 -5060 -5059 -5058 -5057 + -5056 -5055 -5054 -5053 -5052 -5051 -5050 -5049 + -5048 -5047 -5046 -5045 -5044 -5043 -5042 -5041 + -5040 -5039 -5038 -5037 -5036 -5034 -5033 -5032 + -5031 -5030 -5029 -5028 -5027 -5026 -5025 -5024 + -5023 -5022 -5021 -5020 -5019 -5018 -5017 -5016 + -5015 -5014 -5013 -5012 -5011 -5009 -5008 -5007 + -5006 -5005 -5004 -5003 -5002 -5001 -5000 -4999 + -4998 -4997 -4996 -4995 -4994 -4993 -4992 -4991 + -4990 -4989 -4987 -4986 -4985 -4984 -4983 -4982 + -4981 -4980 -4979 -4978 -4977 -4976 -4975 -4974 + -4973 -4972 -4971 -4969 -4968 -4967 -4966 -4965 + -4964 -4963 -4962 -4961 -4960 -4959 -4958 -4957 + -4956 -4955 -4954 -4952 -4951 -4950 -4949 -4948 + -4947 -4946 -4945 -4944 -4943 -4942 -4941 -4940 + -4939 -4938 -4936 -4935 -4934 -4933 -4932 -4931 + -4930 -4929 -4928 -4927 -4926 -4925 -4924 -4922 + -4921 -4920 -4919 -4918 -4917 -4916 -4915 -4914 + -4913 -4912 -4911 -4909 -4908 -4907 -4906 -4905 + -4904 -4903 -4902 -4901 -4900 -4899 -4898 -4896 + -4895 -4894 -4893 -4892 -4891 -4890 -4889 -4888 + -4887 -4886 -4884 -4883 -4882 -4881 -4880 -4879 + -4878 -4877 -4876 -4875 -4874 -4872 -4871 -4870 + -4869 -4868 -4867 -4866 -4865 -4864 -4863 -4861 + -4860 -4859 -4858 -4857 -4856 -4855 -4854 -4853 + -4852 -4850 -4849 -4848 -4847 -4846 -4845 -4844 + -4843 -4842 -4840 -4839 -4838 -4837 -4836 -4835 + -4834 -4833 -4832 -4830 -4829 -4828 -4827 -4826 + -4825 -4824 -4823 -4822 -4820 -4819 -4818 -4817 + -4816 -4815 -4814 -4813 -4811 -4810 -4809 -4808 + -4807 -4806 -4805 -4804 -4803 -4801 -4800 -4799 + -4798 -4797 -4796 -4795 -4794 -4792 -4791 -4790 + -4789 -4788 -4787 -4786 -4784 -4783 -4782 -4781 + -4780 -4779 -4778 -4777 -4775 -4774 -4773 -4772 + -4771 -4770 -4769 -4767 -4766 -4765 -4764 -4763 + -4762 -4761 -4760 -4758 -4757 -4756 -4755 -4754 + -4753 -4752 -4750 -4749 -4748 -4747 -4746 -4745 + -4743 -4742 -4741 -4740 -4739 -4738 -4737 -4735 + -4734 -4733 -4732 -4731 -4730 -4729 -4727 -4726 + -4725 -4724 -4723 -4722 -4720 -4719 -4718 -4717 + -4716 -4715 -4714 -4712 -4711 -4710 -4709 -4708 + -4707 -4705 -4704 -4703 -4702 -4701 -4700 -4698 + -4697 -4696 -4695 -4694 -4693 -4691 -4690 -4689 + -4688 -4687 -4686 -4684 -4683 -4682 -4681 -4680 + -4679 -4677 -4676 -4675 -4674 -4673 -4672 -4670 + -4669 -4668 -4667 -4666 -4664 -4663 -4662 -4661 + -4660 -4659 -4657 -4656 -4655 -4654 -4653 -4651 + -4650 -4649 -4648 -4647 -4646 -4644 -4643 -4642 + -4641 -4640 -4638 -4637 -4636 -4635 -4634 -4632 + -4631 -4630 -4629 -4628 -4627 -4625 -4624 -4623 + -4622 -4621 -4619 -4618 -4617 -4616 -4615 -4613 + -4612 -4611 -4610 -4609 -4607 -4606 -4605 -4604 + -4603 -4601 -4600 -4599 -4598 -4597 -4595 -4594 + -4593 -4592 -4590 -4589 -4588 -4587 -4586 -4584 + -4583 -4582 -4581 -4580 -4578 -4577 -4576 -4575 + -4574 -4572 -4571 -4570 -4569 -4567 -4566 -4565 + -4564 -4563 -4561 -4560 -4559 -4558 -4556 -4555 + -4554 -4553 -4552 -4550 -4549 -4548 -4547 -4545 + -4544 -4543 -4542 -4541 -4539 -4538 -4537 -4536 + -4534 -4533 -4532 -4531 -4529 -4528 -4527 -4526 + -4525 -4523 -4522 -4521 -4520 -4518 -4517 -4516 + -4515 -4513 -4512 -4511 -4510 -4508 -4507 -4506 + -4505 -4503 -4502 -4501 -4500 -4498 -4497 -4496 + -4495 -4493 -4492 -4491 -4490 -4488 -4487 -4486 + -4485 -4483 -4482 -4481 -4480 -4478 -4477 -4476 + -4475 -4473 -4472 -4471 -4470 -4468 -4467 -4466 + -4465 -4463 -4462 -4461 -4460 -4458 -4457 -4456 + -4455 -4453 -4452 -4451 -4449 -4448 -4447 -4446 + -4444 -4443 -4442 -4441 -4439 -4438 -4437 -4435 + -4434 -4433 -4432 -4430 -4429 -4428 -4427 -4425 + -4424 -4423 -4421 -4420 -4419 -4418 -4416 -4415 + -4414 -4412 -4411 -4410 -4409 -4407 -4406 -4405 + -4404 -4402 -4401 -4400 -4398 -4397 -4396 -4394 + -4393 -4392 -4391 -4389 -4388 -4387 -4385 -4384 + -4383 -4382 -4380 -4379 -4378 -4376 -4375 -4374 + -4372 -4371 -4370 -4369 -4367 -4366 -4365 -4363 + -4362 -4361 -4359 -4358 -4357 -4356 -4354 -4353 + -4352 -4350 -4349 -4348 -4346 -4345 -4344 -4342 + -4341 -4340 -4338 -4337 -4336 -4335 -4333 -4332 + -4331 -4329 -4328 -4327 -4325 -4324 -4323 -4321 + -4320 -4319 -4317 -4316 -4315 -4313 -4312 -4311 + -4309 -4308 -4307 -4305 -4304 -4303 -4301 -4300 + -4299 -4297 -4296 -4295 -4293 -4292 -4291 -4289 + -4288 -4287 -4285 -4284 -4283 -4281 -4280 -4279 + -4277 -4276 -4275 -4273 -4272 -4271 -4269 -4268 + -4267 -4265 -4264 -4263 -4261 -4260 -4259 -4257 + -4256 -4254 -4253 -4252 -4250 -4249 -4248 -4246 + -4245 -4244 -4242 -4241 -4240 -4238 -4237 -4236 + -4234 -4233 -4231 -4230 -4229 -4227 -4226 -4225 + -4223 -4222 -4221 -4219 -4218 -4216 -4215 -4214 + -4212 -4211 -4210 -4208 -4207 -4205 -4204 -4203 + -4201 -4200 -4199 -4197 -4196 -4194 -4193 -4192 + -4190 -4189 -4188 -4186 -4185 -4183 -4182 -4181 + -4179 -4178 -4176 -4175 -4174 -4172 -4171 -4170 + -4168 -4167 -4165 -4164 -4163 -4161 -4160 -4158 + -4157 -4156 -4154 -4153 -4151 -4150 -4149 -4147 + -4146 -4144 -4143 -4142 -4140 -4139 -4137 -4136 + -4135 -4133 -4132 -4130 -4129 -4128 -4126 -4125 + -4123 -4122 -4120 -4119 -4118 -4116 -4115 -4113 + -4112 -4111 -4109 -4108 -4106 -4105 -4103 -4102 + -4101 -4099 -4098 -4096 -4095 -4094 -4092 -4091 + -4089 -4088 -4086 -4085 -4084 -4082 -4081 -4079 + -4078 -4076 -4075 -4073 -4072 -4071 -4069 -4068 + -4066 -4065 -4063 -4062 -4061 -4059 -4058 -4056 + -4055 -4053 -4052 -4050 -4049 -4048 -4046 -4045 + -4043 -4042 -4040 -4039 -4037 -4036 -4035 -4033 + -4032 -4030 -4029 -4027 -4026 -4024 -4023 -4021 + -4020 -4018 -4017 -4016 -4014 -4013 -4011 -4010 + -4008 -4007 -4005 -4004 -4002 -4001 -3999 -3998 + -3997 -3995 -3994 -3992 -3991 -3989 -3988 -3986 + -3985 -3983 -3982 -3980 -3979 -3977 -3976 -3974 + -3973 -3971 -3970 -3968 -3967 -3965 -3964 -3963 + -3961 -3960 -3958 -3957 -3955 -3954 -3952 -3951 + -3949 -3948 -3946 -3945 -3943 -3942 -3940 -3939 + -3937 -3936 -3934 -3933 -3931 -3930 -3928 -3927 + -3925 -3924 -3922 -3921 -3919 -3918 -3916 -3915 + -3913 -3912 -3910 -3909 -3907 -3905 -3904 -3902 + -3901 -3899 -3898 -3896 -3895 -3893 -3892 -3890 + -3889 -3887 -3886 -3884 -3883 -3881 -3880 -3878 + -3877 -3875 -3874 -3872 -3870 -3869 -3867 -3866 + -3864 -3863 -3861 -3860 -3858 -3857 -3855 -3854 + -3852 -3851 -3849 -3847 -3846 -3844 -3843 -3841 + -3840 -3838 -3837 -3835 -3834 -3832 -3830 -3829 + -3827 -3826 -3824 -3823 -3821 -3820 -3818 -3816 + -3815 -3813 -3812 -3810 -3809 -3807 -3805 -3804 + -3802 -3801 -3799 -3798 -3796 -3795 -3793 -3791 + -3790 -3788 -3787 -3785 -3784 -3782 -3780 -3779 + -3777 -3776 -3774 -3772 -3771 -3769 -3768 -3766 + -3765 -3763 -3761 -3760 -3758 -3757 -3755 -3753 + -3752 -3750 -3749 -3747 -3746 -3744 -3742 -3741 + -3739 -3738 -3736 -3734 -3733 -3731 -3730 -3728 + -3726 -3725 -3723 -3722 -3720 -3718 -3717 -3715 + -3713 -3712 -3710 -3709 -3707 -3705 -3704 -3702 + -3701 -3699 -3697 -3696 -3694 -3692 -3691 -3689 + -3688 -3686 -3684 -3683 -3681 -3680 -3678 -3676 + -3675 -3673 -3671 -3670 -3668 -3666 -3665 -3663 + -3662 -3660 -3658 -3657 -3655 -3653 -3652 -3650 + -3648 -3647 -3645 -3644 -3642 -3640 -3639 -3637 + -3635 -3634 -3632 -3630 -3629 -3627 -3625 -3624 + -3622 -3620 -3619 -3617 -3615 -3614 -3612 -3610 + -3609 -3607 -3605 -3604 -3602 -3600 -3599 -3597 + -3595 -3594 -3592 -3590 -3589 -3587 -3585 -3584 + -3582 -3580 -3579 -3577 -3575 -3574 -3572 -3570 + -3569 -3567 -3565 -3564 -3562 -3560 -3558 -3557 + -3555 -3553 -3552 -3550 -3548 -3547 -3545 -3543 + -3542 -3540 -3538 -3536 -3535 -3533 -3531 -3530 + -3528 -3526 -3524 -3523 -3521 -3519 -3518 -3516 + -3514 -3513 -3511 -3509 -3507 -3506 -3504 -3502 + -3501 -3499 -3497 -3495 -3494 -3492 -3490 -3488 + -3487 -3485 -3483 -3482 -3480 -3478 -3476 -3475 + -3473 -3471 -3469 -3468 -3466 -3464 -3462 -3461 + -3459 -3457 -3455 -3454 -3452 -3450 -3448 -3447 + -3445 -3443 -3441 -3440 -3438 -3436 -3434 -3433 + -3431 -3429 -3427 -3426 -3424 -3422 -3420 -3419 + -3417 -3415 -3413 -3412 -3410 -3408 -3406 -3404 + -3403 -3401 -3399 -3397 -3396 -3394 -3392 -3390 + -3388 -3387 -3385 -3383 -3381 -3380 -3378 -3376 + -3374 -3372 -3371 -3369 -3367 -3365 -3363 -3362 + -3360 -3358 -3356 -3354 -3353 -3351 -3349 -3347 + -3345 -3344 -3342 -3340 -3338 -3336 -3335 -3333 + -3331 -3329 -3327 -3326 -3324 -3322 -3320 -3318 + -3316 -3315 -3313 -3311 -3309 -3307 -3305 -3304 + -3302 -3300 -3298 -3296 -3295 -3293 -3291 -3289 + -3287 -3285 -3283 -3282 -3280 -3278 -3276 -3274 + -3272 -3271 -3269 -3267 -3265 -3263 -3261 -3259 + -3258 -3256 -3254 -3252 -3250 -3248 -3246 -3245 + -3243 -3241 -3239 -3237 -3235 -3233 -3232 -3230 + -3228 -3226 -3224 -3222 -3220 -3218 -3217 -3215 + -3213 -3211 -3209 -3207 -3205 -3203 -3202 -3200 + -3198 -3196 -3194 -3192 -3190 -3188 -3186 -3185 + -3183 -3181 -3179 -3177 -3175 -3173 -3171 -3169 + -3167 -3166 -3164 -3162 -3160 -3158 -3156 -3154 + -3152 -3150 -3148 -3146 -3144 -3143 -3141 -3139 + -3137 -3135 -3133 -3131 -3129 -3127 -3125 -3123 + -3121 -3119 -3117 -3116 -3114 -3112 -3110 -3108 + -3106 -3104 -3102 -3100 -3098 -3096 -3094 -3092 + -3090 -3088 -3086 -3084 -3082 -3081 -3079 -3077 + -3075 -3073 -3071 -3069 -3067 -3065 -3063 -3061 + -3059 -3057 -3055 -3053 -3051 -3049 -3047 -3045 + -3043 -3041 -3039 -3037 -3035 -3033 -3031 -3029 + -3027 -3025 -3023 -3021 -3019 -3017 -3015 -3013 + -3011 -3009 -3007 -3005 -3003 -3001 -2999 -2997 + -2995 -2993 -2991 -2989 -2987 -2985 -2983 -2981 + -2979 -2977 -2975 -2973 -2971 -2969 -2967 -2965 + -2963 -2961 -2959 -2957 -2955 -2953 -2951 -2949 + -2947 -2945 -2943 -2941 -2939 -2937 -2935 -2933 + -2931 -2928 -2926 -2924 -2922 -2920 -2918 -2916 + -2914 -2912 -2910 -2908 -2906 -2904 -2902 -2900 + -2898 -2896 -2893 -2891 -2889 -2887 -2885 -2883 + -2881 -2879 -2877 -2875 -2873 -2871 -2869 -2866 + -2864 -2862 -2860 -2858 -2856 -2854 -2852 -2850 + -2848 -2846 -2843 -2841 -2839 -2837 -2835 -2833 + -2831 -2829 -2827 -2825 -2822 -2820 -2818 -2816 + -2814 -2812 -2810 -2808 -2805 -2803 -2801 -2799 + -2797 -2795 -2793 -2791 -2788 -2786 -2784 -2782 + -2780 -2778 -2776 -2773 -2771 -2769 -2767 -2765 + -2763 -2761 -2758 -2756 -2754 -2752 -2750 -2748 + -2745 -2743 -2741 -2739 -2737 -2735 -2733 -2730 + -2728 -2726 -2724 -2722 -2719 -2717 -2715 -2713 + -2711 -2709 -2706 -2704 -2702 -2700 -2698 -2695 + -2693 -2691 -2689 -2687 -2684 -2682 -2680 -2678 + -2676 -2673 -2671 -2669 -2667 -2665 -2662 -2660 + -2658 -2656 -2654 -2651 -2649 -2647 -2645 -2642 + -2640 -2638 -2636 -2633 -2631 -2629 -2627 -2625 + -2622 -2620 -2618 -2616 -2613 -2611 -2609 -2607 + -2604 -2602 -2600 -2598 -2595 -2593 -2591 -2589 + -2586 -2584 -2582 -2579 -2577 -2575 -2573 -2570 + -2568 -2566 -2564 -2561 -2559 -2557 -2554 -2552 + -2550 -2548 -2545 -2543 -2541 -2538 -2536 -2534 + -2532 -2529 -2527 -2525 -2522 -2520 -2518 -2515 + -2513 -2511 -2508 -2506 -2504 -2501 -2499 -2497 + -2495 -2492 -2490 -2488 -2485 -2483 -2481 -2478 + -2476 -2474 -2471 -2469 -2467 -2464 -2462 -2459 + -2457 -2455 -2452 -2450 -2448 -2445 -2443 -2441 + -2438 -2436 -2434 -2431 -2429 -2426 -2424 -2422 + -2419 -2417 -2415 -2412 -2410 -2407 -2405 -2403 + -2400 -2398 -2396 -2393 -2391 -2388 -2386 -2384 + -2381 -2379 -2376 -2374 -2372 -2369 -2367 -2364 + -2362 -2359 -2357 -2355 -2352 -2350 -2347 -2345 + -2343 -2340 -2338 -2335 -2333 -2330 -2328 -2325 + -2323 -2321 -2318 -2316 -2313 -2311 -2308 -2306 + -2303 -2301 -2299 -2296 -2294 -2291 -2289 -2286 + -2284 -2281 -2279 -2276 -2274 -2271 -2269 -2266 + -2264 -2261 -2259 -2257 -2254 -2252 -2249 -2247 + -2244 -2242 -2239 -2237 -2234 -2232 -2229 -2227 + -2224 -2222 -2219 -2216 -2214 -2211 -2209 -2206 + -2204 -2201 -2199 -2196 -2194 -2191 -2189 -2186 + -2184 -2181 -2179 -2176 -2173 -2171 -2168 -2166 + -2163 -2161 -2158 -2156 -2153 -2150 -2148 -2145 + -2143 -2140 -2138 -2135 -2132 -2130 -2127 -2125 + -2122 -2120 -2117 -2114 -2112 -2109 -2107 -2104 + -2101 -2099 -2096 -2094 -2091 -2088 -2086 -2083 + -2081 -2078 -2075 -2073 -2070 -2067 -2065 -2062 + -2060 -2057 -2054 -2052 -2049 -2046 -2044 -2041 + -2038 -2036 -2033 -2031 -2028 -2025 -2023 -2020 + -2017 -2015 -2012 -2009 -2007 -2004 -2001 -1999 + -1996 -1993 -1991 -1988 -1985 -1983 -1980 -1977 + -1974 -1972 -1969 -1966 -1964 -1961 -1958 -1956 + -1953 -1950 -1947 -1945 -1942 -1939 -1937 -1934 + -1931 -1928 -1926 -1923 -1920 -1917 -1915 -1912 + -1909 -1907 -1904 -1901 -1898 -1896 -1893 -1890 + -1887 -1884 -1882 -1879 -1876 -1873 -1871 -1868 + -1865 -1862 -1860 -1857 -1854 -1851 -1848 -1846 + -1843 -1840 -1837 -1834 -1832 -1829 -1826 -1823 + -1820 -1818 -1815 -1812 -1809 -1806 -1804 -1801 + -1798 -1795 -1792 -1789 -1787 -1784 -1781 -1778 + -1775 -1772 -1770 -1767 -1764 -1761 -1758 -1755 + -1752 -1750 -1747 -1744 -1741 -1738 -1735 -1732 + -1729 -1727 -1724 -1721 -1718 -1715 -1712 -1709 + -1706 -1703 -1701 -1698 -1695 -1692 -1689 -1686 + -1683 -1680 -1677 -1674 -1671 -1668 -1666 -1663 + -1660 -1657 -1654 -1651 -1648 -1645 -1642 -1639 + -1636 -1633 -1630 -1627 -1624 -1621 -1618 -1615 + -1612 -1609 -1606 -1603 -1600 -1597 -1594 -1591 + -1589 -1586 -1583 -1580 -1577 -1574 -1571 -1567 + -1564 -1561 -1558 -1555 -1552 -1549 -1546 -1543 + -1540 -1537 -1534 -1531 -1528 -1525 -1522 -1519 + -1516 -1513 -1510 -1507 -1504 -1501 -1498 -1495 + -1491 -1488 -1485 -1482 -1479 -1476 -1473 -1470 + -1467 -1464 -1461 -1458 -1454 -1451 -1448 -1445 + -1442 -1439 -1436 -1433 -1430 -1426 -1423 -1420 + -1417 -1414 -1411 -1408 -1404 -1401 -1398 -1395 + -1392 -1389 -1386 -1382 -1379 -1376 -1373 -1370 + -1367 -1363 -1360 -1357 -1354 -1351 -1347 -1344 + -1341 -1338 -1335 -1331 -1328 -1325 -1322 -1319 + -1315 -1312 -1309 -1306 -1302 -1299 -1296 -1293 + -1290 -1286 -1283 -1280 -1277 -1273 -1270 -1267 + -1263 -1260 -1257 -1254 -1250 -1247 -1244 -1241 + -1237 -1234 -1231 -1227 -1224 -1221 -1218 -1214 + -1211 -1208 -1204 -1201 -1198 -1194 -1191 -1188 + -1184 -1181 -1178 -1174 -1171 -1168 -1164 -1161 + -1158 -1154 -1151 -1147 -1144 -1141 -1137 -1134 + -1131 -1127 -1124 -1120 -1117 -1114 -1110 -1107 + -1103 -1100 -1097 -1093 -1090 -1086 -1083 -1080 + -1076 -1073 -1069 -1066 -1062 -1059 -1056 -1052 + -1049 -1045 -1042 -1038 -1035 -1031 -1028 -1024 + -1021 -1017 -1014 -1010 -1007 -1003 -1000 -996 + -993 -989 -986 -982 -979 -975 -972 -968 + -965 -961 -958 -954 -951 -947 -944 -940 + -936 -933 -929 -926 -922 -919 -915 -911 + -908 -904 -901 -897 -894 -890 -886 -883 + -879 -876 -872 -868 -865 -861 -857 -854 + -850 -847 -843 -839 -836 -832 -828 -825 + -821 -817 -814 -810 -806 -803 -799 -795 + -792 -788 -784 -780 -777 -773 -769 -766 + -762 -758 -754 -751 -747 -743 -740 -736 + -732 -728 -725 -721 -717 -713 -709 -706 + -702 -698 -694 -691 -687 -683 -679 -675 + -672 -668 -664 -660 -656 -653 -649 -645 + -641 -637 -633 -630 -626 -622 -618 -614 + -610 -606 -602 -599 -595 -591 -587 -583 + -579 -575 -571 -567 -564 -560 -556 -552 + -548 -544 -540 -536 -532 -528 -524 -520 + -516 -512 -508 -504 -500 -496 -493 -489 + -485 -481 -477 -473 -469 -465 -461 -456 + -452 -448 -444 -440 -436 -432 -428 -424 + -420 -416 -412 -408 -404 -400 -396 -392 + -388 -383 -379 -375 -371 -367 -363 -359 + -355 -351 -346 -342 -338 -334 -330 -326 + -322 -317 -313 -309 -305 -301 -297 -292 + -288 -284 -280 -276 -271 -267 -263 -259 + -255 -250 -246 -242 -238 -233 -229 -225 + -221 -216 -212 -208 -204 -199 -195 -191 + -186 -182 -178 -173 -169 -165 -160 -156 + -152 -147 -143 -139 -134 -130 -126 -121 + -117 -113 -108 -104 -99 -95 -91 -86 + -82 -77 -73 -69 -64 -60 -55 -51 + -46 -42 -37 -33 -29 -24 -20 -15 + -11 -6 -2 3 7 12 16 21 + 25 30 34 39 44 48 53 57 + 62 66 71 76 80 85 89 94 + 99 103 108 112 117 122 126 131 + 136 140 145 150 154 159 164 168 + 173 178 182 187 192 196 201 206 + 211 215 220 225 230 234 239 244 + 249 253 258 263 268 273 277 282 + 287 292 297 302 306 311 316 321 + 326 331 336 341 345 350 355 360 + 365 370 375 380 385 390 395 400 + 405 409 414 419 424 429 434 439 + 444 449 454 459 464 470 475 480 + 485 490 495 500 505 510 515 520 + 525 530 536 541 546 551 556 561 + 566 572 577 582 587 592 597 603 + 608 613 618 623 629 634 639 644 + 650 655 660 665 671 676 681 687 + 692 697 703 708 713 719 724 729 + 735 740 745 751 756 761 767 772 + 778 783 789 794 799 805 810 816 + 821 827 832 838 843 849 854 860 + 865 871 876 882 887 893 899 904 + 910 915 921 927 932 938 943 949 + 955 960 966 972 977 983 989 994 + 1000 1006 1011 1017 1023 1029 1034 1040 + 1046 1052 1057 1063 1069 1075 1081 1086 + 1092 1098 1104 1110 1116 1121 1127 1133 + 1139 1145 1151 1157 1163 1169 1175 1181 + 1186 1192 1198 1204 1210 1216 1222 1228 + 1234 1240 1246 1252 1258 1265 1271 1277 + 1283 1289 1295 1301 1307 1313 1319 1326 + 1332 1338 1344 1350 1356 1363 1369 1375 + 1381 1387 1394 1400 1406 1412 1419 1425 + 1431 1438 1444 1450 1456 1463 1469 1475 + 1482 1488 1495 1501 1507 1514 1520 1527 + 1533 1539 1546 1552 1559 1565 1572 1578 + 1585 1591 1598 1604 1611 1617 1624 1631 + 1637 1644 1650 1657 1664 1670 1677 1684 + 1690 1697 1704 1710 1717 1724 1730 1737 + 1744 1751 1757 1764 1771 1778 1784 1791 + 1798 1805 1812 1819 1825 1832 1839 1846 + 1853 1860 1867 1874 1881 1888 1895 1902 + 1909 1916 1923 1930 1937 1944 1951 1958 + 1965 1972 1979 1986 1993 2000 2008 2015 + 2022 2029 2036 2043 2051 2058 2065 2072 + 2080 2087 2094 2101 2109 2116 2123 2131 + 2138 2145 2153 2160 2168 2175 2182 2190 + 2197 2205 2212 2220 2227 2235 2242 2250 + 2257 2265 2272 2280 2287 2295 2303 2310 + 2318 2326 2333 2341 2349 2356 2364 2372 + 2379 2387 2395 2403 2410 2418 2426 2434 + 2442 2450 2457 2465 2473 2481 2489 2497 + 2505 2513 2521 2529 2537 2545 2553 2561 + 2569 2577 2585 2593 2601 2609 2618 2626 + 2634 2642 2650 2658 2667 2675 2683 2691 + 2700 2708 2716 2725 2733 2741 2750 2758 + 2766 2775 2783 2792 2800 2809 2817 2826 + 2834 2843 2851 2860 2868 2877 2885 2894 + 2903 2911 2920 2929 2937 2946 2955 2964 + 2972 2981 2990 2999 3008 3016 3025 3034 + 3043 3052 3061 3070 3079 3088 3097 3106 + 3115 3124 3133 3142 3151 3160 3169 3178 + 3187 3197 3206 3215 3224 3233 3243 3252 + 3261 3271 3280 3289 3299 3308 3317 3327 + 3336 3346 3355 3365 3374 3384 3393 3403 + 3412 3422 3432 3441 3451 3461 3470 3480 + 3490 3499 3509 3519 3529 3539 3549 3558 + 3568 3578 3588 3598 3608 3618 3628 3638 + 3648 3658 3668 3678 3688 3699 3709 3719 + 3729 3739 3750 3760 3770 3781 3791 3801 + 3812 3822 3832 3843 3853 3864 3874 3885 + 3895 3906 3917 3927 3938 3948 3959 3970 + 3981 3991 4002 4013 4024 4035 4045 4056 + 4067 4078 4089 4100 4111 4122 4133 4144 + 4155 4167 4178 4189 4200 4211 4223 4234 + 4245 4256 4268 4279 4291 4302 4313 4325 + 4336 4348 4359 4371 4383 4394 4406 4418 + 4429 4441 4453 4465 4476 4488 4500 4512 + 4524 4536 4548 4560 4572 4584 4596 4608 + 4620 4632 4645 4657 4669 4681 4694 4706 + 4718 4731 4743 4756 4768 4781 4793 4806 + 4818 4831 4844 4856 4869 4882 4895 4908 + 4920 4933 4946 4959 4972 4985 4998 5011 + 5024 5037 5051 5064 5077 5090 5104 5117 + 5130 5144 5157 5171 5184 5198 5211 5225 + 5238 5252 5266 5280 5293 5307 5321 5335 + 5349 5363 5377 5391 5405 5419 5433 5447 + 5461 5476 5490 5504 5519 5533 5547 5562 + 5576 5591 5605 5620 5635 5649 5664 5679 + 5694 5709 5724 5738 5753 5768 5783 5799 + 5814 5829 5844 5859 5875 5890 5905 5921 + 5936 5952 5967 5983 5999 6014 6030 6046 + 6062 6078 6094 6110 6126 6142 6158 6174 + 6190 6206 6223 6239 6255 6272 6288 6305 + 6321 6338 6355 6371 6388 6405 6422 6439 + 6456 6473 6490 6507 6524 6541 6559 6576 + 6593 6611 6628 6646 6663 6681 6699 6716 + 6734 6752 6770 6788 6806 6824 6842 6860 + 6879 6897 6915 6934 6952 6971 6989 7008 + 7027 7046 7065 7083 7102 7121 7141 7160 + 7179 7198 7218 7237 7256 7276 7296 7315 + 7335 7355 7375 7395 7415 7435 7455 7475 + 7495 7516 7536 7556 7577 7598 7618 7639 + 7660 7681 7702 7723 7744 7765 7786 7808 + 7829 7851 7872 7894 7916 7938 7959 7981 + 8003 8026 8048 8070 8092 8115 8137 8160 + 8183 8206 8228 8251 8274 8298 8321 8344 + 8367 8391 8415 8438 8462 8486 8510 8534 + 8558 8582 8606 8631 8655 8680 8705 8729 + 8754 8779 8804 8830 8855 8880 8906 8931 + 8957 8983 9009 9035 9061 9087 9113 9140 + 9166 9193 9220 9247 9274 9301 9328 9356 + 9383 9411 9438 9466 9494 9522 9550 9579 + 9607 9636 9664 9693 9722 9751 9780 9810 + 9839 9869 9898 9928 9958 9988 10019 10049 + 10080 10110 10141 10172 10203 10235 10266 10298 + 10329 10361 10393 10426 10458 10490 10523 10556 + 10589 10622 10655 10689 10722 10756 10790 10824 + 10859 10893 10928 10963 10998 11033 11068 11104 + 11139 11175 11211 11248 11284 11321 11358 11395 + 11432 11470 11507 11545 11583 11622 11660 11699 + 11738 11777 11816 11856 11896 11936 11976 12017 + 12058 12098 12140 12181 12223 12265 12307 12349 + 12392 12435 12478 12522 12566 12609 12654 12698 + 12743 12788 12834 12879 12925 12971 13018 13065 + 13112 13159 13207 13255 13303 13352 13401 13450 + 13500 13550 13600 13651 13702 13753 13805 13857 + 13909 13962 14015 14069 14123 14177 14232 14287 + 14342 14398 14454 14511 14568 14626 14684 14742 + 14801 14860 14920 14980 15041 15102 15164 15226 + 15288 15351 15415 15479 15544 15609 15675 15741 + 15808 15875 15943 16011 16080 16150 16220 16291 + 16363 16435 16508 16581 16655 16730 16805 16881 + 16958 17036 17114 17193 17273 17353 17435 17517 + 17600 17683 17768 17853 17939 18027 18115 18203 + 18293 18384 18476 18569 18662 18757 18853 18950 + 19047 19146 19246 19348 19450 19554 19658 19764 + 19872 19980 20090 20201 20314 20428 20543 20660 + 20778 20898 21020 21143 21267 21394 21522 21652 + 21783 21917 22052 22189 22329 22470 22613 22759 + 22907 23056 23209 23363 23521 23680 23842 24007 + 24175 24345 24519 24695 24874 25057 25243 25432 + 25625 25821 26021 26225 26433 26645 26861 27081 + 27307 27537 27771 28011 28257 28508 28764 29027 + 29295 29570 29852 30141 30438 30742 31054 31374 + 31704 32042 32391 32750 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 diff --git a/netem/paretonormal b/netem/paretonormal new file mode 100755 index 0000000..925d18f Binary files /dev/null and b/netem/paretonormal differ diff --git a/netem/paretonormal.c b/netem/paretonormal.c new file mode 100644 index 0000000..c793df6 --- /dev/null +++ b/netem/paretonormal.c @@ -0,0 +1,90 @@ +/* + * Paretoormal distribution table generator + * + * This distribution is simply .25*normal + .75*pareto; a combination + * which seems to match experimentally observed distributions reasonably + * well, but is computationally easy to handle. + * The entries represent a scaled inverse of the cumulative distribution + * function. + * + * Taken from the uncopyrighted NISTnet code. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <limits.h> +#include <malloc.h> + +#include <linux/types.h> +#include <linux/pkt_sched.h> + +#define TABLESIZE 16384 +#define TABLEFACTOR NETEM_DIST_SCALE + +static double +normal(double x, double mu, double sigma) +{ + return .5 + .5*erf((x-mu)/(sqrt(2.0)*sigma)); +} + + +static const double a=3.0; + +static int +paretovalue(int i) +{ + double dvalue; + + i = 65536-4*i; + dvalue = (double)i/(double)65536; + dvalue = 1.0/pow(dvalue, 1.0/a); + dvalue -= 1.5; + dvalue *= (4.0/3.0)*(double)TABLEFACTOR; + if (dvalue > 32767) + dvalue = 32767; + return (int)rint(dvalue); +} + +int +main(int argc, char **argv) +{ + double x; + double *table; + int i,n; + + table = calloc(TABLESIZE+1, sizeof(double)); + if (!table) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + + for (x = -10.0; x < 10.05; x += .00005) { + i = (int)rint(TABLESIZE*normal(x, 0.0, 1.0)); + table[i] = x; + } + printf( +"# This is the distribution table for the paretonormal distribution.\n" + ); + + for (i = n = 0; i < TABLESIZE; i += 4) { + int normvalue, parvalue, value; + + normvalue = (int) rint(table[i]*TABLEFACTOR); + parvalue = paretovalue(i); + + value = (normvalue+3*parvalue)/4; + if (value < SHRT_MIN) value = SHRT_MIN; + if (value > SHRT_MAX) value = SHRT_MAX; + + printf(" %d", value); + if (++n == 8) { + putchar('\n'); + n = 0; + } + } + free(table); + + return 0; +} diff --git a/netem/paretonormal.dist b/netem/paretonormal.dist new file mode 100644 index 0000000..61be1b8 --- /dev/null +++ b/netem/paretonormal.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the paretonormal distribution. + -12305 -11171 -10812 -10586 -10418 -10284 -10172 -10075 + -9990 -9914 -9845 -9783 -9724 -9670 -9620 -9572 + -9527 -9485 -9445 -9406 -9370 -9335 -9302 -9269 + -9238 -9208 -9179 -9151 -9123 -9097 -9072 -9047 + -9023 -8999 -8976 -8954 -8932 -8910 -8889 -8869 + -8849 -8830 -8810 -8792 -8773 -8755 -8737 -8720 + -8702 -8685 -8670 -8653 -8637 -8621 -8606 -8590 + -8575 -8560 -8546 -8531 -8517 -8503 -8489 -8476 + -8462 -8449 -8436 -8423 -8410 -8397 -8384 -8372 + -8360 -8348 -8336 -8324 -8312 -8301 -8289 -8278 + -8266 -8255 -8244 -8234 -8223 -8212 -8201 -8191 + -8180 -8170 -8159 -8149 -8139 -8129 -8119 -8110 + -8100 -8090 -8081 -8071 -8062 -8052 -8043 -8033 + -8024 -8015 -8006 -7998 -7989 -7980 -7971 -7962 + -7954 -7945 -7936 -7928 -7919 -7911 -7903 -7894 + -7887 -7879 -7870 -7862 -7854 -7846 -7838 -7830 + -7822 -7815 -7807 -7799 -7791 -7784 -7777 -7769 + -7762 -7754 -7747 -7739 -7732 -7725 -7717 -7710 + -7703 -7696 -7689 -7682 -7675 -7668 -7661 -7654 + -7647 -7640 -7634 -7627 -7620 -7613 -7606 -7600 + -7593 -7586 -7580 -7573 -7567 -7561 -7554 -7548 + -7541 -7535 -7529 -7522 -7516 -7510 -7503 -7497 + -7491 -7485 -7478 -7472 -7466 -7461 -7455 -7449 + -7443 -7437 -7431 -7425 -7419 -7413 -7407 -7401 + -7395 -7389 -7383 -7378 -7372 -7366 -7360 -7355 + -7350 -7344 -7338 -7333 -7327 -7321 -7316 -7310 + -7305 -7299 -7294 -7288 -7283 -7277 -7272 -7266 + -7261 -7256 -7250 -7245 -7240 -7235 -7230 -7224 + -7219 -7214 -7209 -7204 -7198 -7193 -7188 -7183 + -7178 -7173 -7167 -7162 -7157 -7152 -7147 -7142 + -7137 -7132 -7127 -7122 -7117 -7112 -7107 -7103 + -7098 -7093 -7088 -7083 -7078 -7074 -7069 -7064 + -7059 -7054 -7049 -7045 -7040 -7035 -7030 -7026 + -7021 -7016 -7012 -7007 -7002 -6997 -6993 -6988 + -6984 -6979 -6974 -6970 -6965 -6961 -6956 -6951 + -6947 -6942 -6939 -6934 -6930 -6925 -6921 -6916 + -6912 -6907 -6903 -6898 -6894 -6889 -6885 -6881 + -6876 -6872 -6868 -6863 -6859 -6854 -6850 -6846 + -6842 -6837 -6833 -6829 -6824 -6820 -6816 -6811 + -6807 -6803 -6799 -6795 -6790 -6786 -6782 -6778 + -6774 -6769 -6765 -6761 -6757 -6753 -6749 -6745 + -6740 -6736 -6732 -6728 -6724 -6720 -6716 -6712 + -6708 -6704 -6700 -6696 -6692 -6688 -6684 -6680 + -6676 -6672 -6668 -6664 -6660 -6656 -6652 -6648 + -6644 -6640 -6636 -6632 -6628 -6624 -6620 -6617 + -6613 -6609 -6605 -6601 -6597 -6593 -6590 -6586 + -6582 -6578 -6574 -6570 -6567 -6563 -6559 -6555 + -6551 -6548 -6544 -6540 -6536 -6533 -6529 -6525 + -6521 -6518 -6514 -6510 -6506 -6503 -6499 -6495 + -6492 -6488 -6484 -6481 -6477 -6473 -6470 -6466 + -6462 -6459 -6455 -6451 -6448 -6444 -6441 -6437 + -6433 -6430 -6426 -6422 -6418 -6415 -6411 -6407 + -6404 -6400 -6397 -6393 -6390 -6386 -6383 -6379 + -6375 -6372 -6368 -6365 -6361 -6358 -6354 -6351 + -6347 -6344 -6340 -6337 -6333 -6330 -6326 -6323 + -6320 -6316 -6313 -6309 -6306 -6302 -6298 -6295 + -6291 -6288 -6284 -6281 -6278 -6274 -6271 -6268 + -6264 -6261 -6257 -6254 -6251 -6247 -6244 -6241 + -6237 -6234 -6230 -6227 -6224 -6220 -6216 -6213 + -6210 -6206 -6203 -6200 -6196 -6193 -6190 -6187 + -6183 -6180 -6177 -6173 -6170 -6167 -6164 -6160 + -6157 -6154 -6150 -6147 -6143 -6140 -6137 -6134 + -6130 -6127 -6124 -6121 -6117 -6114 -6111 -6108 + -6105 -6101 -6098 -6094 -6091 -6088 -6085 -6081 + -6078 -6075 -6072 -6069 -6066 -6062 -6059 -6056 + -6053 -6050 -6047 -6043 -6040 -6037 -6033 -6030 + -6027 -6024 -6021 -6018 -6015 -6011 -6008 -6005 + -6002 -5999 -5995 -5992 -5989 -5986 -5983 -5980 + -5977 -5974 -5971 -5967 -5964 -5961 -5958 -5954 + -5951 -5948 -5945 -5942 -5939 -5936 -5933 -5930 + -5927 -5924 -5921 -5917 -5914 -5911 -5908 -5905 + -5902 -5899 -5896 -5893 -5890 -5887 -5884 -5880 + -5877 -5874 -5871 -5868 -5865 -5862 -5859 -5857 + -5854 -5851 -5847 -5844 -5841 -5838 -5835 -5832 + -5829 -5826 -5823 -5820 -5817 -5814 -5811 -5808 + -5805 -5802 -5799 -5796 -5793 -5790 -5787 -5784 + -5781 -5778 -5775 -5772 -5769 -5766 -5763 -5760 + -5758 -5754 -5751 -5748 -5745 -5742 -5740 -5737 + -5734 -5731 -5727 -5724 -5722 -5719 -5716 -5713 + -5710 -5707 -5704 -5701 -5698 -5695 -5692 -5689 + -5687 -5684 -5681 -5678 -5675 -5672 -5669 -5666 + -5663 -5660 -5658 -5655 -5651 -5648 -5646 -5643 + -5640 -5637 -5634 -5632 -5629 -5625 -5622 -5620 + -5617 -5614 -5611 -5609 -5606 -5602 -5600 -5597 + -5594 -5591 -5588 -5586 -5582 -5579 -5577 -5574 + -5571 -5568 -5566 -5563 -5559 -5557 -5554 -5551 + -5548 -5546 -5543 -5539 -5537 -5534 -5531 -5528 + -5526 -5523 -5520 -5517 -5514 -5511 -5509 -5506 + -5503 -5501 -5497 -5494 -5492 -5489 -5486 -5483 + -5480 -5477 -5475 -5472 -5469 -5467 -5464 -5460 + -5458 -5455 -5452 -5450 -5447 -5444 -5441 -5438 + -5436 -5433 -5430 -5428 -5424 -5421 -5419 -5416 + -5414 -5411 -5408 -5405 -5402 -5399 -5397 -5394 + -5392 -5388 -5386 -5383 -5380 -5378 -5375 -5372 + -5369 -5366 -5364 -5361 -5358 -5355 -5352 -5350 + -5347 -5345 -5342 -5339 -5336 -5333 -5331 -5328 + -5326 -5322 -5320 -5317 -5314 -5312 -5309 -5306 + -5303 -5301 -5298 -5296 -5292 -5290 -5287 -5284 + -5282 -5279 -5276 -5273 -5271 -5268 -5266 -5262 + -5260 -5257 -5254 -5252 -5249 -5246 -5243 -5241 + -5238 -5236 -5233 -5230 -5227 -5225 -5222 -5219 + -5216 -5214 -5211 -5209 -5206 -5203 -5200 -5198 + -5195 -5193 -5190 -5187 -5184 -5182 -5179 -5176 + -5174 -5171 -5168 -5166 -5163 -5160 -5158 -5155 + -5153 -5149 -5147 -5144 -5142 -5139 -5136 -5133 + -5131 -5129 -5125 -5123 -5120 -5118 -5115 -5112 + -5109 -5107 -5104 -5102 -5099 -5096 -5094 -5091 + -5089 -5085 -5083 -5080 -5078 -5075 -5072 -5070 + -5067 -5065 -5062 -5059 -5057 -5054 -5051 -5049 + -5046 -5044 -5041 -5038 -5035 -5033 -5030 -5027 + -5025 -5022 -5020 -5017 -5014 -5012 -5009 -5007 + -5004 -5001 -4999 -4996 -4993 -4991 -4988 -4986 + -4983 -4980 -4978 -4975 -4973 -4970 -4967 -4965 + -4962 -4959 -4957 -4954 -4952 -4949 -4946 -4944 + -4941 -4938 -4936 -4933 -4931 -4928 -4925 -4923 + -4920 -4917 -4915 -4912 -4910 -4907 -4904 -4902 + -4900 -4897 -4894 -4892 -4889 -4886 -4884 -4881 + -4879 -4876 -4873 -4871 -4869 -4865 -4863 -4861 + -4858 -4855 -4853 -4850 -4848 -4845 -4842 -4840 + -4838 -4834 -4832 -4830 -4827 -4824 -4822 -4819 + -4816 -4814 -4812 -4809 -4806 -4804 -4801 -4798 + -4796 -4793 -4791 -4788 -4786 -4783 -4781 -4778 + -4775 -4773 -4770 -4767 -4765 -4763 -4760 -4757 + -4755 -4752 -4749 -4747 -4745 -4742 -4739 -4737 + -4735 -4732 -4729 -4727 -4724 -4721 -4719 -4716 + -4714 -4711 -4709 -4706 -4703 -4701 -4698 -4696 + -4693 -4691 -4688 -4686 -4683 -4680 -4678 -4676 + -4672 -4670 -4668 -4665 -4662 -4660 -4658 -4655 + -4652 -4650 -4647 -4645 -4642 -4640 -4637 -4635 + -4632 -4629 -4627 -4625 -4622 -4619 -4617 -4614 + -4612 -4609 -4606 -4604 -4602 -4599 -4596 -4594 + -4592 -4589 -4586 -4584 -4581 -4579 -4576 -4573 + -4571 -4569 -4566 -4563 -4561 -4558 -4556 -4553 + -4550 -4548 -4546 -4543 -4540 -4538 -4535 -4533 + -4530 -4527 -4525 -4523 -4520 -4518 -4515 -4512 + -4510 -4508 -4505 -4502 -4500 -4497 -4495 -4493 + -4490 -4487 -4485 -4482 -4480 -4477 -4474 -4472 + -4470 -4467 -4465 -4462 -4459 -4457 -4455 -4452 + -4449 -4446 -4444 -4442 -4439 -4437 -4434 -4431 + -4429 -4427 -4424 -4422 -4419 -4416 -4414 -4412 + -4409 -4407 -4404 -4401 -4399 -4396 -4394 -4392 + -4389 -4386 -4384 -4381 -4379 -4376 -4374 -4371 + -4368 -4366 -4364 -4361 -4359 -4356 -4353 -4351 + -4348 -4346 -4344 -4341 -4338 -4336 -4333 -4331 + -4328 -4326 -4324 -4321 -4318 -4315 -4313 -4311 + -4308 -4306 -4303 -4300 -4298 -4295 -4293 -4291 + -4288 -4286 -4283 -4280 -4278 -4275 -4273 -4270 + -4268 -4266 -4263 -4261 -4258 -4255 -4253 -4250 + -4248 -4245 -4243 -4241 -4238 -4235 -4232 -4230 + -4228 -4225 -4223 -4220 -4218 -4215 -4213 -4210 + -4207 -4205 -4202 -4200 -4198 -4195 -4193 -4190 + -4187 -4185 -4182 -4180 -4177 -4175 -4172 -4170 + -4168 -4165 -4163 -4160 -4157 -4155 -4152 -4150 + -4147 -4145 -4142 -4140 -4138 -4135 -4133 -4130 + -4127 -4124 -4122 -4119 -4117 -4115 -4112 -4110 + -4107 -4105 -4102 -4100 -4097 -4094 -4092 -4089 + -4087 -4084 -4082 -4079 -4077 -4075 -4072 -4070 + -4067 -4065 -4062 -4060 -4057 -4054 -4052 -4049 + -4047 -4044 -4042 -4039 -4037 -4034 -4032 -4029 + -4027 -4024 -4022 -4020 -4017 -4015 -4012 -4009 + -4006 -4004 -4001 -3999 -3996 -3994 -3991 -3989 + -3987 -3984 -3982 -3979 -3977 -3974 -3972 -3969 + -3967 -3964 -3962 -3959 -3957 -3954 -3952 -3949 + -3947 -3944 -3941 -3939 -3936 -3933 -3931 -3929 + -3926 -3924 -3921 -3919 -3916 -3914 -3911 -3909 + -3906 -3904 -3901 -3899 -3896 -3894 -3891 -3889 + -3886 -3884 -3881 -3879 -3876 -3874 -3871 -3869 + -3866 -3864 -3861 -3859 -3856 -3854 -3851 -3849 + -3846 -3844 -3841 -3839 -3836 -3833 -3831 -3828 + -3826 -3823 -3821 -3818 -3816 -3813 -3811 -3808 + -3806 -3803 -3801 -3798 -3796 -3793 -3791 -3788 + -3786 -3783 -3781 -3778 -3775 -3773 -3770 -3768 + -3765 -3763 -3760 -3758 -3755 -3753 -3750 -3748 + -3745 -3743 -3740 -3737 -3735 -3732 -3730 -3727 + -3725 -3722 -3720 -3717 -3715 -3712 -3709 -3707 + -3704 -3702 -3700 -3697 -3695 -3692 -3689 -3687 + -3685 -3682 -3680 -3677 -3675 -3672 -3669 -3667 + -3664 -3662 -3659 -3657 -3654 -3652 -3649 -3646 + -3644 -3641 -3639 -3636 -3634 -3631 -3629 -3626 + -3624 -3621 -3619 -3616 -3614 -3611 -3609 -3606 + -3604 -3601 -3598 -3596 -3593 -3591 -3588 -3585 + -3583 -3580 -3578 -3575 -3573 -3571 -3568 -3566 + -3563 -3561 -3558 -3555 -3553 -3550 -3548 -3545 + -3542 -3540 -3537 -3535 -3532 -3530 -3528 -3525 + -3522 -3520 -3517 -3515 -3512 -3509 -3507 -3504 + -3502 -3499 -3496 -3494 -3492 -3489 -3487 -3484 + -3482 -3479 -3476 -3474 -3471 -3469 -3466 -3463 + -3461 -3458 -3456 -3454 -3451 -3448 -3446 -3443 + -3441 -3438 -3435 -3433 -3430 -3428 -3425 -3423 + -3420 -3418 -3415 -3413 -3410 -3407 -3405 -3402 + -3399 -3397 -3395 -3392 -3390 -3387 -3384 -3382 + -3379 -3376 -3374 -3371 -3369 -3367 -3364 -3361 + -3359 -3356 -3353 -3351 -3348 -3345 -3343 -3341 + -3338 -3336 -3333 -3330 -3328 -3325 -3322 -3320 + -3318 -3315 -3313 -3310 -3307 -3305 -3302 -3299 + -3297 -3294 -3292 -3290 -3287 -3284 -3281 -3279 + -3276 -3273 -3271 -3269 -3266 -3264 -3261 -3258 + -3256 -3253 -3250 -3248 -3246 -3243 -3240 -3238 + -3235 -3232 -3229 -3227 -3225 -3222 -3220 -3217 + -3214 -3212 -3209 -3206 -3204 -3202 -3199 -3196 + -3194 -3191 -3188 -3186 -3184 -3181 -3178 -3175 + -3173 -3170 -3167 -3165 -3163 -3160 -3157 -3155 + -3152 -3149 -3147 -3144 -3142 -3139 -3136 -3134 + -3131 -3128 -3126 -3124 -3121 -3118 -3115 -3113 + -3110 -3108 -3105 -3103 -3100 -3097 -3094 -3092 + -3090 -3087 -3084 -3082 -3079 -3076 -3074 -3071 + -3069 -3066 -3063 -3061 -3058 -3056 -3053 -3050 + -3048 -3045 -3042 -3040 -3037 -3035 -3032 -3029 + -3026 -3024 -3021 -3019 -3016 -3014 -3011 -3008 + -3005 -3003 -3001 -2998 -2995 -2992 -2990 -2987 + -2985 -2982 -2979 -2976 -2974 -2972 -2969 -2966 + -2963 -2961 -2958 -2956 -2953 -2950 -2948 -2945 + -2942 -2940 -2937 -2935 -2932 -2929 -2926 -2924 + -2921 -2919 -2916 -2913 -2911 -2908 -2905 -2903 + -2900 -2897 -2895 -2892 -2890 -2887 -2884 -2881 + -2879 -2876 -2873 -2871 -2868 -2865 -2863 -2860 + -2857 -2855 -2852 -2850 -2847 -2844 -2841 -2839 + -2836 -2834 -2831 -2828 -2825 -2823 -2820 -2818 + -2815 -2812 -2809 -2807 -2804 -2801 -2799 -2796 + -2794 -2791 -2788 -2785 -2782 -2780 -2778 -2775 + -2772 -2769 -2767 -2764 -2761 -2758 -2756 -2753 + -2751 -2748 -2745 -2742 -2740 -2737 -2734 -2732 + -2729 -2726 -2724 -2721 -2718 -2715 -2713 -2710 + -2707 -2705 -2702 -2700 -2697 -2694 -2691 -2689 + -2686 -2683 -2680 -2677 -2675 -2673 -2670 -2667 + -2664 -2662 -2659 -2656 -2653 -2650 -2648 -2645 + -2642 -2639 -2637 -2635 -2632 -2629 -2626 -2623 + -2621 -2618 -2615 -2612 -2610 -2607 -2604 -2601 + -2599 -2596 -2593 -2590 -2588 -2586 -2583 -2580 + -2577 -2575 -2572 -2569 -2566 -2563 -2561 -2558 + -2555 -2552 -2550 -2547 -2544 -2541 -2539 -2536 + -2533 -2530 -2528 -2525 -2522 -2519 -2516 -2514 + -2511 -2508 -2505 -2503 -2500 -2497 -2494 -2492 + -2489 -2486 -2483 -2481 -2478 -2475 -2472 -2470 + -2467 -2464 -2461 -2458 -2456 -2453 -2450 -2447 + -2445 -2442 -2439 -2436 -2434 -2431 -2428 -2425 + -2423 -2420 -2417 -2414 -2412 -2409 -2406 -2403 + -2401 -2398 -2395 -2392 -2390 -2387 -2384 -2381 + -2379 -2375 -2372 -2369 -2367 -2364 -2361 -2358 + -2356 -2353 -2350 -2347 -2345 -2342 -2339 -2336 + -2334 -2331 -2327 -2325 -2322 -2319 -2316 -2314 + -2311 -2308 -2305 -2303 -2300 -2297 -2294 -2291 + -2288 -2285 -2282 -2280 -2277 -2274 -2271 -2269 + -2266 -2263 -2260 -2257 -2254 -2251 -2249 -2246 + -2243 -2240 -2238 -2235 -2231 -2229 -2226 -2223 + -2220 -2218 -2215 -2212 -2209 -2206 -2203 -2200 + -2198 -2195 -2192 -2189 -2186 -2183 -2180 -2178 + -2175 -2172 -2169 -2166 -2163 -2160 -2158 -2155 + -2152 -2149 -2146 -2143 -2140 -2137 -2135 -2132 + -2129 -2126 -2123 -2120 -2118 -2115 -2112 -2108 + -2106 -2103 -2100 -2097 -2094 -2091 -2088 -2086 + -2083 -2080 -2077 -2074 -2071 -2068 -2066 -2062 + -2059 -2057 -2054 -2051 -2048 -2045 -2042 -2039 + -2037 -2033 -2030 -2028 -2025 -2022 -2019 -2016 + -2013 -2010 -2008 -2004 -2001 -1999 -1996 -1992 + -1990 -1987 -1984 -1981 -1978 -1975 -1972 -1970 + -1966 -1963 -1960 -1958 -1954 -1952 -1949 -1946 + -1942 -1940 -1937 -1934 -1931 -1928 -1925 -1922 + -1919 -1916 -1913 -1910 -1907 -1904 -1902 -1898 + -1895 -1893 -1890 -1886 -1884 -1881 -1877 -1875 + -1872 -1869 -1866 -1863 -1860 -1857 -1854 -1851 + -1848 -1845 -1842 -1839 -1836 -1833 -1830 -1827 + -1824 -1821 -1818 -1815 -1812 -1809 -1806 -1803 + -1801 -1797 -1794 -1792 -1788 -1785 -1783 -1779 + -1776 -1774 -1770 -1767 -1764 -1761 -1758 -1755 + -1752 -1749 -1746 -1743 -1740 -1737 -1734 -1731 + -1728 -1725 -1722 -1719 -1716 -1712 -1710 -1707 + -1703 -1701 -1698 -1694 -1692 -1688 -1685 -1683 + -1679 -1676 -1674 -1670 -1667 -1664 -1661 -1658 + -1655 -1652 -1649 -1646 -1643 -1640 -1637 -1633 + -1631 -1627 -1624 -1621 -1618 -1615 -1612 -1609 + -1606 -1603 -1600 -1596 -1594 -1590 -1587 -1584 + -1581 -1578 -1575 -1572 -1569 -1566 -1562 -1560 + -1556 -1553 -1551 -1547 -1544 -1541 -1538 -1535 + -1532 -1528 -1526 -1522 -1519 -1516 -1513 -1509 + -1507 -1503 -1500 -1498 -1494 -1491 -1488 -1485 + -1482 -1479 -1475 -1473 -1469 -1466 -1463 -1460 + -1457 -1454 -1450 -1447 -1444 -1441 -1438 -1434 + -1432 -1428 -1425 -1422 -1419 -1415 -1413 -1409 + -1406 -1403 -1400 -1397 -1393 -1390 -1387 -1384 + -1381 -1378 -1374 -1372 -1368 -1365 -1362 -1358 + -1355 -1352 -1349 -1346 -1342 -1339 -1336 -1333 + -1330 -1327 -1323 -1320 -1317 -1314 -1311 -1307 + -1304 -1301 -1298 -1295 -1291 -1288 -1285 -1281 + -1279 -1275 -1272 -1269 -1265 -1262 -1259 -1256 + -1253 -1249 -1246 -1243 -1239 -1236 -1233 -1230 + -1226 -1223 -1220 -1217 -1214 -1210 -1207 -1204 + -1200 -1197 -1194 -1190 -1188 -1184 -1181 -1178 + -1174 -1171 -1168 -1165 -1161 -1158 -1155 -1151 + -1148 -1145 -1141 -1138 -1135 -1132 -1128 -1125 + -1122 -1118 -1115 -1112 -1108 -1105 -1102 -1099 + -1095 -1092 -1089 -1085 -1082 -1078 -1075 -1072 + -1068 -1066 -1062 -1059 -1055 -1052 -1049 -1045 + -1042 -1038 -1035 -1032 -1028 -1025 -1022 -1019 + -1015 -1012 -1009 -1005 -1002 -998 -995 -992 + -988 -985 -981 -978 -975 -971 -968 -964 + -961 -958 -955 -951 -947 -944 -941 -938 + -934 -931 -927 -924 -921 -917 -914 -910 + -907 -903 -900 -897 -893 -890 -886 -883 + -879 -876 -873 -869 -866 -862 -859 -855 + -852 -849 -845 -842 -838 -835 -831 -828 + -824 -821 -818 -814 -811 -807 -804 -800 + -797 -793 -790 -786 -782 -779 -776 -773 + -769 -765 -762 -758 -755 -751 -748 -744 + -741 -737 -734 -730 -727 -723 -719 -716 + -712 -709 -705 -702 -698 -695 -691 -688 + -685 -681 -678 -674 -671 -667 -664 -659 + -656 -652 -649 -645 -642 -638 -634 -631 + -627 -624 -620 -617 -613 -610 -606 -603 + -599 -595 -592 -588 -585 -581 -578 -574 + -570 -566 -563 -559 -556 -552 -549 -545 + -541 -538 -534 -531 -527 -523 -519 -516 + -512 -509 -505 -502 -498 -494 -490 -487 + -483 -480 -476 -472 -468 -465 -461 -457 + -454 -450 -447 -443 -439 -435 -432 -428 + -425 -420 -417 -413 -410 -406 -402 -398 + -395 -391 -388 -383 -380 -376 -373 -369 + -365 -361 -358 -354 -350 -346 -343 -339 + -336 -331 -328 -324 -320 -316 -313 -309 + -305 -301 -298 -294 -290 -286 -283 -279 + -275 -271 -268 -263 -260 -256 -253 -248 + -245 -241 -237 -233 -230 -225 -222 -218 + -214 -210 -207 -202 -199 -195 -191 -187 + -184 -179 -176 -171 -168 -164 -160 -156 + -153 -149 -145 -141 -137 -133 -129 -126 + -121 -118 -114 -110 -106 -102 -98 -95 + -90 -87 -82 -79 -75 -71 -67 -63 + -59 -55 -52 -47 -44 -39 -36 -31 + -28 -23 -20 -15 -12 -8 -4 0 + 3 8 11 16 19 23 27 31 + 35 39 43 47 51 55 59 63 + 67 71 75 79 83 87 91 96 + 99 104 107 112 115 119 124 127 + 132 135 140 144 148 152 156 160 + 164 168 172 177 180 185 189 193 + 197 201 205 209 214 217 222 226 + 230 234 238 243 246 251 255 259 + 263 267 272 275 280 284 288 292 + 296 301 304 309 313 317 322 325 + 330 334 338 342 346 351 355 360 + 363 368 372 376 381 384 389 393 + 397 402 406 410 414 419 423 427 + 432 436 440 444 448 453 457 462 + 466 470 475 479 483 487 492 496 + 500 505 509 513 518 522 527 531 + 535 540 544 549 553 557 561 565 + 570 574 579 583 587 592 596 601 + 606 610 614 619 623 628 632 636 + 641 645 650 654 658 663 667 672 + 676 681 686 690 694 699 703 708 + 712 716 722 726 730 735 739 744 + 748 753 758 762 766 771 775 780 + 785 789 794 798 803 808 812 816 + 821 826 830 835 839 844 849 853 + 858 863 867 872 876 881 886 890 + 895 900 904 909 914 918 923 928 + 932 937 942 946 951 956 960 965 + 970 974 979 984 989 994 998 1003 + 1008 1013 1017 1022 1027 1031 1037 1041 + 1046 1051 1056 1060 1065 1070 1074 1079 + 1084 1089 1094 1099 1103 1108 1113 1118 + 1123 1128 1132 1137 1143 1147 1152 1157 + 1162 1166 1171 1177 1181 1186 1191 1196 + 1201 1206 1211 1215 1221 1226 1230 1236 + 1241 1245 1250 1256 1260 1265 1271 1275 + 1280 1285 1290 1295 1300 1305 1310 1315 + 1320 1325 1330 1335 1341 1345 1350 1356 + 1361 1365 1370 1376 1381 1386 1391 1396 + 1401 1406 1412 1417 1421 1426 1432 1437 + 1442 1447 1453 1458 1462 1468 1473 1478 + 1483 1489 1494 1499 1504 1510 1515 1520 + 1525 1530 1535 1540 1546 1551 1556 1561 + 1567 1572 1577 1582 1588 1594 1599 1604 + 1609 1615 1620 1625 1630 1636 1641 1646 + 1651 1657 1663 1668 1673 1679 1684 1689 + 1694 1700 1706 1711 1716 1721 1727 1733 + 1738 1743 1749 1754 1760 1765 1770 1776 + 1782 1787 1792 1798 1804 1809 1814 1820 + 1825 1831 1837 1842 1847 1853 1859 1864 + 1870 1875 1881 1887 1892 1897 1903 1909 + 1915 1920 1926 1931 1937 1943 1948 1954 + 1959 1965 1971 1977 1982 1988 1993 1999 + 2005 2011 2016 2022 2028 2034 2040 2045 + 2051 2056 2062 2068 2074 2080 2085 2091 + 2097 2103 2109 2115 2120 2126 2132 2138 + 2144 2150 2155 2161 2167 2173 2179 2185 + 2191 2197 2202 2208 2214 2221 2227 2232 + 2238 2244 2250 2256 2263 2268 2274 2280 + 2286 2292 2298 2304 2311 2317 2323 2329 + 2334 2340 2346 2353 2359 2365 2371 2377 + 2383 2389 2395 2402 2408 2414 2421 2427 + 2433 2439 2445 2451 2457 2463 2470 2476 + 2483 2489 2495 2501 2507 2514 2520 2526 + 2532 2538 2545 2552 2558 2564 2571 2577 + 2583 2590 2596 2602 2608 2615 2621 2628 + 2634 2641 2648 2654 2660 2667 2673 2680 + 2686 2692 2699 2705 2712 2718 2725 2731 + 2738 2744 2751 2758 2764 2771 2778 2784 + 2791 2798 2804 2811 2818 2824 2831 2838 + 2844 2851 2858 2864 2871 2878 2884 2891 + 2898 2905 2911 2918 2925 2932 2938 2945 + 2952 2959 2966 2973 2979 2986 2993 3000 + 3007 3014 3021 3028 3035 3042 3049 3056 + 3063 3070 3077 3084 3091 3097 3104 3111 + 3118 3125 3132 3139 3146 3153 3161 3168 + 3175 3182 3189 3196 3204 3211 3218 3225 + 3233 3240 3247 3254 3261 3268 3275 3283 + 3290 3297 3305 3312 3320 3327 3334 3341 + 3348 3356 3363 3371 3378 3386 3393 3401 + 3408 3415 3422 3430 3437 3445 3453 3460 + 3468 3475 3482 3490 3498 3505 3513 3521 + 3528 3536 3543 3551 3558 3566 3574 3582 + 3589 3597 3604 3612 3620 3628 3636 3643 + 3651 3659 3667 3675 3683 3690 3698 3706 + 3714 3722 3730 3737 3745 3753 3762 3770 + 3777 3785 3793 3801 3809 3817 3825 3833 + 3842 3850 3857 3866 3874 3882 3890 3898 + 3906 3915 3923 3931 3939 3948 3956 3964 + 3972 3981 3989 3997 4005 4014 4022 4030 + 4039 4047 4055 4064 4072 4081 4089 4098 + 4106 4115 4123 4132 4141 4148 4157 4166 + 4175 4183 4192 4201 4209 4218 4227 4235 + 4244 4253 4261 4270 4279 4287 4296 4305 + 4313 4323 4332 4340 4349 4358 4367 4376 + 4385 4394 4403 4411 4421 4430 4438 4448 + 4457 4466 4475 4484 4493 4502 4511 4521 + 4529 4539 4548 4557 4567 4576 4585 4594 + 4604 4613 4622 4632 4641 4651 4660 4669 + 4679 4688 4698 4707 4717 4726 4736 4745 + 4755 4764 4774 4783 4793 4803 4813 4822 + 4832 4841 4852 4861 4871 4881 4891 4900 + 4911 4920 4930 4940 4950 4960 4970 4980 + 4990 5000 5010 5020 5030 5040 5050 5060 + 5071 5080 5091 5101 5112 5122 5131 5142 + 5152 5163 5173 5183 5194 5204 5215 5225 + 5235 5246 5257 5267 5278 5288 5299 5310 + 5320 5330 5342 5352 5363 5374 5384 5395 + 5406 5417 5427 5438 5450 5460 5471 5482 + 5493 5504 5515 5526 5537 5548 5559 5571 + 5582 5593 5604 5615 5627 5638 5649 5660 + 5672 5683 5695 5706 5717 5729 5741 5752 + 5763 5775 5786 5798 5810 5822 5833 5845 + 5856 5868 5880 5892 5904 5916 5928 5940 + 5951 5963 5975 5987 5999 6011 6024 6036 + 6048 6060 6073 6085 6097 6109 6122 6134 + 6146 6159 6171 6184 6196 6209 6221 6234 + 6246 6259 6272 6285 6297 6309 6322 6335 + 6348 6361 6374 6387 6400 6413 6426 6439 + 6451 6465 6478 6491 6505 6518 6531 6544 + 6557 6571 6584 6598 6611 6624 6638 6652 + 6666 6679 6693 6706 6720 6734 6747 6762 + 6776 6789 6803 6817 6831 6845 6859 6874 + 6887 6902 6916 6930 6945 6959 6973 6988 + 7002 7017 7031 7046 7061 7075 7090 7104 + 7119 7134 7149 7164 7178 7194 7208 7224 + 7238 7254 7269 7284 7299 7314 7330 7345 + 7361 7376 7391 7407 7422 7438 7454 7470 + 7485 7501 7517 7533 7548 7565 7581 7596 + 7612 7629 7645 7661 7677 7694 7710 7726 + 7743 7760 7776 7793 7809 7826 7843 7860 + 7877 7894 7911 7927 7944 7961 7979 7996 + 8013 8031 8048 8066 8083 8100 8118 8135 + 8153 8171 8189 8207 8225 8243 8261 8279 + 8297 8315 8333 8351 8370 8389 8407 8425 + 8444 8463 8482 8500 8519 8538 8557 8576 + 8595 8615 8633 8653 8673 8692 8711 8731 + 8750 8770 8790 8810 8829 8850 8869 8890 + 8910 8930 8950 8970 8991 9012 9032 9053 + 9073 9094 9116 9136 9157 9178 9200 9221 + 9242 9263 9285 9307 9328 9350 9372 9393 + 9415 9437 9459 9482 9504 9526 9549 9571 + 9594 9617 9640 9663 9686 9709 9732 9755 + 9778 9802 9826 9849 9873 9897 9920 9945 + 9969 9993 10017 10041 10066 10090 10115 10140 + 10165 10190 10215 10240 10265 10291 10316 10342 + 10368 10394 10419 10445 10471 10498 10525 10551 + 10578 10604 10631 10658 10685 10713 10740 10767 + 10795 10822 10850 10879 10907 10934 10963 10991 + 11020 11049 11077 11107 11136 11165 11194 11224 + 11254 11284 11314 11344 11374 11405 11435 11466 + 11496 11527 11558 11590 11621 11653 11685 11717 + 11749 11782 11813 11846 11879 11912 11945 11979 + 12012 12046 12079 12114 12148 12182 12217 12252 + 12287 12321 12357 12392 12428 12464 12500 12536 + 12573 12610 12647 12684 12722 12759 12797 12835 + 12873 12911 12950 12989 13028 13067 13107 13147 + 13187 13227 13268 13309 13350 13392 13433 13475 + 13517 13560 13602 13646 13689 13732 13776 13820 + 13864 13909 13954 14000 14045 14091 14138 14184 + 14231 14278 14325 14373 14421 14470 14519 14568 + 14618 14668 14718 14769 14820 14871 14923 14976 + 15028 15081 15134 15188 15243 15297 15353 15408 + 15464 15520 15577 15634 15692 15751 15809 15869 + 15929 15989 16050 16111 16173 16235 16298 16361 + 16426 16490 16556 16621 16688 16755 16823 16891 + 16960 17029 17099 17170 17242 17315 17387 17461 + 17535 17610 17687 17764 17841 17919 17999 18079 + 18159 18241 18323 18407 18492 18577 18663 18751 + 18839 18928 19019 19110 19203 19297 19392 19488 + 19585 19683 19783 19884 19986 20090 20195 20301 + 20408 20518 20628 20740 20855 20970 21086 21206 + 21326 21448 21572 21698 21826 21956 22088 22222 + 22358 22496 22638 22780 22926 23074 23225 23378 + 23534 23693 23854 24019 24187 24359 24533 24710 + 24893 25078 25266 25460 25658 25859 26065 26276 + 26492 26712 26939 27170 27408 27652 27901 28157 + 28421 28691 28969 29256 29286 29304 29322 29341 + 29360 29379 29399 29419 29440 29462 29483 29506 + 29529 29553 29577 29602 29628 29655 29682 29711 + 29741 29771 29803 29837 29871 29908 29946 29986 + 30028 30073 30120 30171 30225 30284 30348 30417 + 30495 30582 30681 30797 30937 31116 31365 31789 diff --git a/tc/Makefile b/tc/Makefile new file mode 100644 index 0000000..06546f9 --- /dev/null +++ b/tc/Makefile @@ -0,0 +1,74 @@ +TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o \ + m_police.o m_estimator.o m_action.o + +include ../Config + +TCMODULES := +TCMODULES += q_fifo.o +TCMODULES += q_sfq.o +TCMODULES += q_red.o +TCMODULES += q_prio.o +TCMODULES += q_tbf.o +TCMODULES += q_cbq.o +TCMODULES += f_rsvp.o +TCMODULES += f_u32.o +TCMODULES += f_route.o +TCMODULES += f_fw.o +TCMODULES += q_dsmark.o +TCMODULES += q_gred.o +TCMODULES += f_tcindex.o +TCMODULES += q_ingress.o +TCMODULES += q_hfsc.o +TCMODULES += q_htb.o +TCMODULES += m_gact.o +TCMODULES += m_mirred.o +TCMODULES += m_ipt.o +TCMODULES += m_pedit.o +TCMODULES += p_ip.o +TCMODULES += p_icmp.o +TCMODULES += p_tcp.o +TCMODULES += p_udp.o + +TCOBJ += $(TCMODULES) + +TCLIB := tc_core.o +TCLIB += tc_red.o +TCLIB += tc_cbq.o +TCLIB += tc_estimator.o + +CFLAGS += -DCONFIG_GACT -DCONFIG_GACT_PROB + +TCSO := +TCSO += q_netem.so +ifeq ($(TC_CONFIG_ATM),y) + TCSO += q_atm.so +endif + +LDLIBS += -L. -ltc -lm -ldl + +LDFLAGS += -Wl,-export-dynamic + +%.so: %.c + $(CC) $(CFLAGS) -shared -fpic $< -o $@ + + +all: libtc.a tc $(TCSO) + +tc: $(TCOBJ) $(LIBNETLINK) $(LIBUTIL) $(TCLIB) + +libtc.a: $(TCLIB) + $(AR) rcs $@ $(TCLIB) + +install: all + mkdir -p $(DESTDIR)/usr/lib/tc + install -m 0755 -s tc $(DESTDIR)$(SBINDIR) + for i in $(TCSO); \ + do install -m 755 -s $$i $(DESTDIR)/usr/lib/tc; \ + done + +clean: + rm -f $(TCOBJ) $(TCLIB) libtc.a tc *.so + +q_atm.so: q_atm.c + $(CC) $(CFLAGS) -shared -fpic -o q_atm.so q_atm.c -latm + diff --git a/tc/README.last b/tc/README.last new file mode 100644 index 0000000..9400438 --- /dev/null +++ b/tc/README.last @@ -0,0 +1,47 @@ +Kernel code and interface. +-------------------------- + +* Compile time switches + +There is only one, but very important, compile time switch. +It is not settable by "make config", but should be selected +manually and after a bit of thinking in <include/net/pkt_sched.h> + +PSCHED_CLOCK_SOURCE can take three values: + + PSCHED_GETTIMEOFDAY + PSCHED_JIFFIES + PSCHED_CPU + + + PSCHED_GETTIMEOFDAY + +Default setting is the most conservative PSCHED_GETTIMEOFDAY. +It is very slow both because of weird slowness of do_gettimeofday() +and because it forces code to use unnatural "timeval" format, +where microseconds and seconds fields are separate. +Besides that, it will misbehave, when delays exceed 2 seconds +(f.e. very slow links or classes bounded to small slice of bandwidth) +To resume: as only you will get it working, select correct clock +source and forget about PSCHED_GETTIMEOFDAY forever. + + + PSCHED_JIFFIES + +Clock is derived from jiffies. On architectures with HZ=100 +granularity of this clock is not enough to make reasonable +bindings to real time. However, taking into account Linux +architecture problems, which force us to use artificial +integrated clock in any case, this switch is not so bad +for schduling even on high speed networks, though policing +is not reliable. + + + PSCHED_CPU + +It is available only for alpha and pentiums with correct +CPU timestamp. It is the fastest way, use it when it is available, +but remember: not all pentiums have this facility, and +a lot of them have clock, broken by APM etc. etc. + + diff --git a/tc/f_fw.c b/tc/f_fw.c new file mode 100644 index 0000000..5a56098 --- /dev/null +++ b/tc/f_fw.c @@ -0,0 +1,140 @@ +/* + * f_fw.c FW filter. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <linux/if.h> /* IFNAMSIZ */ +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... fw [ classid CLASSID ] [ police POLICE_SPEC ]\n"); + fprintf(stderr, " POLICE_SPEC := ... look at TBF\n"); + fprintf(stderr, " CLASSID := X:Y\n"); +} + +#define usage() return(-1) + +static int fw_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_police tp; + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + + memset(&tp, 0, sizeof(tp)); + + if (handle) { + if (get_u32(&t->tcm_handle, handle, 0)) { + fprintf(stderr, "Illegal \"handle\"\n"); + return -1; + } + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "classid") == 0 || + matches(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_FW_CLASSID, &handle, 4); + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_FW_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "action") == 0) { + NEXT_ARG(); + if (parse_action(&argc, &argv, TCA_FW_ACT, n)) { + fprintf(stderr, "Illegal fw \"action\"\n"); + return -1; + } + continue; + } else if (strcmp(*argv, "indev") == 0) { + char d[IFNAMSIZ+1]; + memset(d, 0, sizeof (d)); + argc--; + argv++; + if (argc < 1) { + fprintf(stderr, "Illegal indev\n"); + return -1; + } + strncpy(d, *argv, sizeof (d) - 1); + addattr_l(n, MAX_MSG, TCA_FW_INDEV, d, strlen(d) + 1); + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int fw_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_FW_MAX+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_FW_MAX, opt); + + if (handle) + fprintf(f, "handle 0x%x ", handle); + + if (tb[TCA_FW_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "classid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_FW_CLASSID]), b1)); + } + + if (tb[TCA_FW_POLICE]) + tc_print_police(f, tb[TCA_FW_POLICE]); + if (tb[TCA_FW_INDEV]) { + struct rtattr *idev = tb[TCA_FW_INDEV]; + fprintf(f, "input dev %s ",(char *)RTA_DATA(idev)); + } + + if (tb[TCA_FW_ACT]) { + fprintf(f, "\n"); + tc_print_action(f, tb[TCA_FW_ACT]); + } + return 0; +} + +struct filter_util fw_filter_util = { + .id = "fw", + .parse_fopt = fw_parse_opt, + .print_fopt = fw_print_opt, +}; diff --git a/tc/f_fw.o b/tc/f_fw.o new file mode 100644 index 0000000..a3d3798 Binary files /dev/null and b/tc/f_fw.o differ diff --git a/tc/f_route.c b/tc/f_route.c new file mode 100644 index 0000000..a41b9d5 --- /dev/null +++ b/tc/f_route.c @@ -0,0 +1,169 @@ +/* + * f_route.c ROUTE filter. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "rt_names.h" +#include "tc_common.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... route [ from REALM | fromif TAG ] [ to REALM ]\n"); + fprintf(stderr, " [ flowid CLASSID ] [ police POLICE_SPEC ]\n"); + fprintf(stderr, " POLICE_SPEC := ... look at TBF\n"); + fprintf(stderr, " CLASSID := X:Y\n"); +} + +#define usage() return(-1) + +static int route_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_police tp; + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + __u32 fh = 0xFFFF8000; + __u32 order = 0; + + memset(&tp, 0, sizeof(tp)); + + if (handle) { + if (get_u32(&t->tcm_handle, handle, 0)) { + fprintf(stderr, "Illegal \"handle\"\n"); + return -1; + } + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "to") == 0) { + __u32 id; + NEXT_ARG(); + if (rtnl_rtrealm_a2n(&id, *argv)) { + fprintf(stderr, "Illegal \"to\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_TO, &id, 4); + fh &= ~0x80FF; + fh |= id&0xFF; + } else if (matches(*argv, "from") == 0) { + __u32 id; + NEXT_ARG(); + if (rtnl_rtrealm_a2n(&id, *argv)) { + fprintf(stderr, "Illegal \"from\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_FROM, &id, 4); + fh &= 0xFFFF; + fh |= id<<16; + } else if (matches(*argv, "fromif") == 0) { + __u32 id; + NEXT_ARG(); + ll_init_map(&rth); + if ((id=ll_name_to_index(*argv)) <= 0) { + fprintf(stderr, "Illegal \"fromif\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_IIF, &id, 4); + fh &= 0xFFFF; + fh |= (0x8000|id)<<16; + } else if (matches(*argv, "classid") == 0 || + strcmp(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_CLASSID, &handle, 4); + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_ROUTE4_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "order") == 0) { + NEXT_ARG(); + if (get_u32(&order, *argv, 0)) { + fprintf(stderr, "Illegal \"order\"\n"); + return -1; + } + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + if (order) { + fh &= ~0x7F00; + fh |= (order<<8)&0x7F00; + } + if (!t->tcm_handle) + t->tcm_handle = fh; + return 0; +} + +static int route_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_ROUTE4_MAX+1]; + SPRINT_BUF(b1); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ROUTE4_MAX, opt); + + if (handle) + fprintf(f, "fh 0x%08x ", handle); + if (handle&0x7F00) + fprintf(f, "order %d ", (handle>>8)&0x7F); + + if (tb[TCA_ROUTE4_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "flowid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID]), b1)); + } + if (tb[TCA_ROUTE4_TO]) + fprintf(f, "to %s ", rtnl_rtrealm_n2a(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_TO]), b1, sizeof(b1))); + if (tb[TCA_ROUTE4_FROM]) + fprintf(f, "from %s ", rtnl_rtrealm_n2a(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_FROM]), b1, sizeof(b1))); + if (tb[TCA_ROUTE4_IIF]) + fprintf(f, "fromif %s", ll_index_to_name(*(int*)RTA_DATA(tb[TCA_ROUTE4_IIF]))); + if (tb[TCA_ROUTE4_POLICE]) + tc_print_police(f, tb[TCA_ROUTE4_POLICE]); + return 0; +} + +struct filter_util route_filter_util = { + .id = "route", + .parse_fopt = route_parse_opt, + .print_fopt = route_print_opt, +}; diff --git a/tc/f_route.o b/tc/f_route.o new file mode 100644 index 0000000..d2dc8ab Binary files /dev/null and b/tc/f_route.o differ diff --git a/tc/f_rsvp.c b/tc/f_rsvp.c new file mode 100644 index 0000000..13fcf97 --- /dev/null +++ b/tc/f_rsvp.c @@ -0,0 +1,404 @@ +/* + * q_rsvp.c RSVP filter. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "rt_names.h" +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ]\n"); + fprintf(stderr, " [ sender SRC[/PORT | GPI ]\n"); + fprintf(stderr, " [ classid CLASSID ] [ police POLICE_SPEC ]\n"); + fprintf(stderr, " [ tunnelid ID ] [ tunnel ID skip NUMBER ]\n"); + fprintf(stderr, "Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI |\n"); + fprintf(stderr, " u{8|16|32} NUMBER mask MASK at OFFSET}\n"); + fprintf(stderr, " POLICE_SPEC := ... look at TBF\n"); + fprintf(stderr, " FILTERID := X:Y\n"); +} + +#define usage() return(-1) + +int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix * addr, + struct tc_rsvp_pinfo *pinfo, int dir, int family) +{ + int argc = *argc_p; + char **argv = *argv_p; + char *p = strchr(*argv, '/'); + struct tc_rsvp_gpi *pi = dir ? &pinfo->dpi : &pinfo->spi; + + if (p) { + __u16 tmp; + + if (get_u16(&tmp, p+1, 0)) + return -1; + + if (dir == 0) { + /* Source port: u16 at offset 0 */ + pi->key = htonl(((__u32)tmp)<<16); + pi->mask = htonl(0xFFFF0000); + } else { + /* Destination port: u16 at offset 2 */ + pi->key = htonl(((__u32)tmp)); + pi->mask = htonl(0x0000FFFF); + } + pi->offset = 0; + *p = 0; + } + if (get_addr_1(addr, *argv, family)) + return -1; + if (p) + *p = '/'; + + argc--; argv++; + + if (pi->mask || argc <= 0) + goto done; + + if (strcmp(*argv, "spi/ah") == 0 || + strcmp(*argv, "gpi/ah") == 0) { + __u32 gpi; + NEXT_ARG(); + if (get_u32(&gpi, *argv, 0)) + return -1; + pi->mask = htonl(0xFFFFFFFF); + pi->key = htonl(gpi); + pi->offset = 4; + if (pinfo->protocol == 0) + pinfo->protocol = IPPROTO_AH; + argc--; argv++; + } else if (strcmp(*argv, "spi/esp") == 0 || + strcmp(*argv, "gpi/esp") == 0) { + __u32 gpi; + NEXT_ARG(); + if (get_u32(&gpi, *argv, 0)) + return -1; + pi->mask = htonl(0xFFFFFFFF); + pi->key = htonl(gpi); + pi->offset = 0; + if (pinfo->protocol == 0) + pinfo->protocol = IPPROTO_ESP; + argc--; argv++; + } else if (strcmp(*argv, "flowlabel") == 0) { + __u32 flabel; + NEXT_ARG(); + if (get_u32(&flabel, *argv, 0)) + return -1; + if (family != AF_INET6) + return -1; + pi->mask = htonl(0x000FFFFF); + pi->key = htonl(flabel) & pi->mask; + pi->offset = -40; + argc--; argv++; + } else if (strcmp(*argv, "u32") == 0 || + strcmp(*argv, "u16") == 0 || + strcmp(*argv, "u8") == 0) { + int sz = 1; + __u32 tmp; + __u32 mask = 0xff; + if (strcmp(*argv, "u32") == 0) { + sz = 4; + mask = 0xffff; + } else if (strcmp(*argv, "u16") == 0) { + mask = 0xffffffff; + sz = 2; + } + NEXT_ARG(); + if (get_u32(&tmp, *argv, 0)) + return -1; + argc--; argv++; + if (strcmp(*argv, "mask") == 0) { + NEXT_ARG(); + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + } + if (strcmp(*argv, "at") == 0) { + NEXT_ARG(); + if (get_integer(&pi->offset, *argv, 0)) + return -1; + argc--; argv++; + } + if (sz == 1) { + if ((pi->offset & 3) == 0) { + mask <<= 24; + tmp <<= 24; + } else if ((pi->offset & 3) == 1) { + mask <<= 16; + tmp <<= 16; + } else if ((pi->offset & 3) == 3) { + mask <<= 8; + tmp <<= 8; + } + } else if (sz == 2) { + if ((pi->offset & 3) == 0) { + mask <<= 16; + tmp <<= 16; + } + } + pi->offset &= ~3; + pi->mask = htonl(mask); + pi->key = htonl(tmp) & pi->mask; + } + +done: + *argc_p = argc; + *argv_p = argv; + return 0; +} + + +static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; + struct tc_rsvp_pinfo pinfo; + struct tc_police tp; + struct tcmsg *t = NLMSG_DATA(n); + int pinfo_ok = 0; + struct rtattr *tail; + + memset(&pinfo, 0, sizeof(pinfo)); + memset(&tp, 0, sizeof(tp)); + + if (handle) { + if (get_u32(&t->tcm_handle, handle, 0)) { + fprintf(stderr, "Illegal \"handle\"\n"); + return -1; + } + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "session") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 1, family)) { + fprintf(stderr, "Illegal \"session\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_DST, &addr.data, addr.bytelen); + if (pinfo.dpi.mask || pinfo.protocol) + pinfo_ok++; + continue; + } else if (matches(*argv, "sender") == 0 || + matches(*argv, "flowspec") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 0, family)) { + fprintf(stderr, "Illegal \"sender\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_SRC, &addr.data, addr.bytelen); + if (pinfo.spi.mask || pinfo.protocol) + pinfo_ok++; + continue; + } else if (matches("ipproto", *argv) == 0) { + int num; + NEXT_ARG(); + num = inet_proto_a2n(*argv); + if (num < 0) { + fprintf(stderr, "Illegal \"ipproto\"\n"); + return -1; + } + pinfo.protocol = num; + pinfo_ok++; + } else if (matches(*argv, "classid") == 0 || + strcmp(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4); + } else if (strcmp(*argv, "tunnelid") == 0) { + unsigned tid; + NEXT_ARG(); + if (get_unsigned(&tid, *argv, 0)) { + fprintf(stderr, "Illegal \"tunnelid\"\n"); + return -1; + } + pinfo.tunnelid = tid; + pinfo_ok++; + } else if (strcmp(*argv, "tunnel") == 0) { + unsigned tid; + NEXT_ARG(); + if (get_unsigned(&tid, *argv, 0)) { + fprintf(stderr, "Illegal \"tunnel\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_CLASSID, &tid, 4); + NEXT_ARG(); + if (strcmp(*argv, "skip") == 0) { + NEXT_ARG(); + } + if (get_unsigned(&tid, *argv, 0)) { + fprintf(stderr, "Illegal \"skip\"\n"); + return -1; + } + pinfo.tunnelhdr = tid; + pinfo_ok++; + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_RSVP_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (pinfo_ok) + addattr_l(n, 4096, TCA_RSVP_PINFO, &pinfo, sizeof(pinfo)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static char * sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) +{ + if (pi->offset == 0) { + if (dir && pi->mask == htonl(0xFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)); + return buf; + } + if (!dir && pi->mask == htonl(0xFFFF0000)) { + snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)>>16); + return buf; + } + if (pi->mask == htonl(0xFFFFFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, " spi/esp 0x%08x", htonl(pi->key)); + return buf; + } + } else if (pi->offset == 4 && pi->mask == htonl(0xFFFFFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, " spi/ah 0x%08x", htonl(pi->key)); + return buf; + } else if (pi->offset == -40 && pi->mask == htonl(0x000FFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, " flowlabel 0x%05x", htonl(pi->key)); + return buf; + } + snprintf(buf, SPRINT_BSIZE-1, " u32 0x%08x mask %08x at %d", + htonl(pi->key), htonl(pi->mask), pi->offset); + return buf; +} + +static int rsvp_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; + struct rtattr *tb[TCA_RSVP_MAX+1]; + struct tc_rsvp_pinfo *pinfo = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_RSVP_MAX, opt); + + if (handle) + fprintf(f, "fh 0x%08x ", handle); + + if (tb[TCA_RSVP_PINFO]) { + if (RTA_PAYLOAD(tb[TCA_RSVP_PINFO]) < sizeof(*pinfo)) + return -1; + + pinfo = RTA_DATA(tb[TCA_RSVP_PINFO]); + } + + if (tb[TCA_RSVP_CLASSID]) { + SPRINT_BUF(b1); + if (!pinfo || pinfo->tunnelhdr == 0) + fprintf(f, "flowid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_RSVP_CLASSID]), b1)); + else + fprintf(f, "tunnel %d skip %d ", *(__u32*)RTA_DATA(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr); + } else if (pinfo && pinfo->tunnelhdr) + fprintf(f, "tunnel [BAD] skip %d ", pinfo->tunnelhdr); + + if (tb[TCA_RSVP_DST]) { + char buf[128]; + fprintf(f, "session "); + if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_DST]), buf, sizeof(buf)) == 0) + fprintf(f, " [INVALID DADDR] "); + else + fprintf(f, "%s", buf); + if (pinfo && pinfo->dpi.mask) { + SPRINT_BUF(b2); + fprintf(f, "%s ", sprint_spi(&pinfo->dpi, 1, b2)); + } else + fprintf(f, " "); + } else { + if (pinfo && pinfo->dpi.mask) { + SPRINT_BUF(b2); + fprintf(f, "session [NONE]%s ", sprint_spi(&pinfo->dpi, 1, b2)); + } else + fprintf(f, "session NONE "); + } + + if (pinfo && pinfo->protocol) { + SPRINT_BUF(b1); + fprintf(f, "ipproto %s ", inet_proto_n2a(pinfo->protocol, b1, sizeof(b1))); + } + if (pinfo && pinfo->tunnelid) + fprintf(f, "tunnelid %d ", pinfo->tunnelid); + if (tb[TCA_RSVP_SRC]) { + char buf[128]; + fprintf(f, "sender "); + if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_SRC]), buf, sizeof(buf)) == 0) { + fprintf(f, "[BAD]"); + } else { + fprintf(f, " %s", buf); + } + if (pinfo && pinfo->spi.mask) { + SPRINT_BUF(b2); + fprintf(f, "%s ", sprint_spi(&pinfo->spi, 0, b2)); + } else + fprintf(f, " "); + } else if (pinfo && pinfo->spi.mask) { + SPRINT_BUF(b2); + fprintf(f, "sender [NONE]%s ", sprint_spi(&pinfo->spi, 0, b2)); + } + if (tb[TCA_RSVP_POLICE]) + tc_print_police(f, tb[TCA_RSVP_POLICE]); + return 0; +} + +struct filter_util rsvp_filter_util = { + .id = "rsvp", + .parse_fopt = rsvp_parse_opt, + .print_fopt = rsvp_print_opt, +}; + +struct filter_util rsvp6_filter_util = { + .id = "rsvp6", + .parse_fopt = rsvp_parse_opt, + .print_fopt = rsvp_print_opt, +}; diff --git a/tc/f_rsvp.o b/tc/f_rsvp.o new file mode 100644 index 0000000..7223bad Binary files /dev/null and b/tc/f_rsvp.o differ diff --git a/tc/f_tcindex.c b/tc/f_tcindex.c new file mode 100644 index 0000000..39ac75a --- /dev/null +++ b/tc/f_tcindex.c @@ -0,0 +1,185 @@ +/* + * f_tcindex.c Traffic control index filter + * + * Written 1998,1999 by Werner Almesberger + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <netinet/in.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr," Usage: ... tcindex [ hash SIZE ] [ mask MASK ]" + " [ shift SHIFT ]\n"); + fprintf(stderr," [ pass_on | fall_through ]\n"); + fprintf(stderr," [ classid CLASSID ] " + "[ police POLICE_SPEC ]\n"); +} + + +#define usage() return(-1) + + +static int tcindex_parse_opt(struct filter_util *qu, char *handle, int argc, + char **argv, struct nlmsghdr *n) +{ + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + char *end; + + if (handle) { + t->tcm_handle = strtoul(handle,&end,0); + if (*end) { + fprintf(stderr, "Illegal filter ID\n"); + return -1; + } + } + if (!argc) return 0; + tail = NLMSG_TAIL(n); + addattr_l(n,4096,TCA_OPTIONS,NULL,0); + while (argc) { + if (!strcmp(*argv,"hash")) { + int hash; + + NEXT_ARG(); + hash = strtoul(*argv,&end,0); + if (*end || !hash || hash > 0x10000) { + explain(); + return -1; + } + addattr_l(n,4096,TCA_TCINDEX_HASH,&hash,sizeof(hash)); + } + else if (!strcmp(*argv,"mask")) { + __u16 mask; + + NEXT_ARG(); + mask = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + addattr_l(n,4096,TCA_TCINDEX_MASK,&mask,sizeof(mask)); + } + else if (!strcmp(*argv,"shift")) { + int shift; + + NEXT_ARG(); + shift = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + addattr_l(n,4096,TCA_TCINDEX_SHIFT,&shift, + sizeof(shift)); + } + else if (!strcmp(*argv,"fall_through")) { + int value = 1; + + addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value, + sizeof(value)); + } + else if (!strcmp(*argv,"pass_on")) { + int value = 0; + + addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value, + sizeof(value)); + } + else if (!strcmp(*argv,"classid")) { + __u32 handle; + + NEXT_ARG(); + if (get_tc_classid(&handle,*argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_TCINDEX_CLASSID, &handle, 4); + } + else if (!strcmp(*argv,"police")) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_TCINDEX_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } + else { + explain(); + return -1; + } + argc--; + argv++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + +static int tcindex_print_opt(struct filter_util *qu, FILE *f, + struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_TCINDEX_MAX+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_TCINDEX_MAX, opt); + + if (handle != ~0) fprintf(f,"handle 0x%04x ",handle); + if (tb[TCA_TCINDEX_HASH]) { + __u16 hash; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_HASH]) < sizeof(hash)) + return -1; + hash = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_HASH]); + fprintf(f,"hash %d ",hash); + } + if (tb[TCA_TCINDEX_MASK]) { + __u16 mask; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_MASK]) < sizeof(mask)) + return -1; + mask = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_MASK]); + fprintf(f,"mask 0x%04x ",mask); + } + if (tb[TCA_TCINDEX_SHIFT]) { + int shift; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_SHIFT]) < sizeof(shift)) + return -1; + shift = *(int *) RTA_DATA(tb[TCA_TCINDEX_SHIFT]); + fprintf(f,"shift %d ",shift); + } + if (tb[TCA_TCINDEX_FALL_THROUGH]) { + int fall_through; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_FALL_THROUGH]) < + sizeof(fall_through)) + return -1; + fall_through = *(int *) RTA_DATA(tb[TCA_TCINDEX_FALL_THROUGH]); + fprintf(f,fall_through ? "fall_through " : "pass_on "); + } + if (tb[TCA_TCINDEX_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "classid %s ",sprint_tc_classid(*(__u32 *) + RTA_DATA(tb[TCA_TCINDEX_CLASSID]), b1)); + } + if (tb[TCA_TCINDEX_POLICE]) { + fprintf(f, "\n"); + tc_print_police(f, tb[TCA_TCINDEX_POLICE]); + } + return 0; +} + +struct filter_util tcindex_filter_util = { + .id = "tcindex", + .parse_fopt = tcindex_parse_opt, + .print_fopt = tcindex_print_opt, +}; diff --git a/tc/f_tcindex.o b/tc/f_tcindex.o new file mode 100644 index 0000000..c76dd38 Binary files /dev/null and b/tc/f_tcindex.o differ diff --git a/tc/f_u32.c b/tc/f_u32.c new file mode 100644 index 0000000..50dc4df --- /dev/null +++ b/tc/f_u32.c @@ -0,0 +1,1071 @@ +/* + * q_u32.c U32 filter. + * + * This program is free software; you can u32istribute 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * Match mark added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro> [5 nov 2004] + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <linux/if.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... u32 [ match SELECTOR ... ] [ link HTID ] [ classid CLASSID ]\n"); + fprintf(stderr, " [ police POLICE_SPEC ] [ offset OFFSET_SPEC ]\n"); + fprintf(stderr, " [ ht HTID ] [ hashkey HASHKEY_SPEC ]\n"); + fprintf(stderr, " [ sample SAMPLE ]\n"); + fprintf(stderr, "or u32 divisor DIVISOR\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n"); + fprintf(stderr, " SAMPLE := { ip | ip6 | udp | tcp | icmp | u{32|16|8} | mark } SAMPLE_ARGS\n"); + fprintf(stderr, " FILTERID := X:Y:Z\n"); +} + +#define usage() return(-1) + +int get_u32_handle(__u32 *handle, char *str) +{ + __u32 htid=0, hash=0, nodeid=0; + char *tmp = strchr(str, ':'); + + if (tmp == NULL) { + if (memcmp("0x", str, 2) == 0) + return get_u32(handle, str, 16); + return -1; + } + htid = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (htid>=0x1000) + return -1; + if (*tmp) { + str = tmp+1; + hash = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (hash>=0x100) + return -1; + if (*tmp) { + str = tmp+1; + nodeid = strtoul(str, &tmp, 16); + if (tmp == str && *str != 0) + return -1; + if (nodeid>=0x1000) + return -1; + } + } + *handle = (htid<<20)|(hash<<12)|nodeid; + return 0; +} + +char * sprint_u32_handle(__u32 handle, char *buf) +{ + int bsize = SPRINT_BSIZE-1; + __u32 htid = TC_U32_HTID(handle); + __u32 hash = TC_U32_HASH(handle); + __u32 nodeid = TC_U32_NODE(handle); + char *b = buf; + + if (handle == 0) { + snprintf(b, bsize, "none"); + return b; + } + if (htid) { + int l = snprintf(b, bsize, "%x:", htid>>20); + bsize -= l; + b += l; + } + if (nodeid|hash) { + if (hash) { + int l = snprintf(b, bsize, "%x", hash); + bsize -= l; + b += l; + } + if (nodeid) { + int l = snprintf(b, bsize, ":%x", nodeid); + bsize -= l; + b += l; + } + } + if (show_raw) + snprintf(b, bsize, "[%08x] ", handle); + return buf; +} + +static int pack_key(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + int i; + int hwm = sel->nkeys; + + key &= mask; + + for (i=0; i<hwm; i++) { + if (sel->keys[i].off == off && sel->keys[i].offmask == offmask) { + __u32 intersect = mask&sel->keys[i].mask; + + if ((key^sel->keys[i].val) & intersect) + return -1; + sel->keys[i].val |= key; + sel->keys[i].mask |= mask; + return 0; + } + } + + if (hwm >= 128) + return -1; + if (off % 4) + return -1; + sel->keys[hwm].val = key; + sel->keys[hwm].mask = mask; + sel->keys[hwm].off = off; + sel->keys[hwm].offmask = offmask; + sel->nkeys++; + return 0; +} + +static int pack_key32(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + key = htonl(key); + mask = htonl(mask); + return pack_key(sel, key, mask, off, offmask); +} + +static int pack_key16(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + if (key > 0xFFFF || mask > 0xFFFF) + return -1; + + if ((off & 3) == 0) { + key <<= 16; + mask <<= 16; + } + off &= ~3; + key = htonl(key); + mask = htonl(mask); + + return pack_key(sel, key, mask, off, offmask); +} + +static int pack_key8(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + if (key > 0xFF || mask > 0xFF) + return -1; + + if ((off & 3) == 0) { + key <<= 24; + mask <<= 24; + } else if ((off & 3) == 1) { + key <<= 16; + mask <<= 16; + } else if ((off & 3) == 2) { + key <<= 8; + mask <<= 8; + } + off &= ~3; + key = htonl(key); + mask = htonl(mask); + + return pack_key(sel, key, mask, off, offmask); +} + + +int parse_at(int *argc_p, char ***argv_p, int *off, int *offmask) +{ + int argc = *argc_p; + char **argv = *argv_p; + char *p = *argv; + + if (argc <= 0) + return -1; + + if (strlen(p) > strlen("nexthdr+") && + memcmp(p, "nexthdr+", strlen("nexthdr+")) == 0) { + *offmask = -1; + p += strlen("nexthdr+"); + } else if (matches(*argv, "nexthdr+") == 0) { + NEXT_ARG(); + *offmask = -1; + p = *argv; + } + + if (get_integer(off, p, 0)) + return -1; + argc--; argv++; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + + +static int parse_u32(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off, int offmask) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + __u32 key; + __u32 mask; + + if (argc < 2) + return -1; + + if (get_u32(&key, *argv, 0)) + return -1; + argc--; argv++; + + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + res = pack_key32(sel, key, mask, off, offmask); + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_u16(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off, int offmask) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + __u32 key; + __u32 mask; + + if (argc < 2) + return -1; + + if (get_u32(&key, *argv, 0)) + return -1; + argc--; argv++; + + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + res = pack_key16(sel, key, mask, off, offmask); + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_u8(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off, int offmask) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + __u32 key; + __u32 mask; + + if (argc < 2) + return -1; + + if (get_u32(&key, *argv, 0)) + return -1; + argc--; argv++; + + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + + if (key > 0xFF || mask > 0xFF) + return -1; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + res = pack_key8(sel, key, mask, off, offmask); + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip_addr(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + inet_prefix addr; + __u32 mask; + int offmask = 0; + + if (argc < 1) + return -1; + + if (get_prefix_1(&addr, *argv, AF_INET)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + mask = 0; + if (addr.bitlen) + mask = htonl(0xFFFFFFFF<<(32-addr.bitlen)); + if (pack_key(sel, addr.data[0], mask, off, offmask) < 0) + return -1; + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip6_addr(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + int plen = 128; + int i; + inet_prefix addr; + int offmask = 0; + + if (argc < 1) + return -1; + + if (get_prefix_1(&addr, *argv, AF_INET6)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + plen = addr.bitlen; + for (i=0; i<plen; i+=32) { +// if (((i+31)&~0x1F)<=plen) { + if (((i+31))<=plen) { + if ((res = pack_key(sel, addr.data[i/32], 0xFFFFFFFF, off+4*(i/32), offmask)) < 0) + return -1; + } else if (i<plen) { + __u32 mask = htonl(0xFFFFFFFF<<(32-(plen-i))); + if ((res = pack_key(sel, addr.data[i/32], mask, off+4*(i/32), offmask)) < 0) + return -1; + } + } + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + res = parse_ip_addr(&argc, &argv, sel, 12); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + res = parse_ip_addr(&argc, &argv, sel, 16); + goto done; + } + if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 1, 0); + goto done; + } + if (strcmp(*argv, "ihl") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, 0); + goto done; + } + if (strcmp(*argv, "protocol") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 9, 0); + goto done; + } + if (matches(*argv, "precedence") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 1, 0); + goto done; + } + if (strcmp(*argv, "nofrag") == 0) { + argc--; argv++; + res = pack_key16(sel, 0, 0x3FFF, 6, 0); + goto done; + } + if (strcmp(*argv, "firstfrag") == 0) { + argc--; argv++; + res = pack_key16(sel, 0, 0x1FFF, 6, 0); + goto done; + } + if (strcmp(*argv, "df") == 0) { + argc--; argv++; + res = pack_key16(sel, 0x4000, 0x4000, 6, 0); + goto done; + } + if (strcmp(*argv, "mf") == 0) { + argc--; argv++; + res = pack_key16(sel, 0x2000, 0x2000, 6, 0); + goto done; + } + if (strcmp(*argv, "dport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 22, 0); + goto done; + } + if (strcmp(*argv, "sport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 20, 0); + goto done; + } + if (strcmp(*argv, "icmp_type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 20, 0); + goto done; + } + if (strcmp(*argv, "icmp_code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 20, 1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip6(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + res = parse_ip6_addr(&argc, &argv, sel, 8); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + res = parse_ip6_addr(&argc, &argv, sel, 24); + goto done; + } + if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, 0); + goto done; + } + if (strcmp(*argv, "protocol") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 6, 0); + goto done; + } + if (strcmp(*argv, "flowlabel") == 0) { + NEXT_ARG(); + res = parse_u32(&argc, &argv, sel, 0, 0); + goto done; + } + if (strcmp(*argv, "dport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 42, 0); + goto done; + } + if (strcmp(*argv, "sport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 40, 0); + goto done; + } + if (strcmp(*argv, "icmp_type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 40, 0); + goto done; + } + if (strcmp(*argv, "icmp_code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 41, 1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +#define parse_tcp parse_udp +static int parse_udp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 0, -1); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 2, -1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_icmp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, -1); + goto done; + } + if (strcmp(*argv, "code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 1, -1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_mark(int *argc_p, char ***argv_p, struct nlmsghdr *n) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + struct tc_u32_mark mark; + + if (argc <= 1) + return -1; + + if (get_u32(&mark.val, *argv, 0)) { + fprintf(stderr, "Illegal \"mark\" value\n"); + return -1; + } + NEXT_ARG(); + + if (get_u32(&mark.mask, *argv, 0)) { + fprintf(stderr, "Illegal \"mark\" mask\n"); + return -1; + } + NEXT_ARG(); + + if ((mark.val & mark.mask) != mark.val) { + fprintf(stderr, "Illegal \"mark\" (impossible combination)\n"); + return -1; + } + + addattr_l(n, MAX_MSG, TCA_U32_MARK, &mark, sizeof(mark)); + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_selector(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + + if (argc <= 0) + return -1; + + if (matches(*argv, "u32") == 0) { + NEXT_ARG(); + res = parse_u32(&argc, &argv, sel, 0, 0); + goto done; + } + if (matches(*argv, "u16") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 0, 0); + goto done; + } + if (matches(*argv, "u8") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, 0); + goto done; + } + if (matches(*argv, "ip") == 0) { + NEXT_ARG(); + res = parse_ip(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "ip6") == 0) { + NEXT_ARG(); + res = parse_ip6(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "udp") == 0) { + NEXT_ARG(); + res = parse_udp(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "tcp") == 0) { + NEXT_ARG(); + res = parse_tcp(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "icmp") == 0) { + NEXT_ARG(); + res = parse_icmp(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "mark") == 0) { + NEXT_ARG(); + res = parse_mark(&argc, &argv, n); + goto done; + } + + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_offset(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int argc = *argc_p; + char **argv = *argv_p; + + while (argc > 0) { + if (matches(*argv, "plus") == 0) { + int off; + NEXT_ARG(); + if (get_integer(&off, *argv, 0)) + return -1; + sel->off = off; + sel->flags |= TC_U32_OFFSET; + } else if (matches(*argv, "at") == 0) { + int off; + NEXT_ARG(); + if (get_integer(&off, *argv, 0)) + return -1; + sel->offoff = off; + if (off%2) { + fprintf(stderr, "offset \"at\" must be even\n"); + return -1; + } + sel->flags |= TC_U32_VAROFFSET; + } else if (matches(*argv, "mask") == 0) { + __u16 mask; + NEXT_ARG(); + if (get_u16(&mask, *argv, 16)) + return -1; + sel->offmask = htons(mask); + sel->flags |= TC_U32_VAROFFSET; + } else if (matches(*argv, "shift") == 0) { + int shift; + NEXT_ARG(); + if (get_integer(&shift, *argv, 0)) + return -1; + sel->offshift = shift; + sel->flags |= TC_U32_VAROFFSET; + } else if (matches(*argv, "eat") == 0) { + sel->flags |= TC_U32_EAT; + } else { + break; + } + argc--; argv++; + } + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +static int parse_hashkey(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int argc = *argc_p; + char **argv = *argv_p; + + while (argc > 0) { + if (matches(*argv, "mask") == 0) { + __u32 mask; + NEXT_ARG(); + if (get_u32(&mask, *argv, 16)) + return -1; + sel->hmask = htonl(mask); + } else if (matches(*argv, "at") == 0) { + int num; + NEXT_ARG(); + if (get_integer(&num, *argv, 0)) + return -1; + if (num%4) + return -1; + sel->hoff = num; + } else { + break; + } + argc--; argv++; + } + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +static int u32_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + struct { + struct tc_u32_sel sel; + struct tc_u32_key keys[128]; + } sel; + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + int sel_ok = 0; + int sample_ok = 0; + __u32 htid = 0; + __u32 order = 0; + + memset(&sel, 0, sizeof(sel)); + + if (handle && get_u32_handle(&t->tcm_handle, handle)) { + fprintf(stderr, "Illegal filter ID\n"); + return -1; + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "match") == 0) { + NEXT_ARG(); + if (parse_selector(&argc, &argv, &sel.sel, n)) { + fprintf(stderr, "Illegal \"match\"\n"); + return -1; + } + sel_ok++; + continue; + } else if (matches(*argv, "offset") == 0) { + NEXT_ARG(); + if (parse_offset(&argc, &argv, &sel.sel)) { + fprintf(stderr, "Illegal \"offset\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "hashkey") == 0) { + NEXT_ARG(); + if (parse_hashkey(&argc, &argv, &sel.sel)) { + fprintf(stderr, "Illegal \"hashkey\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "classid") == 0 || + strcmp(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_U32_CLASSID, &handle, 4); + sel.sel.flags |= TC_U32_TERMINAL; + } else if (matches(*argv, "divisor") == 0) { + unsigned divisor; + NEXT_ARG(); + if (get_unsigned(&divisor, *argv, 0) || divisor == 0 || + divisor > 0x100) { + fprintf(stderr, "Illegal \"divisor\"\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_U32_DIVISOR, &divisor, 4); + } else if (matches(*argv, "order") == 0) { + NEXT_ARG(); + if (get_u32(&order, *argv, 0)) { + fprintf(stderr, "Illegal \"order\"\n"); + return -1; + } + } else if (strcmp(*argv, "link") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_u32_handle(&handle, *argv)) { + fprintf(stderr, "Illegal \"link\"\n"); + return -1; + } + if (handle && TC_U32_NODE(handle)) { + fprintf(stderr, "\"link\" must be a hash table.\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_U32_LINK, &handle, 4); + } else if (strcmp(*argv, "ht") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_u32_handle(&handle, *argv)) { + fprintf(stderr, "Illegal \"ht\"\n"); + return -1; + } + if (handle && TC_U32_NODE(handle)) { + fprintf(stderr, "\"ht\" must be a hash table.\n"); + return -1; + } + if (sample_ok) + htid = (htid&0xFF000)|(handle&0xFFF00000); + else + htid = (handle&0xFFFFF000); + } else if (strcmp(*argv, "sample") == 0) { + __u32 hash; + struct { + struct tc_u32_sel sel; + struct tc_u32_key keys[4]; + } sel2; + NEXT_ARG(); + if (parse_selector(&argc, &argv, &sel2.sel, n)) { + fprintf(stderr, "Illegal \"sample\"\n"); + return -1; + } + if (sel2.sel.nkeys != 1) { + fprintf(stderr, "\"sample\" must contain exactly ONE key.\n"); + return -1; + } + hash = sel2.sel.keys[0].val&sel2.sel.keys[0].mask; + hash ^= hash>>16; + hash ^= hash>>8; + htid = ((hash<<12)&0xFF000)|(htid&0xFFF00000); + sample_ok = 1; + continue; + } else if (strcmp(*argv, "indev") == 0) { + char ind[IFNAMSIZ + 1]; + memset(ind, 0, sizeof (ind)); + argc--; + argv++; + if (argc < 1) { + fprintf(stderr, "Illegal indev\n"); + return -1; + } + strncpy(ind, *argv, sizeof (ind) - 1); + addattr_l(n, MAX_MSG, TCA_U32_INDEV, ind, strlen(ind) + 1); + + } else if (matches(*argv, "action") == 0) { + NEXT_ARG(); + if (parse_action(&argc, &argv, TCA_U32_ACT, n)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + continue; + + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_U32_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (order) { + if (TC_U32_NODE(t->tcm_handle) && order != TC_U32_NODE(t->tcm_handle)) { + fprintf(stderr, "\"order\" contradicts \"handle\"\n"); + return -1; + } + t->tcm_handle |= order; + } + + if (htid) + addattr_l(n, MAX_MSG, TCA_U32_HASH, &htid, 4); + if (sel_ok) + addattr_l(n, MAX_MSG, TCA_U32_SEL, &sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_u32_key)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_U32_MAX+1]; + struct tc_u32_sel *sel = NULL; + struct tc_u32_pcnt *pf = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_U32_MAX, opt); + + if (handle) { + SPRINT_BUF(b1); + fprintf(f, "fh %s ", sprint_u32_handle(handle, b1)); + } + if (TC_U32_NODE(handle)) { + fprintf(f, "order %d ", TC_U32_NODE(handle)); + } + + if (tb[TCA_U32_SEL]) { + if (RTA_PAYLOAD(tb[TCA_U32_SEL]) < sizeof(*sel)) + return -1; + + sel = RTA_DATA(tb[TCA_U32_SEL]); + } + + if (tb[TCA_U32_DIVISOR]) { + fprintf(f, "ht divisor %d ", *(__u32*)RTA_DATA(tb[TCA_U32_DIVISOR])); + } else if (tb[TCA_U32_HASH]) { + __u32 htid = *(__u32*)RTA_DATA(tb[TCA_U32_HASH]); + fprintf(f, "key ht %x bkt %x ", TC_U32_USERHTID(htid), TC_U32_HASH(htid)); + } else { + fprintf(f, "??? "); + } + if (tb[TCA_U32_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "%sflowid %s ", + !sel || !(sel->flags&TC_U32_TERMINAL) ? "*" : "", + sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_U32_CLASSID]), b1)); + } else if (sel && sel->flags&TC_U32_TERMINAL) { + fprintf(f, "terminal flowid ??? "); + } + if (tb[TCA_U32_LINK]) { + SPRINT_BUF(b1); + fprintf(f, "link %s ", sprint_u32_handle(*(__u32*)RTA_DATA(tb[TCA_U32_LINK]), b1)); + } + + if (tb[TCA_U32_PCNT]) { + if (RTA_PAYLOAD(tb[TCA_U32_PCNT]) < sizeof(*pf)) { + fprintf(f, "Broken perf counters \n"); + return -1; + } + pf = RTA_DATA(tb[TCA_U32_PCNT]); + } + + if (sel && show_stats && NULL != pf) + fprintf(f, " (rule hit %llu success %llu)", + (unsigned long long) pf->rcnt, + (unsigned long long) pf->rhit); + + if (tb[TCA_U32_MARK]) { + struct tc_u32_mark *mark = RTA_DATA(tb[TCA_U32_MARK]); + if (RTA_PAYLOAD(tb[TCA_U32_MARK]) < sizeof(*mark)) { + fprintf(f, "\n Invalid mark (kernel&iproute2 mismatch)\n"); + } else { + fprintf(f, "\n mark 0x%04x 0x%04x (success %d)", + mark->val, mark->mask, mark->success); + } + } + + if (sel) { + int i; + struct tc_u32_key *key = sel->keys; + if (sel->nkeys) { + for (i=0; i<sel->nkeys; i++, key++) { + fprintf(f, "\n match %08x/%08x at %s%d", + (unsigned int)ntohl(key->val), + (unsigned int)ntohl(key->mask), + key->offmask ? "nexthdr+" : "", + key->off); + if (show_stats && NULL != pf) + fprintf(f, " (success %lld ) ", + (unsigned long long) pf->kcnts[i]); + } + } + + if (sel->flags&(TC_U32_VAROFFSET|TC_U32_OFFSET)) { + fprintf(f, "\n offset "); + if (sel->flags&TC_U32_VAROFFSET) + fprintf(f, "%04x>>%d at %d ", ntohs(sel->offmask), sel->offshift, sel->offoff); + if (sel->off) + fprintf(f, "plus %d ", sel->off); + } + if (sel->flags&TC_U32_EAT) + fprintf(f, " eat "); + + if (sel->hmask) { + fprintf(f, "\n hash mask %08x at %d ", + (unsigned int)htonl(sel->hmask), sel->hoff); + } + } + + if (tb[TCA_U32_POLICE]) { + fprintf(f, "\n"); + tc_print_police(f, tb[TCA_U32_POLICE]); + } + if (tb[TCA_U32_INDEV]) { + struct rtattr *idev = tb[TCA_U32_INDEV]; + fprintf(f, "\n input dev %s\n", (char *) RTA_DATA(idev)); + } + if (tb[TCA_U32_ACT]) { + tc_print_action(f, tb[TCA_U32_ACT]); + } + + return 0; +} + +struct filter_util u32_filter_util = { + .id = "u32", + .parse_fopt = u32_parse_opt, + .print_fopt = u32_print_opt, +}; diff --git a/tc/f_u32.o b/tc/f_u32.o new file mode 100644 index 0000000..25f9e21 Binary files /dev/null and b/tc/f_u32.o differ diff --git a/tc/libtc.a b/tc/libtc.a new file mode 100644 index 0000000..fc0f818 Binary files /dev/null and b/tc/libtc.a differ diff --git a/tc/m_action.c b/tc/m_action.c new file mode 100644 index 0000000..2d2b0ed --- /dev/null +++ b/tc/m_action.c @@ -0,0 +1,608 @@ +/* + * m_action.c Action Management + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + * TODO: + * - parse to be passed a filedescriptor for logging purposes + * +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <dlfcn.h> + +#include "utils.h" +#include "tc_common.h" +#include "tc_util.h" + +static struct action_util * action_list; +#ifdef CONFIG_GACT +int gact_ld = 0 ; //fuckin backward compatibility +#endif +int batch_c = 0; +int tab_flush = 0; + +void act_usage(void) +{ + fprintf (stderr, "action usage improper\n"); +} + +static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt) +{ + if (opt && RTA_PAYLOAD(opt)) + fprintf(f, "[Unknown action, optlen=%u] ", + (unsigned) RTA_PAYLOAD(opt)); + return 0; +} + +static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + + if (argc) { + fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv); + } else { + fprintf(stderr, "Unknown action \"%s\"\n", au->id); + } + return -1; +} + +struct action_util *get_action_kind(char *str) +{ + static void *aBODY; + void *dlh; + char buf[256]; + struct action_util *a; +#ifdef CONFIG_GACT + int looked4gact = 0; +restart_s: +#endif + for (a = action_list; a; a = a->next) { + if (strcmp(a->id, str) == 0) + return a; + } + + snprintf(buf, sizeof(buf), "m_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = aBODY; + if (dlh == NULL) { + dlh = aBODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_action_util", str); + a = dlsym(dlh, buf); + if (a == NULL) + goto noexist; + +reg: + a->next = action_list; + action_list = a; + return a; + +noexist: +#ifdef CONFIG_GACT + if (!looked4gact) { + looked4gact = 1; + strcpy(str,"gact"); + goto restart_s; + } +#endif + a = malloc(sizeof(*a)); + if (a) { + memset(a, 0, sizeof(*a)); + strncpy(a->id, "noact", 15); + a->parse_aopt = parse_noaopt; + a->print_aopt = print_noaopt; + goto reg; + } + return a; +} + +int +new_cmd(char **argv) +{ + if ((matches(*argv, "change") == 0) || + (matches(*argv, "replace") == 0)|| + (matches(*argv, "delete") == 0)|| + (matches(*argv, "add") == 0)) + return 1; + + return 0; + +} + +int +parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + struct rtattr *tail, *tail2; + char k[16]; + int ok = 0; + int eap = 0; /* expect action parameters */ + + int ret = 0; + int prio = 0; + + if (argc <= 0) + return -1; + + tail = tail2 = NLMSG_TAIL(n); + + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + + while (argc > 0) { + + memset(k, 0, sizeof (k)); + + if (strcmp(*argv, "action") == 0 ) { + argc--; + argv++; + eap = 1; +#ifdef CONFIG_GACT + if (!gact_ld) { + get_action_kind("gact"); + } +#endif + continue; + } else if (strcmp(*argv, "help") == 0) { + return -1; + } else if (new_cmd(argv)) { + goto done0; + } else { + struct action_util *a = NULL; + strncpy(k, *argv, sizeof (k) - 1); + eap = 0; + if (argc > 0 ) { + a = get_action_kind(k); + } else { +done0: + if (ok) + break; + else + goto done; + } + + if (NULL == a) { + goto bad_val; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, ++prio, NULL, 0); + addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + + ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n); + + if (ret < 0) { + fprintf(stderr,"bad action parsing\n"); + goto bad_val; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + ok++; + } + + } + + if (eap > 0) { + fprintf(stderr,"bad action empty %d\n",eap); + goto bad_val; + } + + tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2; + +done: + *argc_p = argc; + *argv_p = argv; + return 0; +bad_val: + /* no need to undo things, returning from here should + * cause enough pain */ + fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv); + return -1; +} + +int +tc_print_one_action(FILE * f, struct rtattr *arg) +{ + + struct rtattr *tb[TCA_ACT_MAX + 1]; + int err = 0; + struct action_util *a = NULL; + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_ACT_MAX, arg); + if (tb[TCA_ACT_KIND] == NULL) { + fprintf(stderr, "NULL Action!\n"); + return -1; + } + + + a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND])); + if (NULL == a) + return err; + + if (tab_flush) { + fprintf(f," %s \n", a->id); + tab_flush = 0; + return 0; + } + + err = a->print_aopt(a,f,tb[TCA_ACT_OPTIONS]); + + + if (0 > err) + return err; + + if (show_stats && tb[TCA_ACT_STATS]) { + fprintf(f, "\tAction statistics:\n"); + print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL); + fprintf(f, "\n"); + } + + return 0; +} + +int +tc_print_action(FILE * f, const struct rtattr *arg) +{ + + int i; + struct rtattr *tb[TCA_ACT_MAX_PRIO + 1]; + + if (arg == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg); + + if (tab_flush && NULL != tb[0] && NULL == tb[1]) { + int ret = tc_print_one_action(f, tb[0]); + return ret; + } + + for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { + if (tb[i]) { + fprintf(f, "\n\taction order %d: ", i + batch_c); + if (0 > tc_print_one_action(f, tb[i])) { + fprintf(f, "Error printing action\n"); + } + } + + } + + batch_c+=TCA_ACT_MAX_PRIO ; + return 0; +} + +static int do_print_action(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcamsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCAA_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*t)); + + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len); + + if (NULL == tb[TCA_ACT_TAB]) { + if (n->nlmsg_type != RTM_GETACTION) + fprintf(stderr, "do_print_action: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELACTION) { + if (n->nlmsg_flags & NLM_F_ROOT) { + fprintf(fp, "Flushed table "); + tab_flush = 1; + } else { + fprintf(fp, "deleted action "); + } + } + + if (n->nlmsg_type == RTM_NEWACTION) + fprintf(fp, "Added action "); + tc_print_action(fp, tb[TCA_ACT_TAB]); + + return 0; +} + +int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p) +{ + char k[16]; + struct action_util *a = NULL; + int argc = *argc_p; + char **argv = *argv_p; + int prio = 0; + int ret = 0; + __u32 i; + struct sockaddr_nl nladdr; + struct rtattr *tail; + struct rtattr *tail2; + struct nlmsghdr *ans = NULL; + + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + argc -=1; + argv +=1; + + + tail = NLMSG_TAIL(&req.n); + addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); + + while (argc > 0) { + if (strcmp(*argv, "action") == 0 ) { + argc--; + argv++; + continue; + } else if (strcmp(*argv, "help") == 0) { + return -1; + } + + strncpy(k, *argv, sizeof (k) - 1); + a = get_action_kind(k); + if (NULL == a) { + fprintf(stderr, "Error: non existent action: %s\n",k); + ret = -1; + goto bad_val; + } + if (strcmp(a->id, k) != 0) { + fprintf(stderr, "Error: non existent action: %s\n",k); + ret = -1; + goto bad_val; + } + + argc -=1; + argv +=1; + if (argc <= 0) { + fprintf(stderr, "Error: no index specified action: %s\n",k); + ret = -1; + goto bad_val; + } + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&i, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + ret = -1; + goto bad_val; + } + argc -=1; + argv +=1; + } else { + fprintf(stderr, "Error: no index specified action: %s\n",k); + ret = -1; + goto bad_val; + } + + tail2 = NLMSG_TAIL(&req.n); + addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); + addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i); + tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; + + } + + tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; + + req.n.nlmsg_seq = rth.dump = ++rth.seq; + if (cmd == RTM_GETACTION) + ans = &req.n; + + if (rtnl_talk(&rth, &req.n, 0, 0, ans, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + return 1; + } + + if (ans && do_print_action(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + *argc_p = argc; + *argv_p = argv; +bad_val: + return ret; +} + +int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p) +{ + int argc = *argc_p; + char **argv = *argv_p; + int ret = 0; + + struct rtattr *tail; + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + tail = NLMSG_TAIL(&req.n); + argc -=1; + argv +=1; + if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + ret = -1; + } + + *argc_p = argc; + *argv_p = argv; + + return ret; +} + +int tc_act_list_or_flush(int argc, char **argv, int event) +{ + int ret = 0, prio = 0, msg_size = 0; + char k[16]; + struct rtattr *tail,*tail2; + struct action_util *a = NULL; + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + + tail = NLMSG_TAIL(&req.n); + addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); + tail2 = NLMSG_TAIL(&req.n); + + strncpy(k, *argv, sizeof (k) - 1); +#ifdef CONFIG_GACT + if (!gact_ld) { + get_action_kind("gact"); + } +#endif + a = get_action_kind(k); + if (NULL == a) { + fprintf(stderr,"bad action %s\n",k); + goto bad_val; + } + if (strcmp(a->id, k) != 0) { + fprintf(stderr,"bad action %s\n",k); + goto bad_val; + } + strncpy(k, *argv, sizeof (k) - 1); + + addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); + addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; + tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; + + msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + + if (event == RTM_GETACTION) { + if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) { + perror("Cannot send dump request"); + return 1; + } + ret = rtnl_dump_filter(&rth, do_print_action, stdout, NULL, NULL); + } + + if (event == RTM_DELACTION) { + req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len); + req.n.nlmsg_type = RTM_DELACTION; + req.n.nlmsg_flags |= NLM_F_ROOT; + req.n.nlmsg_flags |= NLM_F_REQUEST; + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error flushing\n"); + return 1; + } + + } + +bad_val: + + return ret; +} + +int do_action(int argc, char **argv) +{ + + int ret = 0; + + while (argc > 0) { + + if (matches(*argv, "add") == 0) { + ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv); + } else if (matches(*argv, "change") == 0 || + matches(*argv, "replace") == 0) { + ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv); + } else if (matches(*argv, "delete") == 0) { + argc -=1; + argv +=1; + ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv); + } else if (matches(*argv, "get") == 0) { + argc -=1; + argv +=1; + ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv); + } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) { + if (argc <= 2) { + act_usage(); + return -1; + } + return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION); + } else if (matches(*argv, "flush") == 0) { + if (argc <= 2) { + act_usage(); + return -1; + } + return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION); + } else if (matches(*argv, "help") == 0) { + act_usage(); + return -1; + } else { + + ret = -1; + } + + if (ret < 0) { + fprintf(stderr, "Command \"%s\" is unknown, try \"tc action help\".\n", *argv); + return -1; + } + } + + return 0; +} + diff --git a/tc/m_action.o b/tc/m_action.o new file mode 100644 index 0000000..c09dc34 Binary files /dev/null and b/tc/m_action.o differ diff --git a/tc/m_estimator.c b/tc/m_estimator.c new file mode 100644 index 0000000..78eda7a --- /dev/null +++ b/tc/m_estimator.c @@ -0,0 +1,64 @@ +/* + * m_estimator.c Parse/print estimator module options. + * + * This program is free software; you can u32istribute 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +static void est_help(void); + +static void est_help(void) +{ + fprintf(stderr, "Usage: ... estimator INTERVAL TIME-CONST\n"); + fprintf(stderr, " INTERVAL is interval between measurements\n"); + fprintf(stderr, " TIME-CONST is averaging time constant\n"); + fprintf(stderr, "Example: ... est 1sec 8sec\n"); + return; +} + +int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est) +{ + int argc = *p_argc; + char **argv = *p_argv; + unsigned A, time_const; + + NEXT_ARG(); + if (est->ewma_log) + duparg("estimator", *argv); + if (matches(*argv, "help") == 0) + est_help(); + if (get_usecs(&A, *argv)) + invarg("estimator", "invalid estimator interval"); + NEXT_ARG(); + if (matches(*argv, "help") == 0) + est_help(); + if (get_usecs(&time_const, *argv)) + invarg("estimator", "invalid estimator time constant"); + if (tc_setup_estimator(A, time_const, est) < 0) { + fprintf(stderr, "Error: estimator parameters are out of range.\n"); + return -1; + } + if (show_raw) + fprintf(stderr, "[estimator i=%u e=%u]\n", est->interval, est->ewma_log); + *p_argc = argc; + *p_argv = argv; + return 0; +} diff --git a/tc/m_estimator.o b/tc/m_estimator.o new file mode 100644 index 0000000..b64697e Binary files /dev/null and b/tc/m_estimator.o differ diff --git a/tc/m_gact.c b/tc/m_gact.c new file mode 100644 index 0000000..4bb5041 --- /dev/null +++ b/tc/m_gact.c @@ -0,0 +1,244 @@ +/* + * m_gact.c generic actions module + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" +#include <linux/tc_act/tc_gact.h> + +/* define to turn on probablity stuff */ + +#ifdef CONFIG_GACT_PROB +static const char *prob_n2a(int p) +{ + if (p == PGACT_NONE) + return "none"; + if (p == PGACT_NETRAND) + return "netrand"; + if (p == PGACT_DETERM) + return "determ"; + return "none"; +} +#endif + +static void +explain(void) +{ +#ifdef CONFIG_GACT_PROB + fprintf(stderr, "Usage: ... gact <ACTION> [RAND] [INDEX]\n"); + fprintf(stderr, + "Where: ACTION := reclassify | drop | continue | pass " + "RAND := random <RANDTYPE> <ACTION> <VAL>" + "RANDTYPE := netrand | determ" + "VAL : = value not exceeding 10000" + "INDEX := index value used" + "\n"); +#else + fprintf(stderr, "Usage: ... gact <ACTION> [INDEX]\n"); + fprintf(stderr, + "Where: ACTION := reclassify | drop | continue | pass " + "INDEX := index value used" + "\n"); +#endif +} + +#define usage() return(-1) + +int +get_act(char ***argv_p) +{ + char **argv = *argv_p; + + if (matches(*argv, "reclassify") == 0) { + return TC_ACT_RECLASSIFY; + } else if (matches(*argv, "drop") == 0 || matches(*argv, "shot") == 0) { + return TC_ACT_SHOT; + } else if (matches(*argv, "continue") == 0) { + return TC_ACT_UNSPEC; + } else if (matches(*argv, "pipe") == 0) { + return TC_ACT_PIPE; + } else if (matches(*argv, "pass") == 0 || matches(*argv, "ok") == 0) { + return TC_ACT_OK; + } else { + fprintf(stderr,"bad action type %s\n",*argv); + return -10; + } +} + +int +parse_gact(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0; + int action = TC_POLICE_RECLASSIFY; + struct tc_gact p; +#ifdef CONFIG_GACT_PROB + int rd = 0; + struct tc_gact_p pp; +#endif + struct rtattr *tail; + + memset(&p, 0, sizeof (p)); + p.action = TC_POLICE_RECLASSIFY; + + if (argc < 0) + return -1; + + + if (matches(*argv, "gact") == 0) { + ok++; + } else { + action = get_act(&argv); + if (action != -10) { + p.action = action; + ok++; + } else { + explain(); + return action; + } + } + + if (ok) { + argc--; + argv++; + } + +#ifdef CONFIG_GACT_PROB + if (ok && argc > 0) { + if (matches(*argv, "random") == 0) { + rd = 1; + NEXT_ARG(); + if (matches(*argv, "netrand") == 0) { + NEXT_ARG(); + pp.ptype = PGACT_NETRAND; + } else if (matches(*argv, "determ") == 0) { + NEXT_ARG(); + pp.ptype = PGACT_DETERM; + } else { + fprintf(stderr, "Illegal \"random type\"\n"); + return -1; + } + + action = get_act(&argv); + if (action != -10) { /* FIXME */ + pp.paction = action; + } else { + explain(); + return -1; + } + argc--; + argv++; + if (get_u16(&pp.pval, *argv, 10)) { + fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval); + return -1; + } + if (pp.pval > 10000) { + fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval); + return -1; + } + argc--; + argv++; + } + } +#endif + + if (argc > 0) { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + argc--; + argv++; + ok++; + } + } + + if (!ok) + return -1; + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_GACT_PARMS, &p, sizeof (p)); +#ifdef CONFIG_GACT_PROB + if (rd) { + addattr_l(n, MAX_MSG, TCA_GACT_PROB, &pp, sizeof (pp)); + } +#endif + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +int +print_gact(struct action_util *au,FILE * f, struct rtattr *arg) +{ + SPRINT_BUF(b1); +#ifdef CONFIG_GACT_PROB + SPRINT_BUF(b2); + struct tc_gact_p *pp = NULL; + struct tc_gact_p pp_dummy; +#endif + struct tc_gact *p = NULL; + struct rtattr *tb[TCA_GACT_MAX + 1]; + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_GACT_MAX, arg); + + if (tb[TCA_GACT_PARMS] == NULL) { + fprintf(f, "[NULL gact parameters]"); + return -1; + } + p = RTA_DATA(tb[TCA_GACT_PARMS]); + + fprintf(f, "gact action %s", action_n2a(p->action, b1, sizeof (b1))); +#ifdef CONFIG_GACT_PROB + if (NULL != tb[TCA_GACT_PROB]) { + pp = RTA_DATA(tb[TCA_GACT_PROB]); + } else { + /* need to keep consistent output */ + memset(&pp_dummy, 0, sizeof (pp_dummy)); + pp = &pp_dummy; + } + fprintf(f, "\n\t random type %s %s val %d",prob_n2a(pp->ptype), action_n2a(pp->paction, b2, sizeof (b2)), pp->pval); +#endif + fprintf(f, "\n\t index %d ref %d bind %d",p->index, p->refcnt, p->bindcnt); + if (show_stats) { + if (tb[TCA_GACT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_GACT_TM]); + print_tm(f,tm); + } + } + fprintf(f, "\n "); + return 0; +} + +struct action_util gact_action_util = { + .id = "gact", + .parse_aopt = parse_gact, + .print_aopt = print_gact, +}; diff --git a/tc/m_gact.o b/tc/m_gact.o new file mode 100644 index 0000000..6a26b01 Binary files /dev/null and b/tc/m_gact.o differ diff --git a/tc/m_ipt.c b/tc/m_ipt.c new file mode 100644 index 0000000..518e4a3 --- /dev/null +++ b/tc/m_ipt.c @@ -0,0 +1,599 @@ +/* + * m_ipt.c iptables based targets + * utilities mostly ripped from iptables <duh, its the linux way> + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + * TODO: bad bad hardcoding IPT_LIB_DIR and PROC_SYS_MODPROBE + * +*/ + +#include <syslog.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <iptables.h> +#include <linux/netfilter_ipv4/ip_tables.h> +#include "utils.h" +#include "tc_util.h" +#include <linux/tc_act/tc_ipt.h> +#include <stdio.h> +#include <dlfcn.h> +#include <getopt.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> + +const char *pname = "tc-ipt"; +const char *tname = "mangle"; +const char *pversion = "0.1"; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IPT_LIB_DIR +#define IPT_LIB_DIR "/usr/local/lib/iptables" +#endif + +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif + +static const char *ipthooks[] = { + "NF_IP_PRE_ROUTING", + "NF_IP_LOCAL_IN", + "NF_IP_FORWARD", + "NF_IP_LOCAL_OUT", + "NF_IP_POST_ROUTING", +}; + +static struct option original_opts[] = { + {"jump", 1, 0, 'j'}, + {0, 0, 0, 0} +}; + +static struct iptables_target *t_list = NULL; +static unsigned int global_option_offset = 0; +#define OPTION_OFFSET 256 + + +void +register_target(struct iptables_target *me) +{ +/* fprintf(stderr, "\nDummy register_target %s \n", me->name); +*/ + me->next = t_list; + t_list = me; + +} + +void +exit_tryhelp(int status) +{ + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + pname, pname); + exit(status); +} + +void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", pname, pversion); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + exit(status); +} + +/* stolen from iptables 1.2.11 +They should really have them as a library so i can link to them +Email them next time i remember +*/ + +char * +addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +int string_to_number_ll(const char *s, unsigned long long min, + unsigned long long max, + unsigned long long *ret) +{ + unsigned long long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtoull(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && (!max || number <= max)) { + *ret = number; + return 0; + } + } + return -1; +} + +int string_to_number_l(const char *s, unsigned long min, unsigned long max, + unsigned long *ret) +{ + int result; + unsigned long long number; + + result = string_to_number_ll(s, min, max, &number); + *ret = (unsigned long)number; + + return result; +} + +int string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + int result; + unsigned long number; + + result = string_to_number_l(s, min, max, &number); + *ret = (unsigned int)number; + + return result; +} + +static struct option * +copy_options(struct option *oldopts) +{ + struct option *merge; + unsigned int num_old; + for (num_old = 0; oldopts[num_old].name; num_old++) ; + merge = malloc(sizeof (struct option) * (num_old + 1)); + if (NULL == merge) + return NULL; + memcpy(merge, oldopts, num_old * sizeof (struct option)); + memset(merge + num_old, 0, sizeof (struct option)); + return merge; +} + +static struct option * +merge_options(struct option *oldopts, const struct option *newopts, + unsigned int *option_offset) +{ + struct option *merge; + unsigned int num_old, num_new, i; + + for (num_old = 0; oldopts[num_old].name; num_old++) ; + for (num_new = 0; newopts[num_new].name; num_new++) ; + + *option_offset = global_option_offset + OPTION_OFFSET; + + merge = malloc(sizeof (struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof (struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *option_offset; + } + memset(merge + num_old + num_new, 0, sizeof (struct option)); + + return merge; +} + +static void * +fw_calloc(size_t count, size_t size) +{ + void *p; + + if ((p = (void *) calloc(count, size)) == NULL) { + perror("iptables: calloc failed"); + exit(1); + } + return p; +} + +static struct iptables_target * +find_t(char *name) +{ + struct iptables_target *m; + for (m = t_list; m; m = m->next) { + if (strcmp(m->name, name) == 0) + return m; + } + + return NULL; +} + +static struct iptables_target * +get_target_name(char *name) +{ + void *handle; + char *error; + char *new_name, *lname; + struct iptables_target *m; + + char path[sizeof (IPT_LIB_DIR) + sizeof ("/libipt_.so") + strlen(name)]; + + new_name = malloc(strlen(name) + 1); + lname = malloc(strlen(name) + 1); + if (new_name) + memset(new_name, '\0', strlen(name) + 1); + else + exit_error(PARAMETER_PROBLEM, "get_target_name"); + + if (lname) + memset(lname, '\0', strlen(name) + 1); + else + exit_error(PARAMETER_PROBLEM, "get_target_name"); + + strcpy(new_name, name); + strcpy(lname, name); + + if (isupper(lname[0])) { + int i; + for (i = 0; i < strlen(name); i++) { + lname[i] = tolower(lname[i]); + } + } + + if (islower(new_name[0])) { + int i; + for (i = 0; i < strlen(new_name); i++) { + new_name[i] = toupper(new_name[i]); + } + } + + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", new_name); + handle = dlopen(path, RTLD_LAZY); + if (!handle) { + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", lname); + handle = dlopen(path, RTLD_LAZY); + if (!handle) { + fputs(dlerror(), stderr); + printf("\n"); + return NULL; + } + } + + m = dlsym(handle, new_name); + if ((error = dlerror()) != NULL) { + m = (struct iptables_target *) dlsym(handle, lname); + if ((error = dlerror()) != NULL) { + m = find_t(new_name); + if (NULL == m) { + m = find_t(lname); + if (NULL == m) { + fputs(error, stderr); + fprintf(stderr, "\n"); + dlclose(handle); + return NULL; + } + } + } + } + + return m; +} + + +struct in_addr *dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof (buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +int +build_st(struct iptables_target *target, struct ipt_entry_target *t) +{ + unsigned int nfcache = 0; + + if (target) { + size_t size; + + size = + IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size; + + if (NULL == t) { + target->t = fw_calloc(1, size); + target->init(target->t, &nfcache); + target->t->u.target_size = size; + } else { + target->t = t; + } + strcpy(target->t->u.user.name, target->name); + return 0; + } + + return -1; +} + +static int parse_ipt(struct action_util *a,int *argc_p, + char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + struct iptables_target *m = NULL; + struct ipt_entry fw; + struct rtattr *tail; + int c; + int rargc = *argc_p; + char **argv = *argv_p; + struct option *opts; + int argc = 0, iargc = 0; + char k[16]; + int res = -1; + int size = 0; + int iok = 0, ok = 0; + __u32 hook = 0, index = 0; + res = 0; + + { + int i; + for (i = 0; i < rargc; i++) { + if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { + break; + } + } + iargc = argc = i; + } + + if (argc <= 2) { + fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc); + return -1; + } + + opts = copy_options(original_opts); + + if (NULL == opts) + return -1; + + while (1) { + c = getopt_long(argc, argv, "j:", opts, NULL); + if (c == -1) + break; + switch (c) { + case 'j': + m = get_target_name(optarg); + if (NULL != m) { + + if (0 > build_st(m, NULL)) { + printf(" %s error \n", m->name); + return -1; + } + opts = + merge_options(opts, m->extra_opts, + &m->option_offset); + } else { + fprintf(stderr," failed to find target %s\n\n", optarg); + return -1; + } + ok++; + break; + + default: + memset(&fw, 0, sizeof (fw)); + if (m) { + unsigned int fake_flags = 0; + m->parse(c - m->option_offset, argv, 0, + &fake_flags, NULL, &m->t); + } else { + fprintf(stderr," failed to find target %s\n\n", optarg); + return -1; + + } + ok++; + + /*m->final_check(m->t); -- Is this necessary? + ** useful when theres depencies + ** eg ipt_TCPMSS.c has have the TCP match loaded + ** before this can be used; + ** also seems the ECN target needs it + */ + + break; + + } + } + + if (iargc > optind) { + if (matches(argv[optind], "index") == 0) { + if (get_u32(&index, argv[optind + 1], 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + iok++; + + optind += 2; + } + } + + if (!ok && !iok) { + fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); + return -1; + } + + { + struct tcmsg *t = NLMSG_DATA(n); + if (t->tcm_parent != TC_H_ROOT + && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { + hook = NF_IP_PRE_ROUTING; + } else { + hook = NF_IP_POST_ROUTING; + } + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]); + fprintf(stdout, "\ttarget: "); + + if (m) + m->print(NULL, m->t, 0); + fprintf(stdout, " index %d\n", index); + + if (strlen(tname) > 16) { + size = 16; + k[15] = 0; + } else { + size = 1 + strlen(tname); + } + strncpy(k, tname, size); + + addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size); + addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4); + addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4); + if (m) + addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + argc -= optind; + argv += optind; + *argc_p = rargc - iargc; + *argv_p = argv; + + optind = 1; + + return 0; + +} + +static int +print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct rtattr *tb[TCA_IPT_MAX + 1]; + struct ipt_entry_target *t = NULL; + struct option *opts; + + if (arg == NULL) + return -1; + + opts = copy_options(original_opts); + + if (NULL == opts) + return -1; + + parse_rtattr_nested(tb, TCA_IPT_MAX, arg); + + if (tb[TCA_IPT_TABLE] == NULL) { + fprintf(f, "[NULL ipt table name ] assuming mangle "); + } else { + fprintf(f, "tablename: %s ", + (char *) RTA_DATA(tb[TCA_IPT_TABLE])); + } + + if (tb[TCA_IPT_HOOK] == NULL) { + fprintf(f, "[NULL ipt hook name ]\n "); + return -1; + } else { + __u32 hook; + hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]); + fprintf(f, " hook: %s \n", ipthooks[hook]); + } + + if (tb[TCA_IPT_TARG] == NULL) { + fprintf(f, "\t[NULL ipt target parameters ] \n"); + return -1; + } else { + struct iptables_target *m = NULL; + t = RTA_DATA(tb[TCA_IPT_TARG]); + m = get_target_name(t->u.user.name); + if (NULL != m) { + if (0 > build_st(m, t)) { + fprintf(stderr, " %s error \n", m->name); + return -1; + } + + opts = + merge_options(opts, m->extra_opts, + &m->option_offset); + } else { + fprintf(stderr, " failed to find target %s\n\n", + t->u.user.name); + return -1; + } + fprintf(f, "\ttarget "); + m->print(NULL, m->t, 0); + if (tb[TCA_IPT_INDEX] == NULL) { + fprintf(f, " [NULL ipt target index ]\n"); + } else { + __u32 index; + index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]); + fprintf(f, " \n\tindex %d", index); + } + + if (tb[TCA_IPT_CNT]) { + struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; + fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); + } + if (show_stats) { + if (tb[TCA_IPT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); + print_tm(f,tm); + } + } + fprintf(f, " \n"); + + } + + return 0; +} + +struct action_util ipt_action_util = { + .id = "ipt", + .parse_aopt = parse_ipt, + .print_aopt = print_ipt, +}; + diff --git a/tc/m_ipt.o b/tc/m_ipt.o new file mode 100644 index 0000000..8ee39ea Binary files /dev/null and b/tc/m_ipt.o differ diff --git a/tc/m_mirred.c b/tc/m_mirred.c new file mode 100644 index 0000000..6ade2a8 --- /dev/null +++ b/tc/m_mirred.c @@ -0,0 +1,292 @@ +/* + * m_egress.c ingress/egress packet mirror/redir actions module + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + * TODO: Add Ingress support + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" +#include <linux/tc_act/tc_mirred.h> + +int mirred_d = 1; + +static void +explain(void) +{ + fprintf(stderr, "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> \n"); + fprintf(stderr, "where: \n"); + fprintf(stderr, "DIRECTION := <ingress | egress>\n"); + fprintf(stderr, "aCTION := <mirror | redirect>\n"); + fprintf(stderr, " : INDEX is the specific policy instance id\n"); + fprintf(stderr, " : DEVICENAME is the devicename \n"); +} + +#define usage() return(-1) + +char *mirred_n2a(int action) +{ + switch (action) { + case TCA_EGRESS_REDIR: + return "Egress Redirect"; + case TCA_INGRESS_REDIR: + return "Ingress Redirect"; + case TCA_EGRESS_MIRROR: + return "Egress Mirror"; + case TCA_INGRESS_MIRROR: + return "Ingress Mirror"; + default: + return "unknown"; + } +} + +int +parse_egress(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0, iok = 0, mirror=0,redir=0; + struct tc_mirred p; + struct rtattr *tail; + char d[16]; + + memset(d,0,sizeof(d)-1); + memset(&p,0,sizeof(struct tc_mirred)); + + while (argc > 0) { + + if (matches(*argv, "action") == 0) { + break; + } else if (matches(*argv, "egress") == 0) { + NEXT_ARG(); + ok++; + continue; + } else { + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + iok++; + if (!ok) { + argc--; + argv++; + break; + } + } else if(!ok) { + fprintf(stderr, "was expecting egress (%s)\n", *argv); + break; + + } else if (!mirror && matches(*argv, "mirror") == 0) { + mirror=1; + if (redir) { + fprintf(stderr, "Cant have both mirror and redir\n"); + return -1; + } + p.eaction = TCA_EGRESS_MIRROR; + p.action = TC_ACT_PIPE; + ok++; + } else if (!redir && matches(*argv, "redirect") == 0) { + redir=1; + if (mirror) { + fprintf(stderr, "Cant have both mirror and redir\n"); + return -1; + } + p.eaction = TCA_EGRESS_REDIR; + p.action = TC_ACT_STOLEN; + ok++; + } else if ((redir || mirror) && matches(*argv, "dev") == 0) { + NEXT_ARG(); + if (strlen(d)) + duparg("dev", *argv); + + strncpy(d, *argv, sizeof(d)-1); + argc--; + argv++; + + break; + + } + } + + NEXT_ARG(); + } + + if (!ok && !iok) { + explain(); + return -1; + } + + + + if (d[0]) { + int idx; + ll_init_map(&rth); + + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + + p.ifindex = idx; + } + + + if (argc && p.eaction == TCA_EGRESS_MIRROR) { + + if (matches(*argv, "reclassify") == 0) { + p.action = TC_POLICE_RECLASSIFY; + NEXT_ARG(); + } else if (matches(*argv, "pipe") == 0) { + p.action = TC_POLICE_PIPE; + NEXT_ARG(); + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + p.action = TC_POLICE_SHOT; + NEXT_ARG(); + } else if (matches(*argv, "continue") == 0) { + p.action = TC_POLICE_UNSPEC; + NEXT_ARG(); + } else if (matches(*argv, "pass") == 0) { + p.action = TC_POLICE_OK; + NEXT_ARG(); + } + + } + + if (argc) { + if (iok && matches(*argv, "index") == 0) { + fprintf(stderr, "mirred: Illegal double index\n"); + return -1; + } else { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "mirred: Illegal \"index\"\n"); + return -1; + } + argc--; + argv++; + } + } + } + + if (mirred_d) + fprintf(stdout, "Action %d device %s ifindex %d\n",p.action, d,p.ifindex); + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof (p)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + + +int +parse_mirred(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 0) { + fprintf(stderr,"mirred bad arguement count %d\n", argc); + return -1; + } + + if (matches(*argv, "mirred") == 0) { + NEXT_ARG(); + } else { + fprintf(stderr,"mirred bad arguement %s\n", *argv); + return -1; + } + + + if (matches(*argv, "egress") == 0 || matches(*argv, "index") == 0) { + int ret = parse_egress(a, &argc, &argv, tca_id, n); + if (ret == 0) { + *argc_p = argc; + *argv_p = argv; + return 0; + } + + } else if (matches(*argv, "ingress") == 0) { + fprintf(stderr,"mirred ingress not supported at the moment\n"); + + } else { + fprintf(stderr,"mirred not supported %s\n", *argv); + } + + return -1; + +} + +int +print_mirred(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct tc_mirred *p; + struct rtattr *tb[TCA_MIRRED_MAX + 1]; + const char *dev; + SPRINT_BUF(b1); + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_MIRRED_MAX, arg); + + if (tb[TCA_MIRRED_PARMS] == NULL) { + fprintf(f, "[NULL mirred parameters]"); + return -1; + } + p = RTA_DATA(tb[TCA_MIRRED_PARMS]); + + ll_init_map(&rth); + + if ((dev = ll_index_to_name(p->ifindex)) == 0) { + fprintf(stderr, "Cannot find device %d\n", p->ifindex); + return -1; + } + + fprintf(f, "mirred (%s to device %s) %s", mirred_n2a(p->eaction), dev,action_n2a(p->action, b1, sizeof (b1))); + + fprintf(f, "\n "); + fprintf(f, "\tindex %d ref %d bind %d",p->index,p->refcnt,p->bindcnt); + + if (show_stats) { + if (tb[TCA_MIRRED_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_MIRRED_TM]); + print_tm(f,tm); + } + } + fprintf(f, "\n "); + return 0; +} + +struct action_util mirred_util_util = { + .id = "mirred", + .parse_aopt = parse_mirred, + .print_aopt = print_mirred, +}; diff --git a/tc/m_mirred.o b/tc/m_mirred.o new file mode 100644 index 0000000..f04a755 Binary files /dev/null and b/tc/m_mirred.o differ diff --git a/tc/m_pedit.c b/tc/m_pedit.c new file mode 100644 index 0000000..5031c62 --- /dev/null +++ b/tc/m_pedit.c @@ -0,0 +1,604 @@ +/* + * m_pedit.c generic packet editor actions module + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + * TODO: + * 1) Big endian broken in some spots + * 2) A lot of this stuff was added on the fly; get a big double-double + * and clean it up at some point. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <dlfcn.h> +#include "utils.h" +#include "tc_util.h" +#include "m_pedit.h" + +static struct m_pedit_util *pedit_list; +int pedit_debug = 1; + +static void +p_explain(void) +{ + fprintf(stderr, "Usage: ... pedit <MUNGE>\n"); + fprintf(stderr, + "Where: MUNGE := <RAW>|<LAYERED>\n" + "<RAW>:= <OFFSETC>[ATC]<CMD>\n " + "OFFSETC:= offset <offval> <u8|u16|u32>\n " + "ATC:= at <atval> offmask <maskval> shift <shiftval>\n " + "NOTE: offval is byte offset, must be multiple of 4\n " + "NOTE: maskval is a 32 bit hex number\n " + "NOTE: shiftval is a is a shift value\n " + "CMD:= clear | invert | set <setval>| retain\n " + "<LAYERED>:= ip <ipdata> | ip6 <ip6data> \n " + " | udp <udpdata> | tcp <tcpdata> | icmp <icmpdata> \n" + "For Example usage look at the examples directory"); + +} + +#define usage() return(-1) + +static int +pedit_parse_nopopt (int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int argc = *argc_p; + char **argv = *argv_p; + + if (argc) { + fprintf(stderr, "Unknown action hence option \"%s\" is unparsable\n", *argv); + return -1; + } + + return 0; + +} + +struct m_pedit_util +*get_pedit_kind(char *str) +{ + static void *pBODY; + void *dlh; + char buf[256]; + struct m_pedit_util *p; + + for (p = pedit_list; p; p = p->next) { + if (strcmp(p->id, str) == 0) + return p; + } + + snprintf(buf, sizeof(buf), "p_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = pBODY; + if (dlh == NULL) { + dlh = pBODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "p_pedit_%s", str); + p = dlsym(dlh, buf); + if (p == NULL) + goto noexist; + +reg: + p->next = pedit_list; + pedit_list = p; + return p; + +noexist: + p = malloc(sizeof(*p)); + if (p) { + memset(p, 0, sizeof(*p)); + strncpy(p->id, str, sizeof(p->id)-1); + p->parse_peopt = pedit_parse_nopopt; + goto reg; + } + return p; +} + +int +pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int hwm = sel->nkeys; + + if (hwm >= MAX_OFFS) + return -1; + + if (tkey->off % 4) { + fprintf(stderr, "offsets MUST be in 32 bit boundaries\n"); + return -1; + } + + sel->keys[hwm].val = tkey->val; + sel->keys[hwm].mask = tkey->mask; + sel->keys[hwm].off = tkey->off; + sel->keys[hwm].at = tkey->at; + sel->keys[hwm].offmask = tkey->offmask; + sel->keys[hwm].shift = tkey->shift; + sel->nkeys++; + return 0; +} + + +int +pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + if (tkey->off > (tkey->off & ~3)) { + fprintf(stderr, + "pack_key32: 32 bit offsets must begin in 32bit boundaries\n"); + return -1; + } + + tkey->val = htonl(tkey->val & retain); + tkey->mask = htonl(tkey->mask | ~retain); + /* jamal remove this - it is not necessary given the if check above */ + tkey->off &= ~3; + return pack_key(sel,tkey); +} + +int +pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int ind = 0, stride = 0; + __u32 m[4] = {0xFFFF0000,0xFF0000FF,0x0000FFFF}; + + if (0 > tkey->off) { + ind = tkey->off + 1; + if (0 > ind) + ind = -1*ind; + } else { + ind = tkey->off; + } + + if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) { + fprintf(stderr, "pack_key16 bad value\n"); + return -1; + } + + ind = tkey->off & 3; + + if (0 > ind || 2 < ind) { + fprintf(stderr, "pack_key16 bad index value %d\n",ind); + return -1; + } + + stride = 8 * ind; + tkey->val = htons(tkey->val); + if (stride > 0) { + tkey->val <<= stride; + tkey->mask <<= stride; + retain <<= stride; + } + tkey->mask = retain|m[ind]; + + tkey->off &= ~3; + + if (pedit_debug) + printf("pack_key16: Final val %08x mask %08x \n",tkey->val,tkey->mask); + return pack_key(sel,tkey); + +} + +int +pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int ind = 0, stride = 0; + __u32 m[4] = {0xFFFFFF00,0xFFFF00FF,0xFF00FFFF,0x00FFFFFF}; + + if (0 > tkey->off) { + ind = tkey->off + 1; + if (0 > ind) + ind = -1*ind; + } else { + ind = tkey->off; + } + + if (tkey->val > 0xFF || tkey->mask > 0xFF) { + fprintf(stderr, "pack_key8 bad value (val %x mask %x\n", tkey->val, tkey->mask); + return -1; + } + + ind = tkey->off & 3; + stride = 8 * ind; + tkey->val <<= stride; + tkey->mask <<= stride; + retain <<= stride; + tkey->mask = retain|m[ind]; + tkey->off &= ~3; + + if (pedit_debug) + printf("pack_key8: Final word off %d val %08x mask %08x \n",tkey->off , tkey->val,tkey->mask); + return pack_key(sel,tkey); +} + +int +parse_val(int *argc_p, char ***argv_p, __u32 * val, int type) +{ + int argc = *argc_p; + char **argv = *argv_p; + + if (argc <= 0) + return -1; + + if (TINT == type) + return get_integer(val, *argv, 0); + if (TU32 == type) + return get_u32(val, *argv, 0); + if (TIPV4 == type) { + inet_prefix addr; + if (get_prefix_1(&addr, *argv, AF_INET)) { + return -1; + } + *val=addr.data[0]; + return 0; + } + if (TIPV6 == type) { + /* not implemented yet */ + return -1; + } + + return -1; +} + +int +parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + __u32 mask = 0, val = 0; + __u32 o = 0xFF; + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc <= 0) + return -1; + + if (pedit_debug) + printf("parse_cmd argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); + + if (len == 2) + o = 0xFFFF; + if (len == 4) + o = 0xFFFFFFFF; + + if (matches(*argv, "invert") == 0) { + retain = val = mask = o; + } else if (matches(*argv, "set") == 0) { + NEXT_ARG(); + if (parse_val(&argc, &argv, &val, type)) + return -1; + } else if (matches(*argv, "preserve") == 0) { + retain = mask = o; + } else { + if (matches(*argv, "clear") != 0) + return -1; + } + + argc--; argv++; + + if (argc && matches(*argv, "retain") == 0) { + NEXT_ARG(); + if (parse_val(&argc, &argv, &retain, TU32)) + return -1; + argc--; argv++; + } + + tkey->val = val; + + if (len == 1) { + tkey->mask = 0xFF; + res = pack_key8(retain,sel,tkey); + goto done; + } + if (len == 2) { + tkey->mask = mask; + res = pack_key16(retain,sel,tkey); + goto done; + } + if (len == 4) { + tkey->mask = mask; + res = pack_key32(retain,sel,tkey); + goto done; + } + + return -1; +done: + if (pedit_debug) + printf("parse_cmd done argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); + *argc_p = argc; + *argv_p = argv; + return res; + +} + +int +parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int off; + __u32 len, retain; + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + + if (argc <= 0) + return -1; + + if (get_integer(&off, *argv, 0)) + return -1; + tkey->off = off; + + argc--; + argv++; + + if (argc <= 0) + return -1; + + + if (matches(*argv, "u32") == 0) { + len = 4; + retain = 0xFFFFFFFF; + goto done; + } + if (matches(*argv, "u16") == 0) { + len = 2; + retain = 0x0; + goto done; + } + if (matches(*argv, "u8") == 0) { + len = 1; + retain = 0x0; + goto done; + } + + return -1; + +done: + + NEXT_ARG(); + + /* [at <someval> offmask <maskval> shift <shiftval>] */ + if (matches(*argv, "at") == 0) { + + __u32 atv=0,offmask=0x0,shift=0; + + NEXT_ARG(); + if (get_u32(&atv, *argv, 0)) + return -1; + tkey->at = atv; + + NEXT_ARG(); + + if (get_u32(&offmask, *argv, 16)) + return -1; + tkey->offmask = offmask; + + NEXT_ARG(); + + if (get_u32(&shift, *argv, 0)) + return -1; + tkey->shift = shift; + + NEXT_ARG(); + } + + res = parse_cmd(&argc, &argv, len, TU32,retain,sel,tkey); + + *argc_p = argc; + *argv_p = argv; + return res; +} + +int +parse_munge(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel) +{ + struct tc_pedit_key tkey; + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + + if (argc <= 0) + return -1; + + memset(&tkey, 0, sizeof(tkey)); + + if (matches(*argv, "offset") == 0) { + NEXT_ARG(); + res = parse_offset(&argc, &argv,sel,&tkey); + goto done; +#if jamal + } else if (strcmp(*argv, "help") == 0) { + p_explain(); + return -1; +#endif + } else { + char k[16]; + struct m_pedit_util *p = NULL; + + strncpy(k, *argv, sizeof (k) - 1); + + if (argc > 0 ) { + p = get_pedit_kind(k); + if (NULL == p) + goto bad_val; + res = p->parse_peopt(&argc, &argv, sel,&tkey); + if (res < 0) { + fprintf(stderr,"bad pedit parsing\n"); + goto bad_val; + } + goto done; + } + } + +bad_val: + return -1; + +done: + + *argc_p = argc; + *argv_p = argv; + return res; +} + +int +parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + struct { + struct tc_pedit_sel sel; + struct tc_pedit_key keys[MAX_OFFS]; + } sel; + + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0, iok = 0; + struct rtattr *tail; + + memset(&sel, 0, sizeof(sel)); + + while (argc > 0) { + if (pedit_debug > 1) + fprintf(stderr, "while pedit (%d:%s)\n",argc, *argv); + if (matches(*argv, "pedit") == 0) { + NEXT_ARG(); + ok++; + continue; + } else if (matches(*argv, "munge") == 0) { + if (!ok) { + fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); + p_explain(); + return -1; + } + NEXT_ARG(); + if (parse_munge(&argc, &argv,&sel.sel)) { + fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); + p_explain(); + return -1; + } + ok++; + } else { + break; + } + + } + + if (!ok) { + p_explain(); + return -1; + } + + if (argc) { + if (matches(*argv, "reclassify") == 0) { + sel.sel.action = TC_ACT_RECLASSIFY; + NEXT_ARG(); + } else if (matches(*argv, "pipe") == 0) { + sel.sel.action = TC_ACT_PIPE; + NEXT_ARG(); + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + sel.sel.action = TC_ACT_SHOT; + NEXT_ARG(); + } else if (matches(*argv, "continue") == 0) { + sel.sel.action = TC_ACT_UNSPEC; + NEXT_ARG(); + } else if (matches(*argv, "pass") == 0) { + sel.sel.action = TC_ACT_OK; + NEXT_ARG(); + } + } + + if (argc) { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&sel.sel.index, *argv, 10)) { + fprintf(stderr, "Pedit: Illegal \"index\"\n"); + return -1; + } + argc--; + argv++; + iok++; + } + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS,&sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_pedit_key)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +int +print_pedit(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct tc_pedit_sel *sel; + struct rtattr *tb[TCA_PEDIT_MAX + 1]; + SPRINT_BUF(b1); + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg); + + if (tb[TCA_PEDIT_PARMS] == NULL) { + fprintf(f, "[NULL pedit parameters]"); + return -1; + } + sel = RTA_DATA(tb[TCA_PEDIT_PARMS]); + + fprintf(f, " pedit action %s keys %d\n ", action_n2a(sel->action, b1, sizeof (b1)),sel->nkeys); + fprintf(f, "\t index %d ref %d bind %d", sel->index,sel->refcnt, sel->bindcnt); + + if (show_stats) { + if (tb[TCA_PEDIT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]); + print_tm(f,tm); + } + } + if (sel->nkeys) { + int i; + struct tc_pedit_key *key = sel->keys; + + for (i=0; i<sel->nkeys; i++, key++) { + fprintf(f, "\n\t key #%d",i); + fprintf(f, " at %d: val %08x mask %08x", + (unsigned int)key->off, + (unsigned int)ntohl(key->val), + (unsigned int)ntohl(key->mask)); + } + } else { + fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,sel->nkeys); + } + + + fprintf(f, "\n "); + return 0; +} + +int +pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats) +{ + return 0; +} + +struct action_util pedit_action_util = { + .id = "pedit", + .parse_aopt = parse_pedit, + .print_aopt = print_pedit, +}; diff --git a/tc/m_pedit.h b/tc/m_pedit.h new file mode 100644 index 0000000..0a6d24e --- /dev/null +++ b/tc/m_pedit.h @@ -0,0 +1,62 @@ +/* + * m_pedit.h generic packet editor actions module + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + */ + +#ifndef _ACT_PEDIT_H_ +#define _ACT_PEDIT_H_ 1 + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include "utils.h" +#include "tc_util.h" +#include <linux/tc_act/tc_pedit.h> + +#define MAX_OFFS 128 + +#define TIPV4 1 +#define TIPV6 2 +#define TINT 3 +#define TU32 4 + +#define RU32 0xFFFFFFFF +#define RU16 0xFFFF +#define RU8 0xFF + +#define PEDITKINDSIZ 16 + +struct m_pedit_util +{ + struct m_pedit_util *next; + char id[PEDITKINDSIZ]; + int (*parse_peopt)(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +}; + + +extern int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int parse_val(int *argc_p, char ***argv_p, __u32 * val, int type); +extern int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); +extern int print_pedit(struct action_util *au,FILE * f, struct rtattr *arg); +extern int pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats); + +#endif diff --git a/tc/m_pedit.o b/tc/m_pedit.o new file mode 100644 index 0000000..b3f95c8 Binary files /dev/null and b/tc/m_pedit.o differ diff --git a/tc/m_police.c b/tc/m_police.c new file mode 100644 index 0000000..71adb59 --- /dev/null +++ b/tc/m_police.c @@ -0,0 +1,359 @@ +/* + * m_police.c Parse/print policing module options. + * + * This program is free software; you can u32istribute 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * FIXES: 19990619 - J Hadi Salim (hadi@cyberus.ca) + * simple addattr packaging fix. + * 2002: J Hadi Salim - Add tc action extensions syntax + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +struct action_util police_action_util = { + .id = "police", + .parse_aopt = act_parse_police, + .print_aopt = print_police, +}; + +static void explain(void) +{ + fprintf(stderr, "Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]\n"); + fprintf(stderr, " [ peakrate BPS ] [ avrate BPS ]\n"); + fprintf(stderr, " [ ACTIONTERM ]\n"); + fprintf(stderr, "Old Syntax ACTIONTERM := action <EXCEEDACT>[/NOTEXCEEDACT] \n"); + fprintf(stderr, "New Syntax ACTIONTERM := conform-exceed <EXCEEDACT>[/NOTEXCEEDACT] \n"); + fprintf(stderr, "Where: *EXCEEDACT := pipe | ok | reclassify | drop | continue \n"); + fprintf(stderr, "Where: pipe is only valid for new syntax \n"); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + +#define usage() return(-1) + + +char *police_action_n2a(int action, char *buf, int len) +{ + switch (action) { + case -1: + return "continue"; + break; + case TC_POLICE_OK: + return "pass"; + break; + case TC_POLICE_SHOT: + return "drop"; + break; + case TC_POLICE_RECLASSIFY: + return "reclassify"; + case TC_POLICE_PIPE: + return "pipe"; + default: + snprintf(buf, len, "%d", action); + return buf; + } +} + +int police_action_a2n(char *arg, int *result) +{ + int res; + + if (matches(arg, "continue") == 0) + res = -1; + else if (matches(arg, "drop") == 0) + res = TC_POLICE_SHOT; + else if (matches(arg, "shot") == 0) + res = TC_POLICE_SHOT; + else if (matches(arg, "pass") == 0) + res = TC_POLICE_OK; + else if (strcmp(arg, "ok") == 0) + res = TC_POLICE_OK; + else if (matches(arg, "reclassify") == 0) + res = TC_POLICE_RECLASSIFY; + else if (matches(arg, "pipe") == 0) + res = TC_POLICE_PIPE; + else { + char dummy; + if (sscanf(arg, "%d%c", &res, &dummy) != 1) + return -1; + } + *result = res; + return 0; +} + + +int get_police_result(int *action, int *result, char *arg) +{ + char *p = strchr(arg, '/'); + + if (p) + *p = 0; + + if (police_action_a2n(arg, action)) { + if (p) + *p = '/'; + return -1; + } + + if (p) { + *p = '/'; + if (police_action_a2n(p+1, result)) + return -1; + } + return 0; +} + + +int act_parse_police(struct action_util *a,int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + int ok=0; + struct tc_police p; + __u32 rtab[256]; + __u32 ptab[256]; + __u32 avrate = 0; + int presult = 0; + unsigned buffer=0, mtu=0, mpu=0; + int Rcell_log=-1, Pcell_log = -1; + struct rtattr *tail; + + memset(&p, 0, sizeof(p)); + p.action = TC_POLICE_RECLASSIFY; + + if (a) /* new way of doing things */ + NEXT_ARG(); + + if (argc <= 0) + return -1; + + while (argc > 0) { + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + } else if (matches(*argv, "burst") == 0 || + strcmp(*argv, "buffer") == 0 || + strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (buffer) { + fprintf(stderr, "Double \"buffer/burst\" spec\n"); + return -1; + } + if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) { + explain1("buffer"); + return -1; + } + } else if (strcmp(*argv, "mtu") == 0 || + strcmp(*argv, "minburst") == 0) { + NEXT_ARG(); + if (mtu) { + fprintf(stderr, "Double \"mtu/minburst\" spec\n"); + return -1; + } + if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) { + explain1("mtu"); + return -1; + } + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (mpu) { + fprintf(stderr, "Double \"mpu\" spec\n"); + return -1; + } + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + } else if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (p.rate.rate) { + fprintf(stderr, "Double \"rate\" spec\n"); + return -1; + } + if (get_rate(&p.rate.rate, *argv)) { + explain1("rate"); + return -1; + } + } else if (strcmp(*argv, "avrate") == 0) { + NEXT_ARG(); + if (avrate) { + fprintf(stderr, "Double \"avrate\" spec\n"); + return -1; + } + if (get_rate(&avrate, *argv)) { + explain1("avrate"); + return -1; + } + } else if (matches(*argv, "peakrate") == 0) { + NEXT_ARG(); + if (p.peakrate.rate) { + fprintf(stderr, "Double \"peakrate\" spec\n"); + return -1; + } + if (get_rate(&p.peakrate.rate, *argv)) { + explain1("peakrate"); + return -1; + } + } else if (matches(*argv, "reclassify") == 0) { + p.action = TC_POLICE_RECLASSIFY; + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + p.action = TC_POLICE_SHOT; + } else if (matches(*argv, "continue") == 0) { + p.action = TC_POLICE_UNSPEC; + } else if (matches(*argv, "pass") == 0) { + p.action = TC_POLICE_OK; + } else if (matches(*argv, "pipe") == 0) { + p.action = TC_POLICE_PIPE; + } else if (strcmp(*argv, "conform-exceed") == 0) { + NEXT_ARG(); + if (get_police_result(&p.action, &presult, *argv)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + break; + } + ok++; + argc--; argv++; + } + + if (!ok) + return -1; + + if (p.rate.rate && !buffer) { + fprintf(stderr, "\"burst\" requires \"rate\".\n"); + return -1; + } + if (p.peakrate.rate) { + if (!p.rate.rate) { + fprintf(stderr, "\"peakrate\" requires \"rate\".\n"); + return -1; + } + if (!mtu) { + fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n"); + return -1; + } + } + + if (p.rate.rate) { + if ((Rcell_log = tc_calc_rtable(p.rate.rate, rtab, Rcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "TBF: failed to calculate rate table.\n"); + return -1; + } + p.burst = tc_calc_xmittime(p.rate.rate, buffer); + p.rate.cell_log = Rcell_log; + p.rate.mpu = mpu; + } + p.mtu = mtu; + if (p.peakrate.rate) { + if ((Pcell_log = tc_calc_rtable(p.peakrate.rate, ptab, Pcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "POLICE: failed to calculate peak rate table.\n"); + return -1; + } + p.peakrate.cell_log = Pcell_log; + p.peakrate.mpu = mpu; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_POLICE_TBF, &p, sizeof(p)); + if (p.rate.rate) + addattr_l(n, MAX_MSG, TCA_POLICE_RATE, rtab, 1024); + if (p.peakrate.rate) + addattr_l(n, MAX_MSG, TCA_POLICE_PEAKRATE, ptab, 1024); + if (avrate) + addattr32(n, MAX_MSG, TCA_POLICE_AVRATE, avrate); + if (presult) + addattr32(n, MAX_MSG, TCA_POLICE_RESULT, presult); + + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + return act_parse_police(NULL,argc_p,argv_p,tca_id,n); +} + +int +print_police(struct action_util *a, FILE *f, struct rtattr *arg) +{ + SPRINT_BUF(b1); + struct tc_police *p; + struct rtattr *tb[TCA_POLICE_MAX+1]; + unsigned buffer; + + if (arg == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_POLICE_MAX, arg); + + if (tb[TCA_POLICE_TBF] == NULL) { + fprintf(f, "[NULL police tbf]"); + return 0; + } +#ifndef STOOPID_8BYTE + if (RTA_PAYLOAD(tb[TCA_POLICE_TBF]) < sizeof(*p)) { + fprintf(f, "[truncated police tbf]"); + return -1; + } +#endif + p = RTA_DATA(tb[TCA_POLICE_TBF]); + + fprintf(f, " police 0x%x ", p->index); + fprintf(f, "rate %s ", sprint_rate(p->rate.rate, b1)); + buffer = ((double)p->rate.rate*tc_core_tick2usec(p->burst))/1000000; + fprintf(f, "burst %s ", sprint_size(buffer, b1)); + fprintf(f, "mtu %s ", sprint_size(p->mtu, b1)); + if (show_raw) + fprintf(f, "[%08x] ", p->burst); + if (p->peakrate.rate) + fprintf(f, "peakrate %s ", sprint_rate(p->peakrate.rate, b1)); + if (tb[TCA_POLICE_AVRATE]) + fprintf(f, "avrate %s ", sprint_rate(*(__u32*)RTA_DATA(tb[TCA_POLICE_AVRATE]), b1)); + fprintf(f, "action %s", police_action_n2a(p->action, b1, sizeof(b1))); + if (tb[TCA_POLICE_RESULT]) { + fprintf(f, "/%s ", police_action_n2a(*(int*)RTA_DATA(tb[TCA_POLICE_RESULT]), b1, sizeof(b1))); + } else + fprintf(f, " "); + fprintf(f, "\nref %d bind %d\n",p->refcnt, p->bindcnt); + + return 0; +} + +int +tc_print_police(FILE *f, struct rtattr *arg) { + return print_police(&police_action_util,f,arg); +} diff --git a/tc/m_police.o b/tc/m_police.o new file mode 100644 index 0000000..aa67707 Binary files /dev/null and b/tc/m_police.o differ diff --git a/tc/p_icmp.c b/tc/p_icmp.c new file mode 100644 index 0000000..f9ddbe3 --- /dev/null +++ b/tc/p_icmp.c @@ -0,0 +1,61 @@ +/* + * m_pedit_icmp.c packet editor: ICMP header + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include "utils.h" +#include "tc_util.h" +#include "m_pedit.h" + + +static int +parse_icmp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; +#if 0 + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, 0); + goto done; + } + if (strcmp(*argv, "code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, 1); + goto done; + } + return -1; + + done: + *argc_p = argc; + *argv_p = argv; +#endif + return res; +} + +struct m_pedit_util p_pedit_icmp = { + NULL, + "icmp", + parse_icmp, +}; diff --git a/tc/p_icmp.o b/tc/p_icmp.o new file mode 100644 index 0000000..06cd4f4 Binary files /dev/null and b/tc/p_icmp.o differ diff --git a/tc/p_ip.c b/tc/p_ip.c new file mode 100644 index 0000000..1b2a440 --- /dev/null +++ b/tc/p_ip.c @@ -0,0 +1,159 @@ +/* + * m_pedit.c packet editor: IPV4/6 header + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include "utils.h" +#include "tc_util.h" +#include "m_pedit.h" + +static int +parse_ip(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + tkey->off = 12; + res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + tkey->off = 16; + res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey); + goto done; + } + /* jamal - look at these and make them either old or new + ** scheme given diffserv + ** dont forget the CE bit + */ + if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { + NEXT_ARG(); + tkey->off = 1; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + if (strcmp(*argv, "ihl") == 0) { + NEXT_ARG(); + tkey->off = 0; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + if (strcmp(*argv, "protocol") == 0) { + NEXT_ARG(); + tkey->off = 9; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + /* jamal - fix this */ + if (matches(*argv, "precedence") == 0) { + NEXT_ARG(); + tkey->off = 1; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + /* jamal - validate this at some point */ + if (strcmp(*argv, "nofrag") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x3F,sel,tkey); + goto done; + } + /* jamal - validate this at some point */ + if (strcmp(*argv, "firstfrag") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x1F,sel,tkey); + goto done; + } + if (strcmp(*argv, "ce") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x80,sel,tkey); + goto done; + } + if (strcmp(*argv, "df") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x40,sel,tkey); + goto done; + } + if (strcmp(*argv, "mf") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x20,sel,tkey); + goto done; + } + if (strcmp(*argv, "dport") == 0) { + NEXT_ARG(); + tkey->off = 22; + res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey); + goto done; + } + if (strcmp(*argv, "sport") == 0) { + NEXT_ARG(); + tkey->off = 20; + res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey); + goto done; + } + if (strcmp(*argv, "icmp_type") == 0) { + NEXT_ARG(); + tkey->off = 20; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + if (strcmp(*argv, "icmp_code") == 0) { + NEXT_ARG(); + tkey->off = 20; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + return -1; + + done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int +parse_ip6(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + return res; +} + +struct m_pedit_util p_pedit_ip = { + NULL, + "ip", + parse_ip, +}; + + +struct m_pedit_util p_pedit_ip6 = { + NULL, + "ip6", + parse_ip6, +}; diff --git a/tc/p_ip.o b/tc/p_ip.o new file mode 100644 index 0000000..3479e0b Binary files /dev/null and b/tc/p_ip.o differ diff --git a/tc/p_tcp.c b/tc/p_tcp.c new file mode 100644 index 0000000..aab37a6 --- /dev/null +++ b/tc/p_tcp.c @@ -0,0 +1,38 @@ +/* + * m_pedit_tcp.c packet editor: TCP header + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include "utils.h" +#include "tc_util.h" +#include "m_pedit.h" + +static int +parse_tcp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + return res; +} +struct m_pedit_util p_pedit_tcp = { + NULL, + "tcp", + parse_tcp, +}; + + diff --git a/tc/p_tcp.o b/tc/p_tcp.o new file mode 100644 index 0000000..eecab5e Binary files /dev/null and b/tc/p_tcp.o differ diff --git a/tc/p_udp.c b/tc/p_udp.c new file mode 100644 index 0000000..95ed993 --- /dev/null +++ b/tc/p_udp.c @@ -0,0 +1,38 @@ +/* + * m_pedit_udp.c packet editor: UDP header + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include "utils.h" +#include "tc_util.h" +#include "m_pedit.h" + +static int +parse_udp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + return res; +} + +struct m_pedit_util p_pedit_udp = { + NULL, + "udp", + parse_udp, +}; + diff --git a/tc/p_udp.o b/tc/p_udp.o new file mode 100644 index 0000000..61232d9 Binary files /dev/null and b/tc/p_udp.o differ diff --git a/tc/q_atm.c b/tc/q_atm.c new file mode 100644 index 0000000..4c8dc0b --- /dev/null +++ b/tc/q_atm.c @@ -0,0 +1,260 @@ +/* + * q_atm.c ATM. + * + * Hacked 1998-2000 by Werner Almesberger, EPFL ICA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <atm.h> +#include <linux/atmdev.h> +#include <linux/atmarp.h> + +#include "utils.h" +#include "tc_util.h" + + +#define MAX_HDR_LEN 64 + +#define usage() return(-1) + + +static int atm_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + if (argc) { + fprintf(stderr,"Usage: atm\n"); + return -1; + } + return 0; +} + + +static void explain(void) +{ + fprintf(stderr, "Usage: ... atm ( pvc ADDR | svc ADDR [ sap SAP ] ) " + "[ qos QOS ] [ sndbuf BYTES ]\n"); + fprintf(stderr, " [ hdr HEX... ] [ excess ( CLASSID | clp ) ] " + "[ clip ]\n"); +} + + +static int atm_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct sockaddr_atmsvc addr; + struct atm_qos qos; + struct atm_sap sap; + unsigned char hdr[MAX_HDR_LEN]; + __u32 excess = 0; + struct rtattr *tail; + int sndbuf = 0; + int hdr_len = -1; + int set_clip = 0; + int s; + + memset(&addr,0,sizeof(addr)); + (void) text2qos("aal5,ubr:sdu=9180,rx:none",&qos,0); + (void) text2sap("blli:l2=iso8802",&sap,0); + while (argc > 0) { + if (!strcmp(*argv,"pvc")) { + NEXT_ARG(); + if (text2atm(*argv,(struct sockaddr *) &addr, + sizeof(addr),T2A_PVC | T2A_NAME) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"svc")) { + NEXT_ARG(); + if (text2atm(*argv,(struct sockaddr *) &addr, + sizeof(addr),T2A_SVC | T2A_NAME) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"qos")) { + NEXT_ARG(); + if (text2qos(*argv,&qos,0) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"sndbuf")) { + char *end; + + NEXT_ARG(); + sndbuf = strtol(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"sap")) { + NEXT_ARG(); + if (addr.sas_family != AF_ATMSVC || + text2sap(*argv,&sap,T2A_NAME) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"hdr")) { + unsigned char *ptr; + char *walk; + + NEXT_ARG(); + ptr = hdr; + for (walk = *argv; *walk; walk++) { + int tmp; + + if (ptr == hdr+MAX_HDR_LEN) { + fprintf(stderr,"header is too long\n"); + return -1; + } + if (*walk == '.') continue; + if (!isxdigit(walk[0]) || !walk[1] || + !isxdigit(walk[1])) { + explain(); + return -1; + } + sscanf(walk,"%2x",&tmp); + *ptr++ = tmp; + walk++; + } + hdr_len = ptr-hdr; + } + else if (!strcmp(*argv,"excess")) { + NEXT_ARG(); + if (!strcmp(*argv,"clp")) excess = 0; + else if (get_tc_classid(&excess,*argv)) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"clip")) { + set_clip = 1; + } + else { + explain(); + return 1; + } + argc--; + argv++; + } + s = socket(addr.sas_family,SOCK_DGRAM,0); + if (s < 0) { + perror("socket"); + return -1; + } + if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) { + perror("SO_ATMQOS"); + return -1; + } + if (sndbuf) + if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + if (addr.sas_family == AF_ATMSVC && setsockopt(s,SOL_ATM,SO_ATMSAP, + &sap,sizeof(sap)) < 0) { + perror("SO_ATMSAP"); + return -1; + } + if (connect(s,(struct sockaddr *) &addr,addr.sas_family == AF_ATMPVC ? + sizeof(struct sockaddr_atmpvc) : sizeof(addr)) < 0) { + perror("connect"); + return -1; + } + if (set_clip) + if (ioctl(s,ATMARP_MKIP,0) < 0) { + perror("ioctl ATMARP_MKIP"); + return -1; + } + tail = NLMSG_TAIL(n); + addattr_l(n,1024,TCA_OPTIONS,NULL,0); + addattr_l(n,1024,TCA_ATM_FD,&s,sizeof(s)); + if (excess) addattr_l(n,1024,TCA_ATM_EXCESS,&excess,sizeof(excess)); + if (hdr_len != -1) addattr_l(n,1024,TCA_ATM_HDR,hdr,hdr_len); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + + +static int atm_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_ATM_MAX+1]; + char buffer[MAX_ATM_ADDR_LEN+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ATM_MAX, opt); + if (tb[TCA_ATM_ADDR]) { + if (RTA_PAYLOAD(tb[TCA_ATM_ADDR]) < + sizeof(struct sockaddr_atmpvc)) + fprintf(stderr,"ATM: address too short\n"); + else { + if (atm2text(buffer,MAX_ATM_ADDR_LEN, + RTA_DATA(tb[TCA_ATM_ADDR]),A2T_PRETTY | A2T_NAME) < + 0) fprintf(stderr,"atm2text error\n"); + fprintf(f,"pvc %s ",buffer); + } + } + if (tb[TCA_ATM_HDR]) { + int i; + + fprintf(f,"hdr"); + for (i = 0; i < RTA_PAYLOAD(tb[TCA_ATM_HDR]); i++) + fprintf(f,"%c%02x",i ? '.' : ' ', + ((unsigned char *) RTA_DATA(tb[TCA_ATM_HDR]))[i]); + if (!i) fprintf(f," ."); + fprintf(f," "); + } + if (tb[TCA_ATM_EXCESS]) { + __u32 excess; + + if (RTA_PAYLOAD(tb[TCA_ATM_EXCESS]) < sizeof(excess)) + fprintf(stderr,"ATM: excess class ID too short\n"); + else { + excess = *(__u32 *) RTA_DATA(tb[TCA_ATM_EXCESS]); + if (!excess) fprintf(f,"excess clp "); + else { + char buf[64]; + + print_tc_classid(buf,sizeof(buf),excess); + fprintf(f,"excess %s ",buf); + } + } + } + if (tb[TCA_ATM_STATE]) { + static const char *map[] = { ATM_VS2TXT_MAP }; + int state; + + if (RTA_PAYLOAD(tb[TCA_ATM_STATE]) < sizeof(state)) + fprintf(stderr,"ATM: state field too short\n"); + else { + state = *(int *) RTA_DATA(tb[TCA_ATM_STATE]); + fprintf(f,"%s ",map[state]); + } + } + return 0; +} + + +struct qdisc_util atm_qdisc_util = { + .id = "atm", + .parse_qopt = atm_parse_opt, + .print_qopt = atm_print_opt, + .parse_copt = atm_parse_class_opt, + .print_copt = atm_print_opt, +}; diff --git a/tc/q_cbq.c b/tc/q_cbq.c new file mode 100644 index 0000000..40c0228 --- /dev/null +++ b/tc/q_cbq.c @@ -0,0 +1,553 @@ +/* + * q_cbq.c CBQ. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" +#include "tc_cbq.h" + +static void explain_class(void) +{ + fprintf(stderr, "Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]\n"); + fprintf(stderr, " [ minburst PKTS ] [ bounded ] [ isolated ]\n"); + fprintf(stderr, " [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]\n"); + fprintf(stderr, " [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]\n"); + fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); + fprintf(stderr, " [ split CLASSID ] [ defmap MASK/CHANGE ]\n"); +} + +static void explain(void) +{ + fprintf(stderr, "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n"); + fprintf(stderr, " [ cell BYTES ] [ ewma LOG ]\n"); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + +#define usage() return(-1) + +static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_ratespec r; + struct tc_cbq_lssopt lss; + __u32 rtab[256]; + unsigned mpu=0, avpkt=0, allot=0; + int cell_log=-1; + int ewma_log=-1; + struct rtattr *tail; + + memset(&lss, 0, sizeof(lss)); + memset(&r, 0, sizeof(r)); + + while (argc > 0) { + if (strcmp(*argv, "bandwidth") == 0 || + strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (get_rate(&r.rate, *argv)) { + explain1("bandwidth"); + return -1; + } + } else if (strcmp(*argv, "ewma") == 0) { + NEXT_ARG(); + if (get_unsigned(&ewma_log, *argv, 0)) { + explain1("ewma"); + return -1; + } + if (ewma_log > 31) { + fprintf(stderr, "ewma_log must be < 32\n"); + return -1; + } + } else if (strcmp(*argv, "cell") == 0) { + unsigned cell; + int i; + NEXT_ARG(); + if (get_size(&cell, *argv)) { + explain1("cell"); + return -1; + } + for (i=0; i<32; i++) + if ((1<<i) == cell) + break; + if (i>=32) { + fprintf(stderr, "cell must be 2^n\n"); + return -1; + } + cell_log = i; + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&avpkt, *argv)) { + explain1("avpkt"); + return -1; + } + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + } else if (strcmp(*argv, "allot") == 0) { + NEXT_ARG(); + /* Accept and ignore "allot" for backward compatibility */ + if (get_size(&allot, *argv)) { + explain1("allot"); + return -1; + } + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + /* OK. All options are parsed. */ + + if (r.rate == 0) { + fprintf(stderr, "CBQ: bandwidth is required parameter.\n"); + return -1; + } + if (avpkt == 0) { + fprintf(stderr, "CBQ: \"avpkt\" is required.\n"); + return -1; + } + if (allot < (avpkt*3)/2) + allot = (avpkt*3)/2; + + if ((cell_log = tc_calc_rtable(r.rate, rtab, cell_log, allot, mpu)) < 0) { + fprintf(stderr, "CBQ: failed to calculate rate table.\n"); + return -1; + } + r.cell_log = cell_log; + r.mpu = mpu; + + if (ewma_log < 0) + ewma_log = TC_CBQ_DEF_EWMA; + lss.ewma_log = ewma_log; + lss.maxidle = tc_cbq_calc_maxidle(r.rate, r.rate, avpkt, lss.ewma_log, 0); + lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT; + lss.avpkt = avpkt; + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r)); + addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss)); + addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024); + if (show_raw) { + int i; + for (i=0; i<256; i++) + printf("%u ", rtab[i]); + printf("\n"); + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int wrr_ok=0, fopt_ok=0; + struct tc_ratespec r; + struct tc_cbq_lssopt lss; + struct tc_cbq_wrropt wrr; + struct tc_cbq_fopt fopt; + struct tc_cbq_ovl ovl; + __u32 rtab[256]; + unsigned mpu=0; + int cell_log=-1; + int ewma_log=-1; + unsigned bndw = 0; + unsigned minburst=0, maxburst=0; + struct rtattr *tail; + + memset(&r, 0, sizeof(r)); + memset(&lss, 0, sizeof(lss)); + memset(&wrr, 0, sizeof(wrr)); + memset(&fopt, 0, sizeof(fopt)); + memset(&ovl, 0, sizeof(ovl)); + + while (argc > 0) { + if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (get_rate(&r.rate, *argv)) { + explain1("rate"); + return -1; + } + } else if (strcmp(*argv, "bandwidth") == 0) { + NEXT_ARG(); + if (get_rate(&bndw, *argv)) { + explain1("bandwidth"); + return -1; + } + } else if (strcmp(*argv, "minidle") == 0) { + NEXT_ARG(); + if (get_u32(&lss.minidle, *argv, 0)) { + explain1("minidle"); + return -1; + } + lss.change |= TCF_CBQ_LSS_MINIDLE; + } else if (strcmp(*argv, "minburst") == 0) { + NEXT_ARG(); + if (get_u32(&minburst, *argv, 0)) { + explain1("minburst"); + return -1; + } + lss.change |= TCF_CBQ_LSS_OFFTIME; + } else if (strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (get_u32(&maxburst, *argv, 0)) { + explain1("maxburst"); + return -1; + } + lss.change |= TCF_CBQ_LSS_MAXIDLE; + } else if (strcmp(*argv, "bounded") == 0) { + lss.flags |= TCF_CBQ_LSS_BOUNDED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "borrow") == 0) { + lss.flags &= ~TCF_CBQ_LSS_BOUNDED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "isolated") == 0) { + lss.flags |= TCF_CBQ_LSS_ISOLATED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "sharing") == 0) { + lss.flags &= ~TCF_CBQ_LSS_ISOLATED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "ewma") == 0) { + NEXT_ARG(); + if (get_u32(&ewma_log, *argv, 0)) { + explain1("ewma"); + return -1; + } + if (ewma_log > 31) { + fprintf(stderr, "ewma_log must be < 32\n"); + return -1; + } + lss.change |= TCF_CBQ_LSS_EWMA; + } else if (strcmp(*argv, "cell") == 0) { + unsigned cell; + int i; + NEXT_ARG(); + if (get_size(&cell, *argv)) { + explain1("cell"); + return -1; + } + for (i=0; i<32; i++) + if ((1<<i) == cell) + break; + if (i>=32) { + fprintf(stderr, "cell must be 2^n\n"); + return -1; + } + cell_log = i; + } else if (strcmp(*argv, "prio") == 0) { + unsigned prio; + NEXT_ARG(); + if (get_u32(&prio, *argv, 0)) { + explain1("prio"); + return -1; + } + if (prio > TC_CBQ_MAXPRIO) { + fprintf(stderr, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO); + return -1; + } + wrr.priority = prio; + wrr_ok++; + } else if (strcmp(*argv, "allot") == 0) { + NEXT_ARG(); + if (get_size(&wrr.allot, *argv)) { + explain1("allot"); + return -1; + } + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&lss.avpkt, *argv)) { + explain1("avpkt"); + return -1; + } + lss.change |= TCF_CBQ_LSS_AVPKT; + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + } else if (strcmp(*argv, "weight") == 0) { + NEXT_ARG(); + if (get_size(&wrr.weight, *argv)) { + explain1("weight"); + return -1; + } + wrr_ok++; + } else if (strcmp(*argv, "split") == 0) { + NEXT_ARG(); + if (get_tc_classid(&fopt.split, *argv)) { + fprintf(stderr, "Invalid split node ID.\n"); + usage(); + } + fopt_ok++; + } else if (strcmp(*argv, "defmap") == 0) { + int err; + NEXT_ARG(); + err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange); + if (err < 1) { + fprintf(stderr, "Invalid defmap, should be MASK32[/MASK]\n"); + return -1; + } + if (err == 1) + fopt.defchange = ~0; + fopt_ok++; + } else if (strcmp(*argv, "help") == 0) { + explain_class(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain_class(); + return -1; + } + argc--; argv++; + } + + /* OK. All options are parsed. */ + + /* 1. Prepare link sharing scheduler parameters */ + if (r.rate) { + unsigned pktsize = wrr.allot; + if (wrr.allot < (lss.avpkt*3)/2) + wrr.allot = (lss.avpkt*3)/2; + if ((cell_log = tc_calc_rtable(r.rate, rtab, cell_log, pktsize, mpu)) < 0) { + fprintf(stderr, "CBQ: failed to calculate rate table.\n"); + return -1; + } + r.cell_log = cell_log; + r.mpu = mpu; + } + if (ewma_log < 0) + ewma_log = TC_CBQ_DEF_EWMA; + lss.ewma_log = ewma_log; + if (lss.change&(TCF_CBQ_LSS_OFFTIME|TCF_CBQ_LSS_MAXIDLE)) { + if (lss.avpkt == 0) { + fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n"); + return -1; + } + if (bndw==0 || r.rate == 0) { + fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n"); + return -1; + } + } + if (wrr.priority == 0 && (n->nlmsg_flags&NLM_F_EXCL)) { + wrr_ok = 1; + wrr.priority = TC_CBQ_MAXPRIO; + if (wrr.allot == 0) + wrr.allot = (lss.avpkt*3)/2; + } + if (wrr_ok) { + if (wrr.weight == 0) + wrr.weight = (wrr.priority == TC_CBQ_MAXPRIO) ? 1 : r.rate; + if (wrr.allot == 0) { + fprintf(stderr, "CBQ: \"allot\" is required to set WRR parameters.\n"); + return -1; + } + } + if (lss.change&TCF_CBQ_LSS_MAXIDLE) { + lss.maxidle = tc_cbq_calc_maxidle(bndw, r.rate, lss.avpkt, ewma_log, maxburst); + lss.change |= TCF_CBQ_LSS_MAXIDLE; + lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT; + } + if (lss.change&TCF_CBQ_LSS_OFFTIME) { + lss.offtime = tc_cbq_calc_offtime(bndw, r.rate, lss.avpkt, ewma_log, minburst); + lss.change |= TCF_CBQ_LSS_OFFTIME; + lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT; + } + if (lss.change&TCF_CBQ_LSS_MINIDLE) { + lss.minidle <<= lss.ewma_log; + lss.change |= TCF_CBQ_LSS_EWMA; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + if (lss.change) { + lss.change |= TCF_CBQ_LSS_FLAGS; + addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss)); + } + if (wrr_ok) + addattr_l(n, 1024, TCA_CBQ_WRROPT, &wrr, sizeof(wrr)); + if (fopt_ok) + addattr_l(n, 1024, TCA_CBQ_FOPT, &fopt, sizeof(fopt)); + if (r.rate) { + addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r)); + addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024); + if (show_raw) { + int i; + for (i=0; i<256; i++) + printf("%u ", rtab[i]); + printf("\n"); + } + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + +static int cbq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_CBQ_MAX+1]; + struct tc_ratespec *r = NULL; + struct tc_cbq_lssopt *lss = NULL; + struct tc_cbq_wrropt *wrr = NULL; + struct tc_cbq_fopt *fopt = NULL; + struct tc_cbq_ovl *ovl = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_CBQ_MAX, opt); + + if (tb[TCA_CBQ_RATE]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r)) + fprintf(stderr, "CBQ: too short rate opt\n"); + else + r = RTA_DATA(tb[TCA_CBQ_RATE]); + } + if (tb[TCA_CBQ_LSSOPT]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss)) + fprintf(stderr, "CBQ: too short lss opt\n"); + else + lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]); + } + if (tb[TCA_CBQ_WRROPT]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr)) + fprintf(stderr, "CBQ: too short wrr opt\n"); + else + wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]); + } + if (tb[TCA_CBQ_FOPT]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt)) + fprintf(stderr, "CBQ: too short fopt\n"); + else + fopt = RTA_DATA(tb[TCA_CBQ_FOPT]); + } + if (tb[TCA_CBQ_OVL_STRATEGY]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl)) + fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n", + (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]), + (unsigned) sizeof(*ovl)); + else + ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]); + } + + if (r) { + char buf[64]; + print_rate(buf, sizeof(buf), r->rate); + fprintf(f, "rate %s ", buf); + if (show_details) { + fprintf(f, "cell %ub ", 1<<r->cell_log); + if (r->mpu) + fprintf(f, "mpu %ub ", r->mpu); + } + } + if (lss && lss->flags) { + int comma=0; + fprintf(f, "("); + if (lss->flags&TCF_CBQ_LSS_BOUNDED) { + fprintf(f, "bounded"); + comma=1; + } + if (lss->flags&TCF_CBQ_LSS_ISOLATED) { + if (comma) + fprintf(f, ","); + fprintf(f, "isolated"); + } + fprintf(f, ") "); + } + if (wrr) { + if (wrr->priority != TC_CBQ_MAXPRIO) + fprintf(f, "prio %u", wrr->priority); + else + fprintf(f, "prio no-transmit"); + if (show_details) { + char buf[64]; + fprintf(f, "/%u ", wrr->cpriority); + if (wrr->weight != 1) { + print_rate(buf, sizeof(buf), wrr->weight); + fprintf(f, "weight %s ", buf); + } + if (wrr->allot) + fprintf(f, "allot %ub ", wrr->allot); + } + } + if (lss && show_details) { + fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt); + if (lss->maxidle) { + fprintf(f, "maxidle %luus ", tc_core_tick2usec(lss->maxidle>>lss->ewma_log)); + if (show_raw) + fprintf(f, "[%08x] ", lss->maxidle); + } + if (lss->minidle!=0x7fffffff) { + fprintf(f, "minidle %luus ", tc_core_tick2usec(lss->minidle>>lss->ewma_log)); + if (show_raw) + fprintf(f, "[%08x] ", lss->minidle); + } + if (lss->offtime) { + fprintf(f, "offtime %luus ", tc_core_tick2usec(lss->offtime)); + if (show_raw) + fprintf(f, "[%08x] ", lss->offtime); + } + } + if (fopt && show_details) { + char buf[64]; + print_tc_classid(buf, sizeof(buf), fopt->split); + fprintf(f, "\nsplit %s ", buf); + if (fopt->defmap) { + fprintf(f, "defmap %08x", fopt->defmap); + } + } + return 0; +} + +static int cbq_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ + struct tc_cbq_xstats *st; + + if (xstats == NULL) + return 0; + + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + + st = RTA_DATA(xstats); + fprintf(f, " borrowed %u overactions %u avgidle %g undertime %g", st->borrows, + st->overactions, (double)st->avgidle, (double)st->undertime); + return 0; +} + +struct qdisc_util cbq_qdisc_util = { + .id = "cbq", + .parse_qopt = cbq_parse_opt, + .print_qopt = cbq_print_opt, + .print_xstats = cbq_print_xstats, + .parse_copt = cbq_parse_class_opt, + .print_copt = cbq_print_opt, +}; + diff --git a/tc/q_cbq.o b/tc/q_cbq.o new file mode 100644 index 0000000..3f043d0 Binary files /dev/null and b/tc/q_cbq.o differ diff --git a/tc/q_dsmark.c b/tc/q_dsmark.c new file mode 100644 index 0000000..384e749 --- /dev/null +++ b/tc/q_dsmark.c @@ -0,0 +1,179 @@ +/* + * q_dsmark.c Differentiated Services field marking. + * + * Hacked 1998,1999 by Werner Almesberger, EPFL ICA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + + +#define usage() return(-1) + + +static void explain(void) +{ + fprintf(stderr,"Usage: dsmark indices INDICES [ default_index " + "DEFAULT_INDEX ] [ set_tc_index ]\n"); +} + + +static int dsmark_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct rtattr *tail; + __u16 ind; + char *end; + int dflt,set_tc_index; + + ind = set_tc_index = 0; + dflt = -1; + while (argc > 0) { + if (!strcmp(*argv,"indices")) { + NEXT_ARG(); + ind = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"default_index") || !strcmp(*argv, + "default")) { + NEXT_ARG(); + dflt = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"set_tc_index")) { + set_tc_index = 1; + } + else { + explain(); + return -1; + } + argc--; + argv++; + } + if (!ind) { + explain(); + return -1; + } + tail = NLMSG_TAIL(n); + addattr_l(n,1024,TCA_OPTIONS,NULL,0); + addattr_l(n,1024,TCA_DSMARK_INDICES,&ind,sizeof(ind)); + if (dflt != -1) { + __u16 tmp = dflt; + + addattr_l(n,1024,TCA_DSMARK_DEFAULT_INDEX,&tmp,sizeof(tmp)); + } + if (set_tc_index) addattr_l(n,1024,TCA_DSMARK_SET_TC_INDEX,NULL,0); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + +static void explain_class(void) +{ + fprintf(stderr, "Usage: ... dsmark [ mask MASK ] [ value VALUE ]\n"); +} + + +static int dsmark_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct rtattr *tail; + __u8 tmp; + char *end; + + tail = NLMSG_TAIL(n); + addattr_l(n,1024,TCA_OPTIONS,NULL,0); + while (argc > 0) { + if (!strcmp(*argv,"mask")) { + NEXT_ARG(); + tmp = strtoul(*argv,&end,0); + if (*end) { + explain_class(); + return -1; + } + addattr_l(n,1024,TCA_DSMARK_MASK,&tmp,1); + } + else if (!strcmp(*argv,"value")) { + NEXT_ARG(); + tmp = strtoul(*argv,&end,0); + if (*end) { + explain_class(); + return -1; + } + addattr_l(n,1024,TCA_DSMARK_VALUE,&tmp,1); + } + else { + explain_class(); + return -1; + } + argc--; + argv++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + + +static int dsmark_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_DSMARK_MAX+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_DSMARK_MAX, opt); + + if (tb[TCA_DSMARK_MASK]) { + if (!RTA_PAYLOAD(tb[TCA_DSMARK_MASK])) + fprintf(stderr,"dsmark: empty mask\n"); + else fprintf(f,"mask 0x%02x ", + *(__u8 *) RTA_DATA(tb[TCA_DSMARK_MASK])); + } + if (tb[TCA_DSMARK_VALUE]) { + if (!RTA_PAYLOAD(tb[TCA_DSMARK_VALUE])) + fprintf(stderr,"dsmark: empty value\n"); + else fprintf(f,"value 0x%02x ", + *(__u8 *) RTA_DATA(tb[TCA_DSMARK_VALUE])); + } + if (tb[TCA_DSMARK_INDICES]) { + if (RTA_PAYLOAD(tb[TCA_DSMARK_INDICES]) < sizeof(__u16)) + fprintf(stderr,"dsmark: indices too short\n"); + else fprintf(f,"indices 0x%04x ", + *(__u16 *) RTA_DATA(tb[TCA_DSMARK_INDICES])); + } + if (tb[TCA_DSMARK_DEFAULT_INDEX]) { + if (RTA_PAYLOAD(tb[TCA_DSMARK_DEFAULT_INDEX]) < sizeof(__u16)) + fprintf(stderr,"dsmark: default_index too short\n"); + else fprintf(f,"default_index 0x%04x ", + *(__u16 *) RTA_DATA(tb[TCA_DSMARK_DEFAULT_INDEX])); + } + if (tb[TCA_DSMARK_SET_TC_INDEX]) fprintf(f,"set_tc_index "); + return 0; +} + + +struct qdisc_util dsmark_qdisc_util = { + .id = "dsmark", + .parse_qopt = dsmark_parse_opt, + .print_qopt = dsmark_print_opt, + .parse_copt = dsmark_parse_class_opt, + .print_copt = dsmark_print_opt, +}; diff --git a/tc/q_dsmark.o b/tc/q_dsmark.o new file mode 100644 index 0000000..e2b6575 Binary files /dev/null and b/tc/q_dsmark.o differ diff --git a/tc/q_fifo.c b/tc/q_fifo.c new file mode 100644 index 0000000..9f3b3eb --- /dev/null +++ b/tc/q_fifo.c @@ -0,0 +1,98 @@ +/* + * q_fifo.c FIFO. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... [p|b]fifo [ limit NUMBER ]\n"); +} + +#define usage() return(-1) + +static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_fifo_qopt opt; + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_size(&opt.limit, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (ok) + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + return 0; +} + +static int fifo_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct tc_fifo_qopt *qopt; + + if (opt == NULL) + return 0; + + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + if (strcmp(qu->id, "bfifo") == 0) { + SPRINT_BUF(b1); + fprintf(f, "limit %s", sprint_size(qopt->limit, b1)); + } else + fprintf(f, "limit %up", qopt->limit); + return 0; +} + + +struct qdisc_util bfifo_qdisc_util = { + .id = "bfifo", + .parse_qopt = fifo_parse_opt, + .print_qopt = fifo_print_opt, +}; + +struct qdisc_util pfifo_qdisc_util = { + .id = "pfifo", + .parse_qopt = fifo_parse_opt, + .print_qopt = fifo_print_opt, +}; + +extern int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt); +struct qdisc_util pfifo_fast_qdisc_util = { + .id = "pfifo_fast", + .print_qopt = prio_print_opt, +}; diff --git a/tc/q_fifo.o b/tc/q_fifo.o new file mode 100644 index 0000000..bca57fc Binary files /dev/null and b/tc/q_fifo.o differ diff --git a/tc/q_gred.c b/tc/q_gred.c new file mode 100644 index 0000000..0526c75 --- /dev/null +++ b/tc/q_gred.c @@ -0,0 +1,312 @@ +/* + * q_gred.c GRED. + * + * 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. + * + * Authors: J Hadi Salim(hadi@nortelnetworks.com) + * code ruthlessly ripped from + * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +#include "tc_red.h" + + +#if 0 +#define DPRINTF(format,args...) fprintf(stderr,format,##args) +#else +#define DPRINTF(format,args...) +#endif + +static void explain(void) +{ + fprintf(stderr, "Usage: ... gred DP drop-probability limit BYTES " + "min BYTES max BYTES\n"); + fprintf(stderr, " avpkt BYTES burst PACKETS probability PROBABILITY " + "bandwidth KBPS\n"); + fprintf(stderr, " [prio value]\n"); + fprintf(stderr," OR ...\n"); + fprintf(stderr," gred setup DPs <num of DPs> default <default DP> " + "[grio]\n"); +} + +#define usage() return(-1) + +static int init_gred(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + + struct rtattr *tail; + struct tc_gred_sopt opt; + memset(&opt, 0, sizeof(struct tc_gred_sopt)); + + while (argc > 0) { + DPRINTF(stderr,"init_gred: invoked with %s\n",*argv); + if (strcmp(*argv, "DPs") == 0) { + NEXT_ARG(); + DPRINTF(stderr,"init_gred: next_arg with %s\n",*argv); + opt.DPs=strtol(*argv, (char **)NULL, 10); + if (opt.DPs >MAX_DPs) { /* need a better error check */ + fprintf(stderr, "DPs =%u \n",opt.DPs); + fprintf(stderr, "Illegal \"DPs\"\n"); + fprintf(stderr, "GRED: only %d DPs are " + "currently supported\n",MAX_DPs); + return -1; + } + } else if (strcmp(*argv, "default") == 0) { + NEXT_ARG(); + opt.def_DP=strtol(*argv, (char **)NULL, 10); + if (!opt.DPs) { + fprintf(stderr, "\"default DP\" must be " + "defined after DPs\n"); + return -1; + } + if (opt.def_DP>opt.DPs) { +/* + fprintf(stderr, "\"default DP\" must be less than %d\nNote: DP runs from 0 to %d for %d DPs\n",opt.DPs,opt.DPs-1,opt.DPs); +*/ + fprintf(stderr, "\"default DP\" must be less than %d\n",opt.DPs); + return -1; + } + } else if (strcmp(*argv, "grio") == 0) { + opt.grio=1; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; +} + +if ((!opt.DPs) || (!opt.def_DP)) +{ + fprintf(stderr, "Illegal gred setup parameters \n"); + return -1; +} +DPRINTF("TC_GRED: sending DPs=%d default=%d\n",opt.DPs,opt.def_DP); + n->nlmsg_flags|=NLM_F_CREATE; + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; +return 0; +} +/* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +*/ +static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_gred_qopt opt; + unsigned burst = 0; + unsigned avpkt = 0; + double probability = 0.02; + unsigned rate = 0; + int wlog; + __u8 sbuf[256]; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_size(&opt.limit, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "setup") == 0) { + if (ok) { + fprintf(stderr, "Illegal \"setup\"\n"); + return -1; + } + return init_gred(qu,argc-1, argv+1,n); + + } else if (strcmp(*argv, "min") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_min, *argv)) { + fprintf(stderr, "Illegal \"min\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "max") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_max, *argv)) { + fprintf(stderr, "Illegal \"max\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "DP") == 0) { + NEXT_ARG(); + opt.DP=strtol(*argv, (char **)NULL, 10); + DPRINTF ("\n ******* DP =%u\n",opt.DP); + if (opt.DP >MAX_DPs) { /* need a better error check */ + fprintf(stderr, "DP =%u \n",opt.DP); + fprintf(stderr, "Illegal \"DP\"\n"); + fprintf(stderr, "GRED: only %d DPs are currently supported\n",MAX_DPs); + return -1; + } + ok++; + } else if (strcmp(*argv, "burst") == 0) { + NEXT_ARG(); + if (get_unsigned(&burst, *argv, 0)) { + fprintf(stderr, "Illegal \"burst\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&avpkt, *argv)) { + fprintf(stderr, "Illegal \"avpkt\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "probability") == 0) { + NEXT_ARG(); + if (sscanf(*argv, "%lg", &probability) != 1) { + fprintf(stderr, "Illegal \"probability\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "prio") == 0) { + NEXT_ARG(); + opt.prio=strtol(*argv, (char **)NULL, 10); + /* some error check here */ + ok++; + } else if (strcmp(*argv, "bandwidth") == 0) { + NEXT_ARG(); + if (get_rate(&rate, *argv)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!ok) + return 0; + + if (rate == 0) + get_rate(&rate, "10Mbit"); + + if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt || + (opt.DP<0)) { + fprintf(stderr, "Required parameter (min, max, burst, limit, " + "avpket, DP) is missing\n"); + return -1; + } + + if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { + fprintf(stderr, "GRED: failed to calculate EWMA constant.\n"); + return -1; + } + if (wlog >= 10) + fprintf(stderr, "GRED: WARNING. Burst %d seems to be to " + "large.\n", burst); + opt.Wlog = wlog; + if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { + fprintf(stderr, "GRED: failed to calculate probability.\n"); + return -1; + } + opt.Plog = wlog; + if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) + { + fprintf(stderr, "GRED: failed to calculate idle damping " + "table.\n"); + return -1; + } + opt.Scell_log = wlog; + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt)); + addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_GRED_STAB+1]; + struct tc_gred_qopt *qopt; + int i; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + SPRINT_BUF(b3); + SPRINT_BUF(b4); + SPRINT_BUF(b5); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_GRED_STAB, opt); + + if (tb[TCA_GRED_PARMS] == NULL) + return -1; + + qopt = RTA_DATA(tb[TCA_GRED_PARMS]); + if (RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { + fprintf(f,"\n GRED received message smaller than expected\n"); + return -1; + } + +/* Bad hack! should really return a proper message as shown above*/ + + for (i=0;i<MAX_DPs;i++, qopt++) { + if (qopt->DP >= MAX_DPs) continue; + fprintf(f, "\n DP:%d (prio %d) Average Queue %s Measured " + "Queue %s ", + qopt->DP, + qopt->prio, + sprint_size(qopt->qave, b4), + sprint_size(qopt->backlog, b5)); + fprintf(f, "\n\t Packet drops: %d (forced %d early %d) ", + qopt->forced+qopt->early, + qopt->forced, + qopt->early); + fprintf(f, "\n\t Packet totals: %u (bytes %u) ", + qopt->packets, + qopt->bytesin); + if (show_details) + fprintf(f, "\n limit %s min %s max %s ", + sprint_size(qopt->limit, b1), + sprint_size(qopt->qth_min, b2), + sprint_size(qopt->qth_max, b3)); + fprintf(f, "ewma %u Plog %u Scell_log %u", + qopt->Wlog, qopt->Plog, qopt->Scell_log); + } + return 0; +} + +struct qdisc_util gred_qdisc_util = { + .id = "gred", + .parse_qopt = gred_parse_opt, + .print_qopt = gred_print_opt, +}; diff --git a/tc/q_gred.o b/tc/q_gred.o new file mode 100644 index 0000000..cb44aad Binary files /dev/null and b/tc/q_gred.o differ diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c new file mode 100644 index 0000000..f09c606 --- /dev/null +++ b/tc/q_hfsc.c @@ -0,0 +1,406 @@ +/* + * q_hfsc.c HFSC. + * + * 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. + * + * Authors: Patrick McHardy, <kaber@trash.net> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <math.h> + +#include "utils.h" +#include "tc_util.h" + +static int hfsc_get_sc(int *, char ***, struct tc_service_curve *); + + +static void +explain_qdisc(void) +{ + fprintf(stderr, + "Usage: ... hfsc [ default CLASSID ]\n" + "\n" + " default: default class for unclassified packets\n" + ); +} + +static void +explain_class(void) +{ + fprintf(stderr, + "Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n" + "\n" + "SC := [ [ m1 BPS ] [ d SEC ] m2 BPS\n" + "\n" + " m1 : slope of first segment\n" + " d : x-coordinate of intersection\n" + " m2 : slope of second segment\n" + "\n" + "Alternative format:\n" + "\n" + "SC := [ [ umax BYTE ] dmax SEC ] rate BPS\n" + "\n" + " umax : maximum unit of work\n" + " dmax : maximum delay\n" + " rate : rate\n" + "\n" + ); +} + +static void +explain1(char *arg) +{ + fprintf(stderr, "HFSC: Illegal \"%s\"\n", arg); +} + +static int +hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_hfsc_qopt qopt; + + memset(&qopt, 0, sizeof(qopt)); + + while (argc > 0) { + if (matches(*argv, "default") == 0) { + NEXT_ARG(); + if (qopt.defcls != 0) { + fprintf(stderr, "HFSC: Double \"default\"\n"); + return -1; + } + if (get_u16(&qopt.defcls, *argv, 16) < 0) { + explain1("default"); + return -1; + } + } else if (matches(*argv, "help") == 0) { + explain_qdisc(); + return -1; + } else { + fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv); + explain_qdisc(); + return -1; + } + argc--, argv++; + } + + addattr_l(n, 1024, TCA_OPTIONS, &qopt, sizeof(qopt)); + return 0; +} + +static int +hfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct tc_hfsc_qopt *qopt; + + if (opt == NULL) + return 0; + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + + if (qopt->defcls != 0) + fprintf(f, "default %x ", qopt->defcls); + + return 0; +} + +static int +hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ + struct tc_hfsc_stats *st; + + if (xstats == NULL) + return 0; + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + st = RTA_DATA(xstats); + + fprintf(f, " period %u ", st->period); + if (st->work != 0) + fprintf(f, "work %llu bytes ", (unsigned long long) st->work); + if (st->rtwork != 0) + fprintf(f, "rtwork %llu bytes ", (unsigned long long) st->rtwork); + fprintf(f, "level %u ", st->level); + fprintf(f, "\n"); + + return 0; +} + +static int +hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct tc_service_curve rsc, fsc, usc; + int rsc_ok, fsc_ok, usc_ok; + struct rtattr *tail; + + memset(&rsc, 0, sizeof(rsc)); + memset(&fsc, 0, sizeof(fsc)); + memset(&usc, 0, sizeof(usc)); + rsc_ok = fsc_ok = usc_ok = 0; + + while (argc > 0) { + if (matches(*argv, "rt") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { + explain1("rt"); + return -1; + } + rsc_ok = 1; + } else if (matches(*argv, "ls") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &fsc) < 0) { + explain1("ls"); + return -1; + } + fsc_ok = 1; + } else if (matches(*argv, "sc") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { + explain1("sc"); + return -1; + } + memcpy(&fsc, &rsc, sizeof(fsc)); + rsc_ok = 1; + fsc_ok = 1; + } else if (matches(*argv, "ul") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &usc) < 0) { + explain1("ul"); + return -1; + } + usc_ok = 1; + } else if (matches(*argv, "help") == 0) { + explain_class(); + return -1; + } else { + fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv); + explain_class(); + return -1; + } + argc--, argv++; + } + + if (!(rsc_ok || fsc_ok || usc_ok)) { + fprintf(stderr, "HFSC: no parameters given\n"); + explain_class(); + return -1; + } + if (usc_ok && !fsc_ok) { + fprintf(stderr, "HFSC: Upper-limit Service Curve without " + "Link-Share Service Curve\n"); + explain_class(); + return -1; + } + + tail = NLMSG_TAIL(n); + + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + if (rsc_ok) + addattr_l(n, 1024, TCA_HFSC_RSC, &rsc, sizeof(rsc)); + if (fsc_ok) + addattr_l(n, 1024, TCA_HFSC_FSC, &fsc, sizeof(fsc)); + if (usc_ok) + addattr_l(n, 1024, TCA_HFSC_USC, &usc, sizeof(usc)); + + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static void +hfsc_print_sc(FILE *f, char *name, struct tc_service_curve *sc) +{ + SPRINT_BUF(b1); + + fprintf(f, "%s ", name); + fprintf(f, "m1 %s ", sprint_rate(sc->m1, b1)); + fprintf(f, "d %s ", sprint_usecs(sc->d, b1)); + fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1)); +} + +static int +hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_HFSC_MAX+1]; + struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_HFSC_MAX, opt); + + if (tb[TCA_HFSC_RSC]) { + if (RTA_PAYLOAD(tb[TCA_HFSC_RSC]) < sizeof(*rsc)) + fprintf(stderr, "HFSC: truncated realtime option\n"); + else + rsc = RTA_DATA(tb[TCA_HFSC_RSC]); + } + if (tb[TCA_HFSC_FSC]) { + if (RTA_PAYLOAD(tb[TCA_HFSC_FSC]) < sizeof(*fsc)) + fprintf(stderr, "HFSC: truncated linkshare option\n"); + else + fsc = RTA_DATA(tb[TCA_HFSC_FSC]); + } + if (tb[TCA_HFSC_USC]) { + if (RTA_PAYLOAD(tb[TCA_HFSC_USC]) < sizeof(*usc)) + fprintf(stderr, "HFSC: truncated upperlimit option\n"); + else + usc = RTA_DATA(tb[TCA_HFSC_USC]); + } + + + if (rsc != NULL && fsc != NULL && + memcmp(rsc, fsc, sizeof(*rsc)) == 0) + hfsc_print_sc(f, "sc", rsc); + else { + if (rsc != NULL) + hfsc_print_sc(f, "rt", rsc); + if (fsc != NULL) + hfsc_print_sc(f, "ls", fsc); + } + if (usc != NULL) + hfsc_print_sc(f, "ul", usc); + + return 0; +} + +struct qdisc_util hfsc_qdisc_util = { + .id = "hfsc", + .parse_qopt = hfsc_parse_opt, + .print_qopt = hfsc_print_opt, + .print_xstats = hfsc_print_xstats, + .parse_copt = hfsc_parse_class_opt, + .print_copt = hfsc_print_class_opt, +}; + +static int +hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc) +{ + char **argv = *argvp; + int argc = *argcp; + unsigned int m1 = 0, d = 0, m2 = 0; + + if (matches(*argv, "m1") == 0) { + NEXT_ARG(); + if (get_rate(&m1, *argv) < 0) { + explain1("m1"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "d") == 0) { + NEXT_ARG(); + if (get_usecs(&d, *argv) < 0) { + explain1("d"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "m2") == 0) { + NEXT_ARG(); + if (get_rate(&m2, *argv) < 0) { + explain1("m2"); + return -1; + } + } else + return -1; + + sc->m1 = m1; + sc->d = d; + sc->m2 = m2; + + *argvp = argv; + *argcp = argc; + return 0; +} + +static int +hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc) +{ + char **argv = *argvp; + int argc = *argcp; + unsigned int umax = 0, dmax = 0, rate = 0; + + if (matches(*argv, "umax") == 0) { + NEXT_ARG(); + if (get_size(&umax, *argv) < 0) { + explain1("umax"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "dmax") == 0) { + NEXT_ARG(); + if (get_usecs(&dmax, *argv) < 0) { + explain1("dmax"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "rate") == 0) { + NEXT_ARG(); + if (get_rate(&rate, *argv) < 0) { + explain1("rate"); + return -1; + } + } else + return -1; + + if (umax != 0 && dmax == 0) { + fprintf(stderr, "HFSC: umax given but dmax is zero.\n"); + return -1; + } + + if (dmax != 0 && ceil(umax * 1000000.0 / dmax) > rate) { + /* + * concave curve, slope of first segment is umax/dmax, + * intersection is at dmax + */ + sc->m1 = ceil(umax * 1000000.0 / dmax); /* in bps */ + sc->d = dmax; + sc->m2 = rate; + } else { + /* + * convex curve, slope of first segment is 0, intersection + * is at dmax - umax / rate + */ + sc->m1 = 0; + sc->d = ceil(dmax - umax * 1000000.0 / rate); /* in usec */ + sc->m2 = rate; + } + + *argvp = argv; + *argcp = argc; + return 0; +} + +static int +hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc) +{ + if (hfsc_get_sc1(argcp, argvp, sc) < 0 && + hfsc_get_sc2(argcp, argvp, sc) < 0) + return -1; + + if (sc->m1 == 0 && sc->m2 == 0) { + fprintf(stderr, "HFSC: Service Curve has two zero slopes\n"); + return -1; + } + + return 0; +} diff --git a/tc/q_hfsc.o b/tc/q_hfsc.o new file mode 100644 index 0000000..7f8a979 Binary files /dev/null and b/tc/q_hfsc.o differ diff --git a/tc/q_htb.c b/tc/q_htb.c new file mode 100644 index 0000000..828d4b1 --- /dev/null +++ b/tc/q_htb.c @@ -0,0 +1,330 @@ +/* + * q_htb.c HTB. + * + * 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. + * + * Authors: Martin Devera, devik@cdi.cz + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +#define HTB_TC_VER 0x30003 +#if HTB_TC_VER >> 16 != TC_HTB_PROTOVER +#error "Different kernel and TC HTB versions" +#endif + +static void explain(void) +{ + fprintf(stderr, "Usage: ... qdisc add ... htb [default N] [r2q N]\n" + " default minor id of class to which unclassified packets are sent {0}\n" + " r2q DRR quantums are computed as rate in Bps/r2q {10}\n" + " debug string of 16 numbers each 0-3 {0}\n\n" + "... class add ... htb rate R1 [burst B1] [mpu B] [overhead O]\n" + " [prio P] [slot S] [pslot PS]\n" + " [ceil R2] [cburst B2] [mtu MTU] [quantum Q]\n" + " rate rate allocated to this class (class can still borrow)\n" + " burst max bytes burst which can be accumulated during idle period {computed}\n" + " mpu minimum packet size used in rate computations\n" + " overhead per-packet size overhead used in rate computations\n" + + " ceil definite upper class rate (no borrows) {rate}\n" + " cburst burst but for ceil {computed}\n" + " mtu max packet size we create rate map for {1600}\n" + " prio priority of leaf; lower are served first {0}\n" + " quantum how much bytes to serve from leaf at once {use r2q}\n" + "\nTC HTB version %d.%d\n",HTB_TC_VER>>16,HTB_TC_VER&0xffff + ); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); + explain(); +} + + +#define usage() return(-1) + +static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_htb_glob opt; + struct rtattr *tail; + unsigned i; char *p; + memset(&opt,0,sizeof(opt)); + opt.rate2quantum = 10; + opt.version = 3; + + while (argc > 0) { + if (matches(*argv, "r2q") == 0) { + NEXT_ARG(); + if (get_u32(&opt.rate2quantum, *argv, 10)) { + explain1("r2q"); return -1; + } + } else if (matches(*argv, "default") == 0) { + NEXT_ARG(); + if (get_u32(&opt.defcls, *argv, 16)) { + explain1("default"); return -1; + } + } else if (matches(*argv, "debug") == 0) { + NEXT_ARG(); p = *argv; + for (i=0; i<16; i++,p++) { + if (*p<'0' || *p>'3') break; + opt.debug |= (*p-'0')<<(2*i); + } + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 2024, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt))); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_htb_opt opt; + __u32 rtab[256],ctab[256]; + unsigned buffer=0,cbuffer=0; + int cell_log=-1,ccell_log = -1; + unsigned mtu, mpu; + unsigned char mpu8 = 0, overhead = 0; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); mtu = 1600; /* eth packet len */ + + while (argc > 0) { + if (matches(*argv, "prio") == 0) { + NEXT_ARG(); + if (get_u32(&opt.prio, *argv, 10)) { + explain1("prio"); return -1; + } + ok++; + } else if (matches(*argv, "mtu") == 0) { + NEXT_ARG(); + if (get_u32(&mtu, *argv, 10)) { + explain1("mtu"); return -1; + } + } else if (matches(*argv, "mpu") == 0) { + NEXT_ARG(); + if (get_u8(&mpu8, *argv, 10)) { + explain1("mpu"); return -1; + } + } else if (matches(*argv, "overhead") == 0) { + NEXT_ARG(); + if (get_u8(&overhead, *argv, 10)) { + explain1("overhead"); return -1; + } + } else if (matches(*argv, "quantum") == 0) { + NEXT_ARG(); + if (get_u32(&opt.quantum, *argv, 10)) { + explain1("quantum"); return -1; + } + } else if (matches(*argv, "burst") == 0 || + strcmp(*argv, "buffer") == 0 || + strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (get_size_and_cell(&buffer, &cell_log, *argv) < 0) { + explain1("buffer"); + return -1; + } + ok++; + } else if (matches(*argv, "cburst") == 0 || + strcmp(*argv, "cbuffer") == 0 || + strcmp(*argv, "cmaxburst") == 0) { + NEXT_ARG(); + if (get_size_and_cell(&cbuffer, &ccell_log, *argv) < 0) { + explain1("cbuffer"); + return -1; + } + ok++; + } else if (strcmp(*argv, "ceil") == 0) { + NEXT_ARG(); + if (opt.ceil.rate) { + fprintf(stderr, "Double \"ceil\" spec\n"); + return -1; + } + if (get_rate(&opt.ceil.rate, *argv)) { + explain1("ceil"); + return -1; + } + ok++; + } else if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (opt.rate.rate) { + fprintf(stderr, "Double \"rate\" spec\n"); + return -1; + } + if (get_rate(&opt.rate.rate, *argv)) { + explain1("rate"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + +/* if (!ok) + return 0;*/ + + if (opt.rate.rate == 0) { + fprintf(stderr, "\"rate\" is required.\n"); + return -1; + } + /* if ceil params are missing, use the same as rate */ + if (!opt.ceil.rate) opt.ceil = opt.rate; + + /* compute minimal allowed burst from rate; mtu is added here to make + sute that buffer is larger than mtu and to have some safeguard space */ + if (!buffer) buffer = opt.rate.rate / get_hz() + mtu; + if (!cbuffer) cbuffer = opt.ceil.rate / get_hz() + mtu; + +/* encode overhead and mpu, 8 bits each, into lower 16 bits */ + mpu = (unsigned)mpu8 | (unsigned)overhead << 8; + opt.ceil.mpu = mpu; opt.rate.mpu = mpu; + + if ((cell_log = tc_calc_rtable(opt.rate.rate, rtab, cell_log, mtu, mpu)) < 0) { + fprintf(stderr, "htb: failed to calculate rate table.\n"); + return -1; + } + opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer); + opt.rate.cell_log = cell_log; + + if ((ccell_log = tc_calc_rtable(opt.ceil.rate, ctab, cell_log, mtu, mpu)) < 0) { + fprintf(stderr, "htb: failed to calculate ceil rate table.\n"); + return -1; + } + opt.cbuffer = tc_calc_xmittime(opt.ceil.rate, cbuffer); + opt.ceil.cell_log = ccell_log; + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 2024, TCA_HTB_PARMS, &opt, sizeof(opt)); + addattr_l(n, 3024, TCA_HTB_RTAB, rtab, 1024); + addattr_l(n, 4024, TCA_HTB_CTAB, ctab, 1024); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_HTB_RTAB+1]; + struct tc_htb_opt *hopt; + struct tc_htb_glob *gopt; + double buffer,cbuffer; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + SPRINT_BUF(b3); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_HTB_RTAB, opt); + + if (tb[TCA_HTB_PARMS]) { + + hopt = RTA_DATA(tb[TCA_HTB_PARMS]); + if (RTA_PAYLOAD(tb[TCA_HTB_PARMS]) < sizeof(*hopt)) return -1; + + if (!hopt->level) { + fprintf(f, "prio %d ", (int)hopt->prio); + if (show_details) + fprintf(f, "quantum %d ", (int)hopt->quantum); + } + fprintf(f, "rate %s ", sprint_rate(hopt->rate.rate, b1)); + buffer = ((double)hopt->rate.rate*tc_core_tick2usec(hopt->buffer))/1000000; + fprintf(f, "ceil %s ", sprint_rate(hopt->ceil.rate, b1)); + cbuffer = ((double)hopt->ceil.rate*tc_core_tick2usec(hopt->cbuffer))/1000000; + if (show_details) { + fprintf(f, "burst %s/%u mpu %s overhead %s ", + sprint_size(buffer, b1), + 1<<hopt->rate.cell_log, + sprint_size(hopt->rate.mpu&0xFF, b2), + sprint_size((hopt->rate.mpu>>8)&0xFF, b3)); + fprintf(f, "cburst %s/%u mpu %s overhead %s ", + sprint_size(cbuffer, b1), + 1<<hopt->ceil.cell_log, + sprint_size(hopt->ceil.mpu&0xFF, b2), + sprint_size((hopt->ceil.mpu>>8)&0xFF, b3)); + fprintf(f, "level %d ", (int)hopt->level); + } else { + fprintf(f, "burst %s ", sprint_size(buffer, b1)); + fprintf(f, "cburst %s ", sprint_size(cbuffer, b1)); + } + if (show_raw) + fprintf(f, "buffer [%08x] cbuffer [%08x] ", + hopt->buffer,hopt->cbuffer); + } + if (tb[TCA_HTB_INIT]) { + gopt = RTA_DATA(tb[TCA_HTB_INIT]); + if (RTA_PAYLOAD(tb[TCA_HTB_INIT]) < sizeof(*gopt)) return -1; + + fprintf(f, "r2q %d default %x direct_packets_stat %u", + gopt->rate2quantum,gopt->defcls,gopt->direct_pkts); + if (show_details) + fprintf(f," ver %d.%d",gopt->version >> 16,gopt->version & 0xffff); + } + return 0; +} + +static int htb_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ + struct tc_htb_xstats *st; + if (xstats == NULL) + return 0; + + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + + st = RTA_DATA(xstats); + fprintf(f, " lended: %u borrowed: %u giants: %u\n", + st->lends,st->borrows,st->giants); + fprintf(f, " tokens: %d ctokens: %d\n", st->tokens,st->ctokens); + return 0; +} + +struct qdisc_util htb_qdisc_util = { + .id = "htb", + .parse_qopt = htb_parse_opt, + .print_qopt = htb_print_opt, + .print_xstats = htb_print_xstats, + .parse_copt = htb_parse_class_opt, + .print_copt = htb_print_opt, +}; + +/* for testing of old one */ +struct qdisc_util htb2_qdisc_util = { + .id = "htb2", + .parse_qopt = htb_parse_opt, + .print_qopt = htb_print_opt, + .print_xstats = htb_print_xstats, + .parse_copt = htb_parse_class_opt, + .print_copt = htb_print_opt, +}; diff --git a/tc/q_htb.o b/tc/q_htb.o new file mode 100644 index 0000000..e00de5b Binary files /dev/null and b/tc/q_htb.o differ diff --git a/tc/q_ingress.c b/tc/q_ingress.c new file mode 100644 index 0000000..71fbd49 --- /dev/null +++ b/tc/q_ingress.c @@ -0,0 +1,69 @@ +/* + * + * q_ingress.c INGRESS. + * + * 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. + * + * Authors: J Hadi Salim + * + * This is here just in case it is needed + * useless right now; might be useful in the future + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... ingress \n"); +} + +#define usage() return(-1) + +static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + + if (argc > 0) { + while (argc > 0) { + + if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + argc--; argv++; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + } + } + + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + return 0; +} + +static int ingress_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + + fprintf(f, "---------------- "); + return 0; +} + +struct qdisc_util ingress_qdisc_util = { + .id = "ingress", + .parse_qopt = ingress_parse_opt, + .print_qopt = ingress_print_opt, +}; diff --git a/tc/q_ingress.o b/tc/q_ingress.o new file mode 100644 index 0000000..5fb4d37 Binary files /dev/null and b/tc/q_ingress.o differ diff --git a/tc/q_netem.c b/tc/q_netem.c new file mode 100644 index 0000000..f696cc3 --- /dev/null +++ b/tc/q_netem.c @@ -0,0 +1,293 @@ +/* + * q_netem.c NETEM. + * + * 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. + * + * Authors: Stephen Hemminger <shemminger@osdl.org> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <errno.h> + +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static void explain(void) +{ + fprintf(stderr, +"Usage: ... netem [ limit PACKETS ] \n" \ +" [ delay TIME [ JITTER [CORRELATION]]]\n" \ +" [ drop PERCENT [CORRELATION]] \n" \ +" [ duplicate PERCENT [CORRELATION]]\n" \ +" [ distribution {uniform|normal|pareto|paretonormal} ]\n" \ +" [ gap PACKETS ]\n"); +} + +static void explain1(const char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + +#define usage() return(-1) + +/* + * Simplistic file parser for distrbution data. + * Format is: + * # comment line(s) + * data0 data1 + */ +#define MAXDIST 65536 +static int get_distribution(const char *type, __s16 *data) +{ + FILE *f; + int n; + long x; + size_t len; + char *line = NULL; + char name[128]; + + snprintf(name, sizeof(name), "/usr/lib/tc/%s.dist", type); + if ((f = fopen(name, "r")) == NULL) { + fprintf(stderr, "No distribution data for %s (%s: %s)\n", + type, name, strerror(errno)); + return -1; + } + + n = 0; + while (getline(&line, &len, f) != -1) { + char *p, *endp; + if (*line == '\n' || *line == '#') + continue; + + for (p = line; ; p = endp) { + x = strtol(p, &endp, 0); + if (endp == p) + break; + + if (n >= MAXDIST) { + fprintf(stderr, "%s: too much data\n", + name); + n = -1; + goto error; + } + data[n++] = x; + } + } + error: + free(line); + fclose(f); + return n; +} + +static int isnumber(const char *arg) +{ + char *p; + (void) strtod(arg, &p); + return (p != arg); +} + +#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isnumber(argv[1])) + +/* Adjust for the fact that psched_ticks aren't always usecs + (based on kernel PSCHED_CLOCK configuration */ +static int get_ticks(__u32 *ticks, const char *str) +{ + unsigned t; + + if(get_usecs(&t, str)) + return -1; + + *ticks = tc_core_usec2tick(t); + return 0; +} + +static char *sprint_ticks(__u32 ticks, char *buf) +{ + return sprint_usecs(tc_core_tick2usec(ticks), buf); +} + + +static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + size_t dist_size = 0; + struct rtattr *tail; + struct tc_netem_qopt opt; + struct tc_netem_corr cor; + __s16 dist_data[MAXDIST]; + + memset(&opt, 0, sizeof(opt)); + opt.limit = 1000; + memset(&cor, 0, sizeof(cor)); + + while (argc > 0) { + if (matches(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_size(&opt.limit, *argv)) { + explain1("limit"); + return -1; + } + } else if (matches(*argv, "latency") == 0 || + matches(*argv, "delay") == 0) { + NEXT_ARG(); + if (get_ticks(&opt.latency, *argv)) { + explain1("latency"); + return -1; + } + + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_ticks(&opt.jitter, *argv)) { + explain1("latency"); + return -1; + } + + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_percent(&cor.delay_corr, + *argv)) { + explain1("latency"); + return -1; + } + } + } + } else if (matches(*argv, "loss") == 0 || + matches(*argv, "drop") == 0) { + NEXT_ARG(); + if (get_percent(&opt.loss, *argv)) { + explain1("loss"); + return -1; + } + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_percent(&cor.loss_corr, *argv)) { + explain1("loss"); + return -1; + } + } + } else if (matches(*argv, "gap") == 0) { + NEXT_ARG(); + if (get_u32(&opt.gap, *argv, 0)) { + explain1("gap"); + return -1; + } + } else if (matches(*argv, "duplicate") == 0) { + NEXT_ARG(); + if (get_percent(&opt.duplicate, *argv)) { + explain1("duplicate"); + return -1; + } + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_percent(&cor.dup_corr, *argv)) { + explain1("duplicate"); + return -1; + } + } + } else if (matches(*argv, "distribution") == 0) { + NEXT_ARG(); + dist_size = get_distribution(*argv, dist_data); + if (dist_size < 0) + return -1; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + tail = NLMSG_TAIL(n); + + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)); + + if (dist_size > 0) { + addattr_l(n, 32768, TCA_NETEM_DELAY_DIST, + dist_data, dist_size*sizeof(dist_data[0])); + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + const struct tc_netem_corr *cor = NULL; + struct tc_netem_qopt qopt; + int len = RTA_PAYLOAD(opt) - sizeof(qopt); + SPRINT_BUF(b1); + + if (opt == NULL) + return 0; + + if (len < 0) { + fprintf(stderr, "options size error\n"); + return -1; + } + memcpy(&qopt, RTA_DATA(opt), sizeof(qopt)); + + if (len > 0) { + struct rtattr *tb[TCA_NETEM_MAX+1]; + parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt), + len); + + if (tb[TCA_NETEM_CORR]) { + if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor)) + return -1; + cor = RTA_DATA(tb[TCA_NETEM_CORR]); + } + } + + fprintf(f, "limit %d", qopt.limit); + + if (qopt.latency) { + fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1)); + + if (qopt.jitter) { + fprintf(f, " %s", sprint_ticks(qopt.jitter, b1)); + if (cor && cor->delay_corr) + fprintf(f, " %s", sprint_percent(cor->delay_corr, b1)); + } + } + + if (qopt.loss) { + fprintf(f, " loss %s", sprint_percent(qopt.loss, b1)); + if (cor && cor->loss_corr) + fprintf(f, " %s", sprint_percent(cor->loss_corr, b1)); + } + + if (qopt.duplicate) { + fprintf(f, " duplicate %s", + sprint_percent(qopt.duplicate, b1)); + if (cor && cor->dup_corr) + fprintf(f, " %s", sprint_percent(cor->dup_corr, b1)); + } + + if (qopt.gap) + fprintf(f, " gap %lu", (unsigned long)qopt.gap); + + return 0; +} + +struct qdisc_util netem_qdisc_util = { + .id = "netem", + .parse_qopt = netem_parse_opt, + .print_qopt = netem_print_opt, +}; + diff --git a/tc/q_netem.so b/tc/q_netem.so new file mode 100755 index 0000000..b312dea Binary files /dev/null and b/tc/q_netem.so differ diff --git a/tc/q_prio.c b/tc/q_prio.c new file mode 100644 index 0000000..d696e1b --- /dev/null +++ b/tc/q_prio.c @@ -0,0 +1,119 @@ +/* + * q_prio.c PRIO. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Changes: + * + * Ole Husgaard <sparre@login.dknet.dk>: 990513: prio2band map was always reset. + * J Hadi Salim <hadi@cyberus.ca>: 990609: priomap fix. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... prio bands NUMBER priomap P1 P2...\n"); +} + +#define usage() return(-1) + +static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + int pmap_mode = 0; + int idx = 0; + struct tc_prio_qopt opt={3,{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }}; + + while (argc > 0) { + if (strcmp(*argv, "bands") == 0) { + if (pmap_mode) + explain(); + NEXT_ARG(); + if (get_integer(&opt.bands, *argv, 10)) { + fprintf(stderr, "Illegal \"bands\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "priomap") == 0) { + if (pmap_mode) { + fprintf(stderr, "Error: duplicate priomap\n"); + return -1; + } + pmap_mode = 1; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + unsigned band; + if (!pmap_mode) { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + if (get_unsigned(&band, *argv, 10)) { + fprintf(stderr, "Illegal \"priomap\" element\n"); + return -1; + } + if (band > opt.bands) { + fprintf(stderr, "\"priomap\" element is out of bands\n"); + return -1; + } + if (idx > TC_PRIO_MAX) { + fprintf(stderr, "\"priomap\" index > TC_PRIO_MAX=%u\n", TC_PRIO_MAX); + return -1; + } + opt.priomap[idx++] = band; + } + argc--; argv++; + } + +/* + if (pmap_mode) { + for (; idx < TC_PRIO_MAX; idx++) + opt.priomap[idx] = opt.priomap[TC_PRIO_BESTEFFORT]; + } +*/ + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + return 0; +} + +int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + int i; + struct tc_prio_qopt *qopt; + + if (opt == NULL) + return 0; + + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + fprintf(f, "bands %u priomap ", qopt->bands); + for (i=0; i<=TC_PRIO_MAX; i++) + fprintf(f, " %d", qopt->priomap[i]); + return 0; +} + +struct qdisc_util prio_qdisc_util = { + .id = "prio", + .parse_qopt = prio_parse_opt, + .print_qopt = prio_print_opt, +}; + diff --git a/tc/q_prio.o b/tc/q_prio.o new file mode 100644 index 0000000..addc84d Binary files /dev/null and b/tc/q_prio.o differ diff --git a/tc/q_red.c b/tc/q_red.c new file mode 100644 index 0000000..1743f6c --- /dev/null +++ b/tc/q_red.c @@ -0,0 +1,219 @@ +/* + * q_red.c RED. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +#include "tc_red.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... red limit BYTES min BYTES max BYTES avpkt BYTES burst PACKETS\n"); + fprintf(stderr, " probability PROBABILITY bandwidth KBPS [ ecn ]\n"); +} + +#define usage() return(-1) + +static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_red_qopt opt; + unsigned burst = 0; + unsigned avpkt = 0; + double probability = 0.02; + unsigned rate = 0; + int ecn_ok = 0; + int wlog; + __u8 sbuf[256]; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_size(&opt.limit, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "min") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_min, *argv)) { + fprintf(stderr, "Illegal \"min\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "max") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_max, *argv)) { + fprintf(stderr, "Illegal \"max\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "burst") == 0) { + NEXT_ARG(); + if (get_unsigned(&burst, *argv, 0)) { + fprintf(stderr, "Illegal \"burst\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&avpkt, *argv)) { + fprintf(stderr, "Illegal \"avpkt\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "probability") == 0) { + NEXT_ARG(); + if (sscanf(*argv, "%lg", &probability) != 1) { + fprintf(stderr, "Illegal \"probability\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "bandwidth") == 0) { + NEXT_ARG(); + if (get_rate(&rate, *argv)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "ecn") == 0) { + ecn_ok = 1; + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!ok) + return 0; + + if (rate == 0) + get_rate(&rate, "10Mbit"); + + if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt) { + fprintf(stderr, "Required parameter (min, max, burst, limit, avpket) is missing\n"); + return -1; + } + + if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { + fprintf(stderr, "RED: failed to calculate EWMA constant.\n"); + return -1; + } + if (wlog >= 10) + fprintf(stderr, "RED: WARNING. Burst %d seems to be to large.\n", burst); + opt.Wlog = wlog; + if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { + fprintf(stderr, "RED: failed to calculate probability.\n"); + return -1; + } + opt.Plog = wlog; + if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) { + fprintf(stderr, "RED: failed to calculate idle damping table.\n"); + return -1; + } + opt.Scell_log = wlog; + if (ecn_ok) { +#ifdef TC_RED_ECN + opt.flags |= TC_RED_ECN; +#else + fprintf(stderr, "RED: ECN support is missing in this binary.\n"); + return -1; +#endif + } + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_RED_PARMS, &opt, sizeof(opt)); + addattr_l(n, 1024, TCA_RED_STAB, sbuf, 256); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_RED_STAB+1]; + struct tc_red_qopt *qopt; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + SPRINT_BUF(b3); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_RED_STAB, opt); + + if (tb[TCA_RED_PARMS] == NULL) + return -1; + qopt = RTA_DATA(tb[TCA_RED_PARMS]); + if (RTA_PAYLOAD(tb[TCA_RED_PARMS]) < sizeof(*qopt)) + return -1; + fprintf(f, "limit %s min %s max %s ", + sprint_size(qopt->limit, b1), + sprint_size(qopt->qth_min, b2), + sprint_size(qopt->qth_max, b3)); +#ifdef TC_RED_ECN + if (qopt->flags & TC_RED_ECN) + fprintf(f, "ecn "); +#endif + if (show_details) { + fprintf(f, "ewma %u Plog %u Scell_log %u", + qopt->Wlog, qopt->Plog, qopt->Scell_log); + } + return 0; +} + +static int red_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ +#ifdef TC_RED_ECN + struct tc_red_xstats *st; + + if (xstats == NULL) + return 0; + + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + + st = RTA_DATA(xstats); + fprintf(f, " marked %u early %u pdrop %u other %u", + st->marked, st->early, st->pdrop, st->other); + return 0; + +#endif + return 0; +} + + +struct qdisc_util red_qdisc_util = { + .id = "red", + .parse_qopt = red_parse_opt, + .print_qopt = red_print_opt, + .print_xstats = red_print_xstats, +}; diff --git a/tc/q_red.o b/tc/q_red.o new file mode 100644 index 0000000..a53d29a Binary files /dev/null and b/tc/q_red.o differ diff --git a/tc/q_sfq.c b/tc/q_sfq.c new file mode 100644 index 0000000..05385cf --- /dev/null +++ b/tc/q_sfq.c @@ -0,0 +1,107 @@ +/* + * q_sfq.c SFQ. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n"); +} + +#define usage() return(-1) + +static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_sfq_qopt opt; + + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (strcmp(*argv, "quantum") == 0) { + NEXT_ARG(); + if (get_size(&opt.quantum, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "perturb") == 0) { + NEXT_ARG(); + if (get_integer(&opt.perturb_period, *argv, 0)) { + fprintf(stderr, "Illegal \"perturb\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_u32(&opt.limit, *argv, 0)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + if (opt.limit < 2) { + fprintf(stderr, "Illegal \"limit\", must be > 1\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (ok) + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + return 0; +} + +static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct tc_sfq_qopt *qopt; + SPRINT_BUF(b1); + + if (opt == NULL) + return 0; + + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + fprintf(f, "limit %up ", qopt->limit); + fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1)); + if (show_details) { + fprintf(f, "flows %u/%u ", qopt->flows, qopt->divisor); + } + if (qopt->perturb_period) + fprintf(f, "perturb %dsec ", qopt->perturb_period); + return 0; +} + +struct qdisc_util sfq_qdisc_util = { + .id = "sfq", + .parse_qopt = sfq_parse_opt, + .print_qopt = sfq_print_opt, +}; diff --git a/tc/q_sfq.o b/tc/q_sfq.o new file mode 100644 index 0000000..021c8c9 Binary files /dev/null and b/tc/q_sfq.o differ diff --git a/tc/q_tbf.c b/tc/q_tbf.c new file mode 100644 index 0000000..6ed5e0b --- /dev/null +++ b/tc/q_tbf.c @@ -0,0 +1,264 @@ +/* + * q_tbf.c TBF. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... tbf limit BYTES burst BYTES[/BYTES] rate KBPS [ mtu BYTES[/BYTES] ]\n"); + fprintf(stderr, " [ peakrate KBPS ] [ latency TIME ]\n"); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + + +#define usage() return(-1) + +static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_tbf_qopt opt; + __u32 rtab[256]; + __u32 ptab[256]; + unsigned buffer=0, mtu=0, mpu=0, latency=0; + int Rcell_log=-1, Pcell_log = -1; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (matches(*argv, "limit") == 0) { + NEXT_ARG(); + if (opt.limit || latency) { + fprintf(stderr, "Double \"limit/latency\" spec\n"); + return -1; + } + if (get_size(&opt.limit, *argv)) { + explain1("limit"); + return -1; + } + ok++; + } else if (matches(*argv, "latency") == 0) { + NEXT_ARG(); + if (opt.limit || latency) { + fprintf(stderr, "Double \"limit/latency\" spec\n"); + return -1; + } + if (get_usecs(&latency, *argv)) { + explain1("latency"); + return -1; + } + ok++; + } else if (matches(*argv, "burst") == 0 || + strcmp(*argv, "buffer") == 0 || + strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (buffer) { + fprintf(stderr, "Double \"buffer/burst\" spec\n"); + return -1; + } + if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) { + explain1("buffer"); + return -1; + } + ok++; + } else if (strcmp(*argv, "mtu") == 0 || + strcmp(*argv, "minburst") == 0) { + NEXT_ARG(); + if (mtu) { + fprintf(stderr, "Double \"mtu/minburst\" spec\n"); + return -1; + } + if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) { + explain1("mtu"); + return -1; + } + ok++; + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (mpu) { + fprintf(stderr, "Double \"mpu\" spec\n"); + return -1; + } + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + ok++; + } else if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (opt.rate.rate) { + fprintf(stderr, "Double \"rate\" spec\n"); + return -1; + } + if (get_rate(&opt.rate.rate, *argv)) { + explain1("rate"); + return -1; + } + ok++; + } else if (matches(*argv, "peakrate") == 0) { + NEXT_ARG(); + if (opt.peakrate.rate) { + fprintf(stderr, "Double \"peakrate\" spec\n"); + return -1; + } + if (get_rate(&opt.peakrate.rate, *argv)) { + explain1("peakrate"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!ok) + return 0; + + if (opt.rate.rate == 0 || !buffer) { + fprintf(stderr, "Both \"rate\" and \"burst\" are required.\n"); + return -1; + } + if (opt.peakrate.rate) { + if (!mtu) { + fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n"); + return -1; + } + } + + if (opt.limit == 0 && latency == 0) { + fprintf(stderr, "Either \"limit\" or \"latency\" are required.\n"); + return -1; + } + + if (opt.limit == 0) { + double lim = opt.rate.rate*(double)latency/1000000 + buffer; + if (opt.peakrate.rate) { + double lim2 = opt.peakrate.rate*(double)latency/1000000 + mtu; + if (lim2 < lim) + lim = lim2; + } + opt.limit = lim; + } + + if ((Rcell_log = tc_calc_rtable(opt.rate.rate, rtab, Rcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "TBF: failed to calculate rate table.\n"); + return -1; + } + opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer); + opt.rate.cell_log = Rcell_log; + opt.rate.mpu = mpu; + if (opt.peakrate.rate) { + if ((Pcell_log = tc_calc_rtable(opt.peakrate.rate, ptab, Pcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "TBF: failed to calculate peak rate table.\n"); + return -1; + } + opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu); + opt.peakrate.cell_log = Pcell_log; + opt.peakrate.mpu = mpu; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt)); + addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024); + if (opt.peakrate.rate) + addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_TBF_PTAB+1]; + struct tc_tbf_qopt *qopt; + double buffer, mtu; + double latency; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_TBF_PTAB, opt); + + if (tb[TCA_TBF_PARMS] == NULL) + return -1; + + qopt = RTA_DATA(tb[TCA_TBF_PARMS]); + if (RTA_PAYLOAD(tb[TCA_TBF_PARMS]) < sizeof(*qopt)) + return -1; + fprintf(f, "rate %s ", sprint_rate(qopt->rate.rate, b1)); + buffer = ((double)qopt->rate.rate*tc_core_tick2usec(qopt->buffer))/1000000; + if (show_details) { + fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1), + 1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2)); + } else { + fprintf(f, "burst %s ", sprint_size(buffer, b1)); + } + if (show_raw) + fprintf(f, "[%08x] ", qopt->buffer); + if (qopt->peakrate.rate) { + fprintf(f, "peakrate %s ", sprint_rate(qopt->peakrate.rate, b1)); + if (qopt->mtu || qopt->peakrate.mpu) { + mtu = ((double)qopt->peakrate.rate*tc_core_tick2usec(qopt->mtu))/1000000; + if (show_details) { + fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1), + 1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2)); + } else { + fprintf(f, "minburst %s ", sprint_size(mtu, b1)); + } + if (show_raw) + fprintf(f, "[%08x] ", qopt->mtu); + } + } + + if (show_raw) + fprintf(f, "limit %s ", sprint_size(qopt->limit, b1)); + + latency = 1000000*(qopt->limit/(double)qopt->rate.rate) - tc_core_tick2usec(qopt->buffer); + if (qopt->peakrate.rate) { + double lat2 = 1000000*(qopt->limit/(double)qopt->peakrate.rate) - tc_core_tick2usec(qopt->mtu); + if (lat2 > latency) + latency = lat2; + } + fprintf(f, "lat %s ", sprint_usecs(tc_core_tick2usec(latency), b1)); + + return 0; +} + +struct qdisc_util tbf_qdisc_util = { + .id = "tbf", + .parse_qopt = tbf_parse_opt, + .print_qopt = tbf_print_opt, +}; + diff --git a/tc/q_tbf.o b/tc/q_tbf.o new file mode 100644 index 0000000..b060ba6 Binary files /dev/null and b/tc/q_tbf.o differ diff --git a/tc/tc b/tc/tc new file mode 100755 index 0000000..0962fc7 Binary files /dev/null and b/tc/tc differ diff --git a/tc/tc.c b/tc/tc.c new file mode 100644 index 0000000..dd6ac97 --- /dev/null +++ b/tc/tc.c @@ -0,0 +1,347 @@ +/* + * tc.c "tc" utility frontend. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Fixes: + * + * Petri Mattila <petri@prihateam.fi> 990308: wrong memset's resulted in faults + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <errno.h> + +#include "SNAPSHOT.h" +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +int show_stats = 0; +int show_details = 0; +int show_raw = 0; +int resolve_hosts = 0; +int use_iec = 0; +struct rtnl_handle rth; + +static void *BODY; /* cached handle dlopen(NULL) */ +static struct qdisc_util * qdisc_list; +static struct filter_util * filter_list; + +static int print_noqopt(struct qdisc_util *qu, FILE *f, + struct rtattr *opt) +{ + if (opt && RTA_PAYLOAD(opt)) + fprintf(f, "[Unknown qdisc, optlen=%u] ", + (unsigned) RTA_PAYLOAD(opt)); + return 0; +} + +static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + if (argc) { + fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv); + return -1; + } + return 0; +} + +static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle) +{ + if (opt && RTA_PAYLOAD(opt)) + fprintf(f, "fh %08x [Unknown filter, optlen=%u] ", + fhandle, (unsigned) RTA_PAYLOAD(opt)); + else if (fhandle) + fprintf(f, "fh %08x ", fhandle); + return 0; +} + +static int parse_nofopt(struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n) +{ + __u32 handle; + + if (argc) { + fprintf(stderr, "Unknown filter \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv); + return -1; + } + if (fhandle) { + struct tcmsg *t = NLMSG_DATA(n); + if (get_u32(&handle, fhandle, 16)) { + fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle); + return -1; + } + t->tcm_handle = handle; + } + return 0; +} + +struct qdisc_util *get_qdisc_kind(const char *str) +{ + void *dlh; + char buf[256]; + struct qdisc_util *q; + + for (q = qdisc_list; q; q = q->next) + if (strcmp(q->id, str) == 0) + return q; + + snprintf(buf, sizeof(buf), "/usr/lib/tc/q_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (!dlh) { + /* look in current binary, only open once */ + dlh = BODY; + if (dlh == NULL) { + dlh = BODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_qdisc_util", str); + q = dlsym(dlh, buf); + if (q == NULL) + goto noexist; + +reg: + q->next = qdisc_list; + qdisc_list = q; + return q; + +noexist: + q = malloc(sizeof(*q)); + if (q) { + + memset(q, 0, sizeof(*q)); + q->id = strcpy(malloc(strlen(str)+1), str); + q->parse_qopt = parse_noqopt; + q->print_qopt = print_noqopt; + goto reg; + } + return q; +} + + +struct filter_util *get_filter_kind(const char *str) +{ + void *dlh; + char buf[256]; + struct filter_util *q; + + for (q = filter_list; q; q = q->next) + if (strcmp(q->id, str) == 0) + return q; + + snprintf(buf, sizeof(buf), "/usr/lib/tc/f_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = BODY; + if (dlh == NULL) { + dlh = BODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_filter_util", str); + q = dlsym(dlh, buf); + if (q == NULL) + goto noexist; + +reg: + q->next = filter_list; + filter_list = q; + return q; +noexist: + q = malloc(sizeof(*q)); + if (q) { + memset(q, 0, sizeof(*q)); + strncpy(q->id, str, 15); + q->parse_fopt = parse_nofopt; + q->print_fopt = print_nofopt; + goto reg; + } + return q; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n" + "where OBJECT := { qdisc | class | filter | action }\n" + " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -b[atch] file }\n"); +} + +static int do_cmd(int argc, char **argv) +{ + if (matches(*argv, "qdisc") == 0) + return do_qdisc(argc-1, argv+1); + + if (matches(*argv, "class") == 0) + return do_class(argc-1, argv+1); + + if (matches(*argv, "filter") == 0) + return do_filter(argc-1, argv+1); + + if (matches(*argv, "actions") == 0) + return do_action(argc-1, argv+1); + + if (matches(*argv, "help") == 0) { + usage(); + return 0; + } + + fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n", + *argv); + return -1; +} + +static int makeargs(char *line, char *argv[], int maxargs) +{ + static const char ws[] = " \t\r\n"; + char *cp; + int argc = 0; + + for (cp = strtok(line, ws); cp; cp = strtok(NULL, ws)) { + if (argc >= maxargs) { + fprintf(stderr, "Too many arguments to command\n"); + exit(1); + } + argv[argc++] = cp; + } + argv[argc] = NULL; + + return argc; +} + +static int batch(const char *name) +{ + char *line = NULL; + size_t len = 0; + ssize_t cc; + int lineno = 0; + char *largv[100]; + int largc, ret = 0; + + if (strcmp(name, "-") != 0) { + if (freopen(name, "r", stdin) == NULL) { + fprintf(stderr, "Cannot open file \"%s\" for reading: %s=n", + name, strerror(errno)); + return -1; + } + } + + tc_core_init(); + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + return -1; + } + + while ((cc = getline(&line, &len, stdin)) != -1) { + ++lineno; + + /* ignore blank lines and comments */ + if (*line == '\n' || *line == '#') + continue; + + /* handle continuation lines */ + while (cc >= 2 && strcmp(line+cc-2, "\\\n") == 0) { + char *line1 = NULL; + ssize_t len1 = 0; + int cc1; + cc1 = getline(&line1, &len1, stdin); + + if (cc1 < 0) { + fprintf(stderr, "Missing continuation line\n"); + return -1; + } + ++lineno; + line = realloc(line, cc + cc1); + if (!line) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + + strcpy(line+cc-2, line1); + cc += cc1 - 2; + free(line1); + } + + largc = makeargs(line, largv, 100); + + ret = do_cmd(largc, largv); + if (ret) { + fprintf(stderr, "Command failed %s:%d\n", name, lineno); + break; + } + } + + rtnl_close(&rth); + return ret; +} + + +int main(int argc, char **argv) +{ + int ret; + + while (argc > 1) { + if (argv[1][0] != '-') + break; + if (matches(argv[1], "-stats") == 0 || + matches(argv[1], "-statistics") == 0) { + ++show_stats; + } else if (matches(argv[1], "-details") == 0) { + ++show_details; + } else if (matches(argv[1], "-raw") == 0) { + ++show_raw; + } else if (matches(argv[1], "-Version") == 0) { + printf("tc utility, iproute2-ss%s\n", SNAPSHOT); + return 0; + } else if (matches(argv[1], "-iec") == 0) { + ++use_iec; + } else if (matches(argv[1], "-help") == 0) { + usage(); + return 0; + } else if (matches(argv[1], "-batch") == 0) { + if (argc < 3) { + fprintf(stderr, "Wrong number of arguments in batch mode\n"); + return -1; + } + + return batch(argv[2]); + } else { + fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]); + return -1; + } + argc--; argv++; + } + + if (argc <= 1) { + usage(); + return 0; + } + + tc_core_init(); + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + + ret = do_cmd(argc-1, argv+1); + rtnl_close(&rth); + + return ret; +} diff --git a/tc/tc.o b/tc/tc.o new file mode 100644 index 0000000..86a511c Binary files /dev/null and b/tc/tc.o differ diff --git a/tc/tc_cbq.c b/tc/tc_cbq.c new file mode 100644 index 0000000..0abcc9d --- /dev/null +++ b/tc/tc_cbq.c @@ -0,0 +1,57 @@ +/* + * tc_cbq.c CBQ maintanance routines. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" +#include "tc_cbq.h" + +unsigned tc_cbq_calc_maxidle(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned maxburst) +{ + double maxidle; + double g = 1.0 - 1.0/(1<<ewma_log); + double xmt = (double)avpkt/bndw; + + maxidle = xmt*(1-g); + if (bndw != rate && maxburst) { + double vxmt = (double)avpkt/rate - xmt; + vxmt *= (pow(g, -(double)maxburst) - 1); + if (vxmt > maxidle) + maxidle = vxmt; + } + return tc_core_usec2tick(maxidle*(1<<ewma_log)*1000000); +} + +unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned minburst) +{ + double g = 1.0 - 1.0/(1<<ewma_log); + double offtime = (double)avpkt/rate - (double)avpkt/bndw; + + if (minburst == 0) + return 0; + if (minburst == 1) + offtime *= pow(g, -(double)minburst) - 1; + else + offtime *= 1 + (pow(g, -(double)(minburst-1)) - 1)/(1-g); + return tc_core_usec2tick(offtime*1000000); +} diff --git a/tc/tc_cbq.h b/tc/tc_cbq.h new file mode 100644 index 0000000..8f95649 --- /dev/null +++ b/tc/tc_cbq.h @@ -0,0 +1,9 @@ +#ifndef _TC_CBQ_H_ +#define _TC_CBQ_H_ 1 + +unsigned tc_cbq_calc_maxidle(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned maxburst); +unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned minburst); + +#endif diff --git a/tc/tc_cbq.o b/tc/tc_cbq.o new file mode 100644 index 0000000..660babb Binary files /dev/null and b/tc/tc_cbq.o differ diff --git a/tc/tc_class.c b/tc/tc_class.c new file mode 100644 index 0000000..c4b27eb --- /dev/null +++ b/tc/tc_class.c @@ -0,0 +1,319 @@ +/* + * tc_class.c "tc class". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <math.h> + +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static void usage(void); + +static void usage(void) +{ + fprintf(stderr, "Usage: tc class [ add | del | change | get ] dev STRING\n"); + fprintf(stderr, " [ classid CLASSID ] [ root | parent CLASSID ]\n"); + fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " tc class show [ dev STRING ] [ root | parent CLASSID ]\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "QDISC_KIND := { prio | cbq | etc. }\n"); + fprintf(stderr, "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n"); + return; +} + +int tc_class_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[4096]; + } req; + struct qdisc_util *q = NULL; + struct tc_estimator est; + char d[16]; + char k[16]; + + memset(&req, 0, sizeof(req)); + memset(&est, 0, sizeof(est)); + memset(d, 0, sizeof(d)); + memset(k, 0, sizeof(k)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.t.tcm_family = AF_UNSPEC; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "classid") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_handle) + duparg("classid", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid class ID"); + req.t.tcm_handle = handle; + } else if (strcmp(*argv, "root") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID.\n"); + return -1; + } + req.t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + req.t.tcm_parent = handle; + } else if (matches(*argv, "estimator") == 0) { + if (parse_estimator(&argc, &argv, &est)) + return -1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + strncpy(k, *argv, sizeof(k)-1); + + q = get_qdisc_kind(k); + argc--; argv++; + break; + } + argc--; argv++; + } + + if (k[0]) + addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); + if (est.ewma_log) + addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); + + if (q) { + if (q->parse_copt == NULL) { + fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k); + return 1; + } + if (q->parse_copt(q, argc, argv, &req.n)) + return 1; + } else { + if (argc) { + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc class help\".", *argv); + return -1; + } + } + + if (d[0]) { + ll_init_map(&rth); + + if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + return 2; + + return 0; +} + +int filter_ifindex; +__u32 filter_qdisc; + +static int print_class(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcmsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCA_MAX+1]; + struct qdisc_util *q; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) { + fprintf(stderr, "Not a class\n"); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*t)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + if (filter_qdisc && TC_H_MAJ(t->tcm_handle^filter_qdisc)) + return 0; + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); + + if (tb[TCA_KIND] == NULL) { + fprintf(stderr, "print_class: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELTCLASS) + fprintf(fp, "deleted "); + + abuf[0] = 0; + if (t->tcm_handle) { + if (filter_qdisc) + print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_handle)); + else + print_tc_classid(abuf, sizeof(abuf), t->tcm_handle); + } + fprintf(fp, "class %s %s ", (char*)RTA_DATA(tb[TCA_KIND]), abuf); + + if (filter_ifindex == 0) + fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + + if (t->tcm_parent == TC_H_ROOT) + fprintf(fp, "root "); + else { + if (filter_qdisc) + print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_parent)); + else + print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); + fprintf(fp, "parent %s ", abuf); + } + if (t->tcm_info) + fprintf(fp, "leaf %x: ", t->tcm_info>>16); + q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); + if (tb[TCA_OPTIONS]) { + if (q && q->print_copt) + q->print_copt(q, fp, tb[TCA_OPTIONS]); + else + fprintf(fp, "[cannot parse class parameters]"); + } + fprintf(fp, "\n"); + if (show_stats) { + struct rtattr *xstats = NULL; + + if (tb[TCA_STATS] || tb[TCA_STATS2]) { + print_tcstats_attr(fp, tb, " ", &xstats); + fprintf(fp, "\n"); + } + if (q && (xstats || tb[TCA_XSTATS]) && q->print_xstats) { + q->print_xstats(q, fp, xstats ? : tb[TCA_XSTATS]); + fprintf(fp, "\n"); + } + } + fflush(fp); + return 0; +} + + +int tc_class_list(int argc, char **argv) +{ + struct tcmsg t; + char d[16]; + + memset(&t, 0, sizeof(t)); + t.tcm_family = AF_UNSPEC; + memset(d, 0, sizeof(d)); + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "qdisc") == 0) { + NEXT_ARG(); + if (filter_qdisc) + duparg("qdisc", *argv); + if (get_qdisc_handle(&filter_qdisc, *argv)) + invarg(*argv, "invalid qdisc ID"); + } else if (strcmp(*argv, "root") == 0) { + if (t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + if (t.tcm_parent) + duparg("parent", *argv); + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + t.tcm_parent = handle; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "What is \"%s\"? Try \"tc class help\".\n", *argv); + return -1; + } + + argc--; argv++; + } + + ll_init_map(&rth); + + if (d[0]) { + if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + filter_ifindex = t.tcm_ifindex; + } + + if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +int do_class(int argc, char **argv) +{ + if (argc < 1) + return tc_class_list(0, NULL); + if (matches(*argv, "add") == 0) + return tc_class_modify(RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return tc_class_modify(RTM_NEWTCLASS, 0, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return tc_class_modify(RTM_NEWTCLASS, NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return tc_class_modify(RTM_DELTCLASS, 0, argc-1, argv+1); +#if 0 + if (matches(*argv, "get") == 0) + return tc_class_get(RTM_GETTCLASS, 0, argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return tc_class_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv); + return -1; +} diff --git a/tc/tc_class.o b/tc/tc_class.o new file mode 100644 index 0000000..77d3dc1 Binary files /dev/null and b/tc/tc_class.o differ diff --git a/tc/tc_common.h b/tc/tc_common.h new file mode 100644 index 0000000..7e13582 --- /dev/null +++ b/tc/tc_common.h @@ -0,0 +1,11 @@ + +#define TCA_BUF_MAX (64*1024) + +extern struct rtnl_handle rth; +extern int do_qdisc(int argc, char **argv); +extern int do_class(int argc, char **argv); +extern int do_filter(int argc, char **argv); +extern int do_action(int argc, char **argv); + +struct tc_estimator; +extern int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est); diff --git a/tc/tc_core.c b/tc/tc_core.c new file mode 100644 index 0000000..07cf2fa --- /dev/null +++ b/tc/tc_core.c @@ -0,0 +1,89 @@ +/* + * tc_core.c TC core library. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" + +static __u32 t2us=1; +static __u32 us2t=1; +static double tick_in_usec = 1; + +long tc_core_usec2tick(long usec) +{ + return usec*tick_in_usec; +} + +long tc_core_tick2usec(long tick) +{ + return tick/tick_in_usec; +} + +unsigned tc_calc_xmittime(unsigned rate, unsigned size) +{ + return tc_core_usec2tick(1000000*((double)size/rate)); +} + +/* + rtab[pkt_len>>cell_log] = pkt_xmit_time + */ + +int tc_calc_rtable(unsigned bps, __u32 *rtab, int cell_log, unsigned mtu, + unsigned mpu) +{ + int i; + unsigned overhead = (mpu >> 8) & 0xFF; + mpu = mpu & 0xFF; + + if (mtu == 0) + mtu = 2047; + + if (cell_log < 0) { + cell_log = 0; + while ((mtu>>cell_log) > 255) + cell_log++; + } + for (i=0; i<256; i++) { + unsigned sz = (i<<cell_log); + if (overhead) + sz += overhead; + if (sz < mpu) + sz = mpu; + rtab[i] = tc_core_usec2tick(1000000*((double)sz/bps)); + } + return cell_log; +} + +int tc_core_init() +{ + FILE *fp = fopen("/proc/net/psched", "r"); + + if (fp == NULL) + return -1; + + if (fscanf(fp, "%08x%08x", &t2us, &us2t) != 2) { + fclose(fp); + return -1; + } + fclose(fp); + tick_in_usec = (double)t2us/us2t; + return 0; +} diff --git a/tc/tc_core.h b/tc/tc_core.h new file mode 100644 index 0000000..1537f95 --- /dev/null +++ b/tc/tc_core.h @@ -0,0 +1,19 @@ +#ifndef _TC_CORE_H_ +#define _TC_CORE_H_ 1 + +#include <asm/types.h> +#include <linux/pkt_sched.h> + +long tc_core_usec2tick(long usec); +long tc_core_tick2usec(long tick); +unsigned tc_calc_xmittime(unsigned rate, unsigned size); +int tc_calc_rtable(unsigned bps, __u32 *rtab, int cell_log, unsigned mtu, unsigned mpu); + +int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est); + +int tc_core_init(void); + +extern struct rtnl_handle g_rth; +extern int is_batch_mode; + +#endif diff --git a/tc/tc_core.o b/tc/tc_core.o new file mode 100644 index 0000000..9b7d80e Binary files /dev/null and b/tc/tc_core.o differ diff --git a/tc/tc_estimator.c b/tc/tc_estimator.c new file mode 100644 index 0000000..434db0f --- /dev/null +++ b/tc/tc_estimator.c @@ -0,0 +1,44 @@ +/* + * tc_core.c TC core library. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" + +int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est) +{ + for (est->interval=0; est->interval<=5; est->interval++) { + if (A <= (1<<est->interval)*(1000000/4)) + break; + } + if (est->interval > 5) + return -1; + est->interval -= 2; + for (est->ewma_log=1; est->ewma_log<32; est->ewma_log++) { + double w = 1.0 - 1.0/(1<<est->ewma_log); + if (A/(-log(w)) > time_const) + break; + } + est->ewma_log--; + if (est->ewma_log==0 || est->ewma_log >= 31) + return -1; + return 0; +} diff --git a/tc/tc_estimator.o b/tc/tc_estimator.o new file mode 100644 index 0000000..c397f73 Binary files /dev/null and b/tc/tc_estimator.o differ diff --git a/tc/tc_filter.c b/tc/tc_filter.c new file mode 100644 index 0000000..f6de840 --- /dev/null +++ b/tc/tc_filter.c @@ -0,0 +1,371 @@ +/* + * tc_filter.c "tc filter". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <linux/if_ether.h> + +#include "rt_names.h" +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static void usage(void); + +static void usage(void) +{ + fprintf(stderr, "Usage: tc filter [ add | del | change | get ] dev STRING\n"); + fprintf(stderr, " [ pref PRIO ] [ protocol PROTO ]\n"); + fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); + fprintf(stderr, " [ root | classid CLASSID ] [ handle FILTERID ]\n"); + fprintf(stderr, " [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " tc filter show [ dev STRING ] [ root | parent CLASSID ]\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "FILTER_TYPE := { rsvp | u32 | fw | route | etc. }\n"); + fprintf(stderr, "FILTERID := ... format depends on classifier, see there\n"); + fprintf(stderr, "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n"); + return; +} + + +int tc_filter_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[MAX_MSG]; + } req; + struct filter_util *q = NULL; + __u32 prio = 0; + __u32 protocol = 0; + char *fhandle = NULL; + char d[16]; + char k[16]; + struct tc_estimator est; + + memset(&req, 0, sizeof(req)); + memset(&est, 0, sizeof(est)); + memset(d, 0, sizeof(d)); + memset(k, 0, sizeof(k)); + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.t.tcm_family = AF_UNSPEC; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "root") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "Invalid parent ID"); + req.t.tcm_parent = handle; + } else if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + if (fhandle) + duparg("handle", *argv); + fhandle = *argv; + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "priority") == 0) { + NEXT_ARG(); + if (prio) + duparg("priority", *argv); + if (get_u32(&prio, *argv, 0)) + invarg(*argv, "invalid prpriority value"); + } else if (matches(*argv, "protocol") == 0) { + __u16 id; + NEXT_ARG(); + if (protocol) + duparg("protocol", *argv); + if (ll_proto_a2n(&id, *argv)) + invarg(*argv, "invalid protocol"); + protocol = id; + } else if (matches(*argv, "estimator") == 0) { + if (parse_estimator(&argc, &argv, &est) < 0) + return -1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + strncpy(k, *argv, sizeof(k)-1); + + q = get_filter_kind(k); + argc--; argv++; + break; + } + + argc--; argv++; + } + + req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); + + if (k[0]) + addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); + + if (q) { + if (q->parse_fopt(q, fhandle, argc, argv, &req.n)) + return 1; + } else { + if (fhandle) { + fprintf(stderr, "Must specify filter type when using " + "\"handle\"\n"); + return -1; + } + if (argc) { + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", *argv); + return -1; + } + } + if (est.ewma_log) + addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); + + + if (d[0]) { + ll_init_map(&rth); + + if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + return 2; + } + + return 0; +} + +static __u32 filter_parent; +static int filter_ifindex; +static __u32 filter_prio; +static __u32 filter_protocol; + +static int print_filter(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcmsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCA_MAX+1]; + struct filter_util *q; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWTFILTER && n->nlmsg_type != RTM_DELTFILTER) { + fprintf(stderr, "Not a filter\n"); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*t)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); + + if (tb[TCA_KIND] == NULL) { + fprintf(stderr, "print_filter: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELTFILTER) + fprintf(fp, "deleted "); + + fprintf(fp, "filter "); + if (!filter_ifindex || filter_ifindex != t->tcm_ifindex) + fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + + if (!filter_parent || filter_parent != t->tcm_parent) { + if (t->tcm_parent == TC_H_ROOT) + fprintf(fp, "root "); + else { + print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); + fprintf(fp, "parent %s ", abuf); + } + } + if (t->tcm_info) { + __u32 protocol = TC_H_MIN(t->tcm_info); + __u32 prio = TC_H_MAJ(t->tcm_info)>>16; + if (!filter_protocol || filter_protocol != protocol) { + if (protocol) { + SPRINT_BUF(b1); + fprintf(fp, "protocol %s ", + ll_proto_n2a(protocol, b1, sizeof(b1))); + } + } + if (!filter_prio || filter_prio != prio) { + if (prio) + fprintf(fp, "pref %u ", prio); + } + } + fprintf(fp, "%s ", (char*)RTA_DATA(tb[TCA_KIND])); + q = get_filter_kind(RTA_DATA(tb[TCA_KIND])); + if (tb[TCA_OPTIONS]) { + if (q) + q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle); + else + fprintf(fp, "[cannot parse parameters]"); + } + fprintf(fp, "\n"); + + if (show_stats && (tb[TCA_STATS] || tb[TCA_STATS2])) { + print_tcstats_attr(fp, tb, " ", NULL); + fprintf(fp, "\n"); + } + + fflush(fp); + return 0; +} + + +int tc_filter_list(int argc, char **argv) +{ + struct tcmsg t; + char d[16]; + __u32 prio = 0; + __u32 protocol = 0; + char *fhandle = NULL; + + memset(&t, 0, sizeof(t)); + t.tcm_family = AF_UNSPEC; + memset(d, 0, sizeof(d)); + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "root") == 0) { + if (t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + filter_parent = t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + filter_parent = t.tcm_parent = handle; + } else if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + if (fhandle) + duparg("handle", *argv); + fhandle = *argv; + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "priority") == 0) { + NEXT_ARG(); + if (prio) + duparg("priority", *argv); + if (get_u32(&prio, *argv, 0)) + invarg(*argv, "invalid preference"); + filter_prio = prio; + } else if (matches(*argv, "protocol") == 0) { + __u16 res; + NEXT_ARG(); + if (protocol) + duparg("protocol", *argv); + if (ll_proto_a2n(&res, *argv)) + invarg(*argv, "invalid protocol"); + protocol = res; + filter_protocol = protocol; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, " What is \"%s\"? Try \"tc filter help\"\n", *argv); + return -1; + } + + argc--; argv++; + } + + t.tcm_info = TC_H_MAKE(prio<<16, protocol); + + ll_init_map(&rth); + + if (d[0]) { + if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + filter_ifindex = t.tcm_ifindex; + } + + if (rtnl_dump_request(&rth, RTM_GETTFILTER, &t, sizeof(t)) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_filter, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +int do_filter(int argc, char **argv) +{ + if (argc < 1) + return tc_filter_list(0, NULL); + if (matches(*argv, "add") == 0) + return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1); +#if 0 + if (matches(*argv, "get") == 0) + return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return tc_filter_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", *argv); + return -1; +} + diff --git a/tc/tc_filter.o b/tc/tc_filter.o new file mode 100644 index 0000000..5924f02 Binary files /dev/null and b/tc/tc_filter.o differ diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c new file mode 100644 index 0000000..7802d52 --- /dev/null +++ b/tc/tc_qdisc.c @@ -0,0 +1,319 @@ +/* + * tc_qdisc.c "tc qdisc". + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * J Hadi Salim: Extension to ingress + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <math.h> + +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static int usage(void); + +static int usage(void) +{ + fprintf(stderr, "Usage: tc qdisc [ add | del | replace | change | get ] dev STRING\n"); + fprintf(stderr, " [ handle QHANDLE ] [ root | ingress | parent CLASSID ]\n"); + fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); + fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " tc qdisc show [ dev STRING ] [ingress]\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n"); + fprintf(stderr, "OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help\n"); + return -1; +} + +int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct qdisc_util *q = NULL; + struct tc_estimator est; + char d[16]; + char k[16]; + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[TCA_BUF_MAX]; + } req; + + memset(&req, 0, sizeof(req)); + memset(&est, 0, sizeof(est)); + memset(&d, 0, sizeof(d)); + memset(&k, 0, sizeof(k)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.t.tcm_family = AF_UNSPEC; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "handle") == 0) { + __u32 handle; + if (req.t.tcm_handle) + duparg("handle", *argv); + NEXT_ARG(); + if (get_qdisc_handle(&handle, *argv)) + invarg(*argv, "invalid qdisc ID"); + req.t.tcm_handle = handle; + } else if (strcmp(*argv, "root") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_ROOT; +#ifdef TC_H_INGRESS + } else if (strcmp(*argv, "ingress") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_INGRESS; + strncpy(k, "ingress", sizeof(k)-1); + q = get_qdisc_kind(k); + req.t.tcm_handle = 0xffff0000; + + argc--; argv++; + break; +#endif + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + req.t.tcm_parent = handle; + } else if (matches(*argv, "estimator") == 0) { + if (parse_estimator(&argc, &argv, &est)) + return -1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + strncpy(k, *argv, sizeof(k)-1); + + q = get_qdisc_kind(k); + argc--; argv++; + break; + } + argc--; argv++; + } + + if (k[0]) + addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); + if (est.ewma_log) + addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); + + if (q) { + if (q->parse_qopt(q, argc, argv, &req.n)) + return 1; + } else { + if (argc) { + if (matches(*argv, "help") == 0) + usage(); + + fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc qdisc help\".\n", *argv); + return -1; + } + } + + if (d[0]) { + int idx; + + ll_init_map(&rth); + + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + req.t.tcm_ifindex = idx; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + return 2; + + return 0; +} + +static int filter_ifindex; + +static int print_qdisc(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcmsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCA_MAX+1]; + struct qdisc_util *q; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) { + fprintf(stderr, "Not a qdisc\n"); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*t)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + if (filter_ifindex && filter_ifindex != t->tcm_ifindex) + return 0; + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); + + if (tb[TCA_KIND] == NULL) { + fprintf(stderr, "print_qdisc: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELQDISC) + fprintf(fp, "deleted "); + + fprintf(fp, "qdisc %s %x: ", (char*)RTA_DATA(tb[TCA_KIND]), t->tcm_handle>>16); + if (filter_ifindex == 0) + fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + if (t->tcm_parent == TC_H_ROOT) + fprintf(fp, "root "); + else if (t->tcm_parent) { + print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); + fprintf(fp, "parent %s ", abuf); + } + if (t->tcm_info != 1) { + fprintf(fp, "refcnt %d ", t->tcm_info); + } + /* pfifo_fast is generic enough to warrant the hardcoding --JHS */ + + if (0 == strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND]))) + q = get_qdisc_kind("prio"); + else + q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); + + if (tb[TCA_OPTIONS]) { + if (q) + q->print_qopt(q, fp, tb[TCA_OPTIONS]); + else + fprintf(fp, "[cannot parse qdisc parameters]"); + } + fprintf(fp, "\n"); + if (show_stats) { + struct rtattr *xstats = NULL; + + if (tb[TCA_STATS] || tb[TCA_STATS2] || tb[TCA_XSTATS]) { + print_tcstats_attr(fp, tb, " ", &xstats); + fprintf(fp, "\n"); + } + + if (q && xstats && q->print_xstats) { + q->print_xstats(q, fp, xstats); + fprintf(fp, "\n"); + } + } + fflush(fp); + return 0; +} + + +int tc_qdisc_list(int argc, char **argv) +{ + struct tcmsg t; + char d[16]; + + memset(&t, 0, sizeof(t)); + t.tcm_family = AF_UNSPEC; + memset(&d, 0, sizeof(d)); + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(d, *argv, sizeof(d)-1); +#ifdef TC_H_INGRESS + } else if (strcmp(*argv, "ingress") == 0) { + if (t.tcm_parent) { + fprintf(stderr, "Duplicate parent ID\n"); + usage(); + } + t.tcm_parent = TC_H_INGRESS; +#endif + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "What is \"%s\"? Try \"tc qdisc help\".\n", *argv); + return -1; + } + + argc--; argv++; + } + + ll_init_map(&rth); + + if (d[0]) { + if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + filter_ifindex = t.tcm_ifindex; + } + + if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_qdisc, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +int do_qdisc(int argc, char **argv) +{ + if (argc < 1) + return tc_qdisc_list(0, NULL); + if (matches(*argv, "add") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, 0, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "link") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return tc_qdisc_modify(RTM_DELQDISC, 0, argc-1, argv+1); +#if 0 + if (matches(*argv, "get") == 0) + return tc_qdisc_get(RTM_GETQDISC, 0, argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return tc_qdisc_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv); + return -1; +} diff --git a/tc/tc_qdisc.o b/tc/tc_qdisc.o new file mode 100644 index 0000000..6b619ec Binary files /dev/null and b/tc/tc_qdisc.o differ diff --git a/tc/tc_red.c b/tc/tc_red.c new file mode 100644 index 0000000..385e7af --- /dev/null +++ b/tc/tc_red.c @@ -0,0 +1,97 @@ +/* + * tc_red.c RED maintanance routines. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" +#include "tc_red.h" + +/* + Plog = log(prob/(qmax - qmin)) + */ +int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob) +{ + int i = qmax - qmin; + + if (i <= 0) + return -1; + + prob /= i; + + for (i=0; i<32; i++) { + if (prob > 1.0) + break; + prob *= 2; + } + if (i>=32) + return -1; + return i; +} + +/* + burst + 1 - qmin/avpkt < (1-(1-W)^burst)/W + */ + +int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt) +{ + int wlog = 1; + double W = 0.5; + double a = (double)burst + 1 - (double)qmin/avpkt; + + if (a < 1.0) + return -1; + for (wlog=1; wlog<32; wlog++, W /= 2) { + if (a <= (1 - pow(1-W, burst))/W) + return wlog; + } + return -1; +} + +/* + Stab[t>>Scell_log] = -log(1-W) * t/xmit_time + */ + +int tc_red_eval_idle_damping(int Wlog, unsigned avpkt, unsigned bps, __u8 *sbuf) +{ + double xmit_time = tc_core_usec2tick(1000000*(double)avpkt/bps); + double lW = -log(1.0 - 1.0/(1<<Wlog))/xmit_time; + double maxtime = 31/lW; + int clog; + int i; + double tmp; + + tmp = maxtime; + for (clog=0; clog<32; clog++) { + if (maxtime/(1<<clog) < 512) + break; + } + if (clog >= 32) + return -1; + + sbuf[0] = 0; + for (i=1; i<255; i++) { + sbuf[i] = (i<<clog)*lW; + if (sbuf[i] > 31) + sbuf[i] = 31; + } + sbuf[255] = 31; + return clog; +} diff --git a/tc/tc_red.h b/tc/tc_red.h new file mode 100644 index 0000000..6f6b09e --- /dev/null +++ b/tc/tc_red.h @@ -0,0 +1,8 @@ +#ifndef _TC_RED_H_ +#define _TC_RED_H_ 1 + +extern int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob); +extern int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt); +extern int tc_red_eval_idle_damping(int wlog, unsigned avpkt, unsigned bandwidth, __u8 *sbuf); + +#endif diff --git a/tc/tc_red.o b/tc/tc_red.o new file mode 100644 index 0000000..866ee10 Binary files /dev/null and b/tc/tc_red.o differ diff --git a/tc/tc_util.c b/tc/tc_util.c new file mode 100644 index 0000000..9cde144 --- /dev/null +++ b/tc/tc_util.c @@ -0,0 +1,519 @@ +/* + * tc_util.c Misc TC utility functions. + * + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <math.h> + +#include "utils.h" +#include "tc_util.h" + +int get_qdisc_handle(__u32 *h, const char *str) +{ + __u32 maj; + char *p; + + maj = TC_H_UNSPEC; + if (strcmp(str, "none") == 0) + goto ok; + maj = strtoul(str, &p, 16); + if (p == str) + return -1; + maj <<= 16; + if (*p != ':' && *p!=0) + return -1; +ok: + *h = maj; + return 0; +} + +int get_tc_classid(__u32 *h, const char *str) +{ + __u32 maj, min; + char *p; + + maj = TC_H_ROOT; + if (strcmp(str, "root") == 0) + goto ok; + maj = TC_H_UNSPEC; + if (strcmp(str, "none") == 0) + goto ok; + maj = strtoul(str, &p, 16); + if (p == str) { + maj = 0; + if (*p != ':') + return -1; + } + if (*p == ':') { + if (maj >= (1<<16)) + return -1; + maj <<= 16; + str = p+1; + min = strtoul(str, &p, 16); + if (*p != 0) + return -1; + if (min >= (1<<16)) + return -1; + maj |= min; + } else if (*p != 0) + return -1; + +ok: + *h = maj; + return 0; +} + +int print_tc_classid(char *buf, int len, __u32 h) +{ + if (h == TC_H_ROOT) + sprintf(buf, "root"); + else if (h == TC_H_UNSPEC) + snprintf(buf, len, "none"); + else if (TC_H_MAJ(h) == 0) + snprintf(buf, len, ":%x", TC_H_MIN(h)); + else if (TC_H_MIN(h) == 0) + snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); + else + snprintf(buf, len, "%x:%x", TC_H_MAJ(h)>>16, TC_H_MIN(h)); + return 0; +} + +char * sprint_tc_classid(__u32 h, char *buf) +{ + if (print_tc_classid(buf, SPRINT_BSIZE-1, h)) + strcpy(buf, "???"); + return buf; +} + +/* See http://physics.nist.gov/cuu/Units/binary.html */ +static const struct rate_suffix { + const char *name; + double scale; +} suffixes[] = { + { "bit", 1. }, + { "Kibit", 1024. }, + { "kbit", 1000. }, + { "mibit", 1024.*1024. }, + { "mbit", 1000000. }, + { "gibit", 1024.*1024.*1024. }, + { "gbit", 1000000000. }, + { "tibit", 1024.*1024.*1024.*1024. }, + { "tbit", 1000000000000. }, + { "Bps", 8. }, + { "KiBps", 8.*1024. }, + { "KBps", 8000. }, + { "MiBps", 8.*1024*1024. }, + { "MBps", 8000000. }, + { "GiBps", 8.*1024.*1024.*1024. }, + { "GBps", 8000000000. }, + { "TiBps", 8.*1024.*1024.*1024.*1024. }, + { "TBps", 8000000000000. }, + { NULL } +}; + + +int get_rate(unsigned *rate, const char *str) +{ + char *p; + double bps = strtod(str, &p); + const struct rate_suffix *s; + + if (p == str) + return -1; + + if (*p == '\0') { + *rate = bps / 8.; /* assume bytes/sec */ + return 0; + } + + for (s = suffixes; s->name; ++s) { + if (strcasecmp(s->name, p) == 0) { + *rate = (bps * s->scale) / 8.; + return 0; + } + } + + return -1; +} + +int get_rate_and_cell(unsigned *rate, int *cell_log, char *str) +{ + char * slash = strchr(str, '/'); + + if (slash) + *slash = 0; + + if (get_rate(rate, str)) + return -1; + + if (slash) { + int cell; + int i; + + if (get_integer(&cell, slash+1, 0)) + return -1; + *slash = '/'; + + for (i=0; i<32; i++) { + if ((1<<i) == cell) { + *cell_log = i; + return 0; + } + } + return -1; + } + return 0; +} + +void print_rate(char *buf, int len, __u32 rate) +{ + double tmp = (double)rate*8; + extern int use_iec; + + if (use_iec) { + if (tmp >= 1000.0*1024.0*1024.0) + snprintf(buf, len, "%.0fMibit", tmp/1024.0*1024.0); + else if (tmp >= 1000.0*1024) + snprintf(buf, len, "%.0fKibit", tmp/1024); + else + snprintf(buf, len, "%.0fbit", tmp); + } else { + if (tmp >= 1000.0*1000000.0) + snprintf(buf, len, "%.0fMbit", tmp/1000000.0); + else if (tmp >= 1000.0 * 1000.0) + snprintf(buf, len, "%.0fKbit", tmp/1000.0); + else + snprintf(buf, len, "%.0fbit", tmp); + } +} + +char * sprint_rate(__u32 rate, char *buf) +{ + print_rate(buf, SPRINT_BSIZE-1, rate); + return buf; +} + +int get_usecs(unsigned *usecs, const char *str) +{ + double t; + char *p; + + t = strtod(str, &p); + if (p == str) + return -1; + + if (*p) { + if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 || + strcasecmp(p, "secs")==0) + t *= 1000000; + else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 || + strcasecmp(p, "msecs") == 0) + t *= 1000; + else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 || + strcasecmp(p, "usecs") == 0) + t *= 1; + else + return -1; + } + + *usecs = t; + return 0; +} + + +void print_usecs(char *buf, int len, __u32 usec) +{ + double tmp = usec; + + if (tmp >= 1000000) + snprintf(buf, len, "%.1fs", tmp/1000000); + else if (tmp >= 1000) + snprintf(buf, len, "%.1fms", tmp/1000); + else + snprintf(buf, len, "%uus", usec); +} + +char * sprint_usecs(__u32 usecs, char *buf) +{ + print_usecs(buf, SPRINT_BSIZE-1, usecs); + return buf; +} + +int get_size(unsigned *size, const char *str) +{ + double sz; + char *p; + + sz = strtod(str, &p); + if (p == str) + return -1; + + if (*p) { + if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k")==0) + sz *= 1024; + else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g")==0) + sz *= 1024*1024*1024; + else if (strcasecmp(p, "gbit") == 0) + sz *= 1024*1024*1024/8; + else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m")==0) + sz *= 1024*1024; + else if (strcasecmp(p, "mbit") == 0) + sz *= 1024*1024/8; + else if (strcasecmp(p, "kbit") == 0) + sz *= 1024/8; + else if (strcasecmp(p, "b") != 0) + return -1; + } + + *size = sz; + return 0; +} + +int get_size_and_cell(unsigned *size, int *cell_log, char *str) +{ + char * slash = strchr(str, '/'); + + if (slash) + *slash = 0; + + if (get_size(size, str)) + return -1; + + if (slash) { + int cell; + int i; + + if (get_integer(&cell, slash+1, 0)) + return -1; + *slash = '/'; + + for (i=0; i<32; i++) { + if ((1<<i) == cell) { + *cell_log = i; + return 0; + } + } + return -1; + } + return 0; +} + +void print_size(char *buf, int len, __u32 sz) +{ + double tmp = sz; + + if (sz >= 1024*1024 && fabs(1024*1024*rint(tmp/(1024*1024)) - sz) < 1024) + snprintf(buf, len, "%gMb", rint(tmp/(1024*1024))); + else if (sz >= 1024 && fabs(1024*rint(tmp/1024) - sz) < 16) + snprintf(buf, len, "%gKb", rint(tmp/1024)); + else + snprintf(buf, len, "%ub", sz); +} + +char * sprint_size(__u32 size, char *buf) +{ + print_size(buf, SPRINT_BSIZE-1, size); + return buf; +} + +static const double max_percent_value = 0xffffffff; + +int get_percent(__u32 *percent, const char *str) +{ + char *p; + double per = strtod(str, &p) / 100.; + + if (per > 1. || per < 0) + return -1; + if (*p && strcmp(p, "%")) + return -1; + + *percent = (unsigned) rint(per * max_percent_value); + return 0; +} + +void print_percent(char *buf, int len, __u32 per) +{ + snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value); +} + +char * sprint_percent(__u32 per, char *buf) +{ + print_percent(buf, SPRINT_BSIZE-1, per); + return buf; +} + +void print_qdisc_handle(char *buf, int len, __u32 h) +{ + snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); +} + +char * sprint_qdisc_handle(__u32 h, char *buf) +{ + print_qdisc_handle(buf, SPRINT_BSIZE-1, h); + return buf; +} + +char * action_n2a(int action, char *buf, int len) +{ + switch (action) { + case -1: + return "continue"; + break; + case TC_ACT_OK: + return "pass"; + break; + case TC_ACT_SHOT: + return "drop"; + break; + case TC_ACT_RECLASSIFY: + return "reclassify"; + case TC_ACT_PIPE: + return "pipe"; + case TC_ACT_STOLEN: + return "stolen"; + default: + snprintf(buf, len, "%d", action); + return buf; + } +} + +int action_a2n(char *arg, int *result) +{ + int res; + + if (matches(arg, "continue") == 0) + res = -1; + else if (matches(arg, "drop") == 0) + res = TC_ACT_SHOT; + else if (matches(arg, "shot") == 0) + res = TC_ACT_SHOT; + else if (matches(arg, "pass") == 0) + res = TC_ACT_OK; + else if (strcmp(arg, "ok") == 0) + res = TC_ACT_OK; + else if (matches(arg, "reclassify") == 0) + res = TC_ACT_RECLASSIFY; + else { + char dummy; + if (sscanf(arg, "%d%c", &res, &dummy) != 1) + return -1; + } + *result = res; + return 0; +} + +void print_tm(FILE * f, const struct tcf_t *tm) +{ + int hz = get_user_hz(); + if (tm->install != 0) + fprintf(f, " installed %u sec", (unsigned)(tm->install/hz)); + if (tm->lastuse != 0) + fprintf(f, " used %u sec", (unsigned)(tm->lastuse/hz)); + if (tm->expires != 0) + fprintf(f, " expires %u sec", (unsigned)(tm->expires/hz)); +} + +void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats) +{ + SPRINT_BUF(b1); + struct rtattr *tbs[TCA_STATS_MAX + 1]; + + parse_rtattr_nested(tbs, TCA_STATS_MAX, rta); + + if (tbs[TCA_STATS_BASIC]) { + struct gnet_stats_basic bs = {0}; + memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); + fprintf(fp, "%sSent %llu bytes %u pkt", + prefix, (unsigned long long) bs.bytes, bs.packets); + } + + if (tbs[TCA_STATS_QUEUE]) { + struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); + fprintf(fp, " (dropped %u, overlimits %u requeues %u) ", + q.drops, q.overlimits, q.requeues); + } + + if (tbs[TCA_STATS_RATE_EST]) { + struct gnet_stats_rate_est re = {0}; + memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re))); + fprintf(fp, "\n%srate %s %upps ", + prefix, sprint_rate(re.bps, b1), re.pps); + } + + if (tbs[TCA_STATS_QUEUE]) { + struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); + if (!tbs[TCA_STATS_RATE_EST]) + fprintf(fp, "\n%s", prefix); + fprintf(fp, "backlog %s %up requeues %u ", + sprint_size(q.backlog, b1), q.qlen, q.requeues); + } + + if (xstats) + *xstats = tbs[TCA_STATS_APP] ? : NULL; +} + +void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats) +{ + SPRINT_BUF(b1); + + if (tb[TCA_STATS2]) { + print_tcstats2_attr(fp, tb[TCA_STATS2], prefix, xstats); + if (xstats && NULL == *xstats) + goto compat_xstats; + return; + } + /* backward compatibility */ + if (tb[TCA_STATS]) { + struct tc_stats st; + + /* handle case where kernel returns more/less than we know about */ + memset(&st, 0, sizeof(st)); + memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st))); + + fprintf(fp, "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ", + prefix, (unsigned long long)st.bytes, st.packets, st.drops, + st.overlimits); + + if (st.bps || st.pps || st.qlen || st.backlog) { + fprintf(fp, "\n%s", prefix); + if (st.bps || st.pps) { + fprintf(fp, "rate "); + if (st.bps) + fprintf(fp, "%s ", sprint_rate(st.bps, b1)); + if (st.pps) + fprintf(fp, "%upps ", st.pps); + } + if (st.qlen || st.backlog) { + fprintf(fp, "backlog "); + if (st.backlog) + fprintf(fp, "%s ", sprint_size(st.backlog, b1)); + if (st.qlen) + fprintf(fp, "%up ", st.qlen); + } + } + } + +compat_xstats: + if (tb[TCA_XSTATS] && xstats) + *xstats = tb[TCA_XSTATS]; +} + diff --git a/tc/tc_util.h b/tc/tc_util.h new file mode 100644 index 0000000..1aa1bda --- /dev/null +++ b/tc/tc_util.h @@ -0,0 +1,84 @@ +#ifndef _TC_UTIL_H_ +#define _TC_UTIL_H_ 1 + +#define MAX_MSG 16384 +#include <linux/pkt_sched.h> +#include <linux/pkt_cls.h> +#include <linux/gen_stats.h> +#include "tc_core.h" + +struct qdisc_util +{ + struct qdisc_util *next; + const char *id; + int (*parse_qopt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n); + int (*print_qopt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); + int (*print_xstats)(struct qdisc_util *qu, FILE *f, struct rtattr *xstats); + + int (*parse_copt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n); + int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); +}; + +struct filter_util +{ + struct filter_util *next; + char id[16]; + int (*parse_fopt)(struct filter_util *qu, char *fhandle, int argc, + char **argv, struct nlmsghdr *n); + int (*print_fopt)(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle); +}; + +struct action_util +{ + struct action_util *next; + char id[16]; + int (*parse_aopt)(struct action_util *a, int *argc, char ***argv, + int code, struct nlmsghdr *n); + int (*print_aopt)(struct action_util *au, FILE *f, struct rtattr *opt); + int (*print_xstats)(struct action_util *au, FILE *f, struct rtattr *xstats); +}; + +extern struct qdisc_util *get_qdisc_kind(const char *str); +extern struct filter_util *get_filter_kind(const char *str); + +extern int get_qdisc_handle(__u32 *h, const char *str); +extern int get_rate(unsigned *rate, const char *str); +extern int get_percent(unsigned *percent, const char *str); +extern int get_size(unsigned *size, const char *str); +extern int get_size_and_cell(unsigned *size, int *cell_log, char *str); +extern int get_usecs(unsigned *usecs, const char *str); +extern void print_rate(char *buf, int len, __u32 rate); +extern void print_size(char *buf, int len, __u32 size); +extern void print_percent(char *buf, int len, __u32 percent); +extern void print_qdisc_handle(char *buf, int len, __u32 h); +extern void print_usecs(char *buf, int len, __u32 usecs); +extern char * sprint_rate(__u32 rate, char *buf); +extern char * sprint_size(__u32 size, char *buf); +extern char * sprint_qdisc_handle(__u32 h, char *buf); +extern char * sprint_tc_classid(__u32 h, char *buf); +extern char * sprint_usecs(__u32 usecs, char *buf); +extern char * sprint_percent(__u32 percent, char *buf); + +extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats); +extern void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats); + +extern int get_tc_classid(__u32 *h, const char *str); +extern int print_tc_classid(char *buf, int len, __u32 h); +extern char * sprint_tc_classid(__u32 h, char *buf); + +extern int tc_print_police(FILE *f, struct rtattr *tb); +extern int parse_police(int *, char ***, int, struct nlmsghdr *); + +extern char *action_n2a(int action, char *buf, int len); +extern int action_a2n(char *arg, int *result); +extern int act_parse_police(struct action_util *a,int *, char ***, int, struct nlmsghdr *); +extern int print_police(struct action_util *a, FILE *f, + struct rtattr *tb); +extern int police_print_xstats(struct action_util *a,FILE *f, + struct rtattr *tb); +extern int tc_print_action(FILE *f, const struct rtattr *tb); +extern int tc_print_ipt(FILE *f, const struct rtattr *tb); +extern int parse_action(int *, char ***, int, struct nlmsghdr *); +extern void print_tm(FILE *f, const struct tcf_t *tm); + +#endif diff --git a/tc/tc_util.o b/tc/tc_util.o new file mode 100644 index 0000000..bfe5a31 Binary files /dev/null and b/tc/tc_util.o differ diff --git a/testsuite/Makefile b/testsuite/Makefile new file mode 100644 index 0000000..5661cea --- /dev/null +++ b/testsuite/Makefile @@ -0,0 +1,33 @@ +TESTS := $(patsubst tests/%,%,$(wildcard tests/*)) +IPVERS := $(filter-out iproute2/Makefile,$(wildcard iproute2/*)) + +DEV := eth0 + +.PHONY: compile listtests alltests $(TESTS) + +compile: + echo "Entering iproute2" && cd iproute2 && $(MAKE) && cd ..; + +listtests: + @for t in $(TESTS); do \ + echo "$$t"; \ + done + +alltests: $(TESTS) + +clean: + @rm -rf results/* + +$(TESTS): + @for i in $(IPVERS); do \ + echo -n "Running $@ with $$i on `uname -r`: "; \ + logger "TESTMARK: $@"; \ + o=`echo $$i | sed -e 's/iproute2\///'`; \ + TC="$$i/tc/tc" IP="$$i/ip/ip" DEV="$(DEV)" sudo tests/$@ > results/$@.$$o.out 2> results/$@.$$o.err; \ + dmesg > results/$@.$$o.dmesg; \ + if [ -z "`cat results/$@.$$o.err`" ]; then \ + echo "PASS"; \ + else \ + echo "FAILED"; \ + fi \ + done diff --git a/testsuite/configs/all-2.4 b/testsuite/configs/all-2.4 new file mode 100644 index 0000000..cc4131c --- /dev/null +++ b/testsuite/configs/all-2.4 @@ -0,0 +1,848 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_X86=y +# CONFIG_SBUS is not set +CONFIG_UID16=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMIII is not set +CONFIG_MPENTIUM4=y +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MELAN is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_X86_HAS_TSC=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_PGE=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +CONFIG_X86_F00F_WORKS_OK=y +CONFIG_X86_MCE=y +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set +# CONFIG_EDD is not set +CONFIG_NOHIGHMEM=y +# CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM64G is not set +# CONFIG_HIGHMEM is not set +# CONFIG_MATH_EMULATION is not set +# CONFIG_MTRR is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=32 +# CONFIG_X86_NUMA is not set +# CONFIG_X86_TSC_DISABLE is not set +CONFIG_X86_TSC=y +CONFIG_HAVE_DEC_LOCK=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_ISA=y +CONFIG_PCI_NAMES=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +CONFIG_HOTPLUG=y + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +CONFIG_CARDBUS=y +# CONFIG_TCIC is not set +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HOTPLUG_PCI_COMPAQ is not set +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +# CONFIG_HOTPLUG_PCI_IBM is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set +# CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +# CONFIG_OOM_KILLER is not set +CONFIG_PM=y +# CONFIG_APM is not set + +# +# ACPI Support +# +# CONFIG_ACPI is not set +CONFIG_ACPI_BOOT=y + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play configuration +# +CONFIG_PNP=y +CONFIG_ISAPNP=y + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_CISS_MONITOR_THREAD is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BLK_STATS is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_NAT=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_TOS=y +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +CONFIG_ATM=y +CONFIG_ATM_CLIP=y +# CONFIG_ATM_CLIP_NO_ICMP is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_BR2684 is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_CSZ=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_ATM=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_ROUTE4=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_CLS_RSVP=y +CONFIG_NET_CLS_RSVP6=y +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set +# CONFIG_PHONE_IXJ_PCMCIA is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +# CONFIG_IDEDISK_STROKE is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_DELKIN is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +CONFIG_BLK_DEV_CMD640=y +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_BLK_DEV_GENERIC is not set +CONFIG_IDEPCI_SHARE_IRQ=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_BLK_DEV_ADMA100 is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +CONFIG_BLK_DEV_PIIX=y +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +CONFIG_BLK_DEV_RZ1000=y +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_IDEDMA_IVB is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set +# CONFIG_BLK_DEV_ATARAID_SII is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +CONFIG_DUMMY=m +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_B44 is not set +# CONFIG_CS89x0 is not set +# CONFIG_TULIP is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +# CONFIG_EEPRO100 is not set +# CONFIG_EEPRO100_PIO is not set +# CONFIG_E100 is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +CONFIG_8139TOO=y +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_OLD_RX_RESET is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNDANCE_MMIO is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# +# ATM drivers +# +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_UINPUT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set +# CONFIG_MK712_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPMI_PANIC_EVENT is not set +# CONFIG_IPMI_DEVICE_INTERFACE is not set +# CONFIG_IPMI_KCS is not set +# CONFIG_IPMI_WATCHDOG is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set +# CONFIG_SCx200_GPIO is not set +# CONFIG_AMD_RNG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_AMD_PM768 is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set + +# +# Direct Rendering Manager (XFree86 DRI support) +# +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_PCMCIA_SERIAL_CS is not set +# CONFIG_SYNCLINK_CS is not set +# CONFIG_MWAVE is not set +# CONFIG_OBMOUSE is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_QFMT_V2 is not set +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=y +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BEFS_DEBUG is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +CONFIG_RAMFS=y +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_TCP=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Console drivers +# +CONFIG_VGA_CONSOLE=y +# CONFIG_VIDEO_SELECT is not set +# CONFIG_MDA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Sound +# +CONFIG_SOUND=y +# CONFIG_SOUND_ALI5455 is not set +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_CMPCI is not set +CONFIG_SOUND_EMU10K1=y +CONFIG_MIDI_EMU10K1=y +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_ES1370 is not set +# CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_ESSSOLO1 is not set +# CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_MAESTRO3 is not set +# CONFIG_SOUND_FORTE is not set +# CONFIG_SOUND_ICH is not set +# CONFIG_SOUND_RME96XX is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_VIA82CXXX is not set +# CONFIG_MIDI_VIA82CXXX is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_TVMIXER is not set +# CONFIG_SOUND_AD1980 is not set +# CONFIG_SOUND_WM97XX is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_UHCI_ALT=y +# CONFIG_USB_OHCI is not set +# CONFIG_USB_SL811HS_ALT is not set +# CONFIG_USB_SL811HS is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_MIDI is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_HID is not set +# CONFIG_USB_HIDINPUT is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_BRLVGER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_SPEEDTOUCH is not set + +# +# Support for USB gadgets +# +# CONFIG_USB_GADGET is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_DEBUG_HIGHMEM=y +CONFIG_DEBUG_SLAB=y +CONFIG_DEBUG_IOVIRT=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_FRAME_POINTER=y +CONFIG_LOG_BUF_SHIFT=0 + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC32=y +# CONFIG_ZLIB_INFLATE is not set +# CONFIG_ZLIB_DEFLATE is not set +# CONFIG_FW_LOADER is not set diff --git a/testsuite/configs/all-no-act b/testsuite/configs/all-no-act new file mode 100644 index 0000000..baeed43 --- /dev/null +++ b/testsuite/configs/all-no-act @@ -0,0 +1,1499 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc2-bk13 +# Wed Dec 8 14:43:07 2004 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_LOCK_KERNEL=y + +# +# General setup +# +CONFIG_LOCALVERSION="no-act" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_KOBJECT_UEVENT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_X86_PC=y +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +# CONFIG_X86_GENERICARCH is not set +# CONFIG_X86_ES7000 is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +CONFIG_MPENTIUM4=y +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MEFFICEON is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +# CONFIG_X86_GENERIC is not set +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +# CONFIG_HPET_TIMER is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=8 +# CONFIG_SCHED_SMT is not set +CONFIG_PREEMPT=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +CONFIG_X86_MCE_NONFATAL=y +# CONFIG_X86_MCE_P4THERMAL is not set +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_NOHIGHMEM=y +# CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM64G is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +CONFIG_IRQBALANCE=y +CONFIG_HAVE_DEC_LOCK=y +# CONFIG_REGPARM is not set + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_SOFTWARE_SUSPEND=y +CONFIG_PM_STD_PARTITION="" + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_VIDEO=y +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_ASUS is not set +# CONFIG_ACPI_IBM is not set +# CONFIG_ACPI_TOSHIBA is not set +# CONFIG_ACPI_CUSTOM_DSDT is not set +CONFIG_ACPI_BLACKLIST_YEAR=0 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +# CONFIG_X86_PM_TIMER is not set + +# +# APM (Advanced Power Management) BIOS Support +# +# CONFIG_APM is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +# CONFIG_PCI_MSI is not set +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PC-card bridges +# +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=m +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set +CONFIG_PNPACPI=y + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y +CONFIG_BLK_DEV_NBD=y +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_LBD=y +CONFIG_CDROM_PKTCDVD=y +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +CONFIG_BLK_DEV_CMD640=y +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_IDEPNP is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +CONFIG_BLK_DEV_SIS5513=y +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +# CONFIG_BLK_DEV_SD is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +CONFIG_I2O=y +CONFIG_I2O_CONFIG=y +CONFIG_I2O_BLOCK=y +# CONFIG_I2O_SCSI is not set +CONFIG_I2O_PROC=y + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_TUNNEL=y +CONFIG_IP_TCPDIAG=y +CONFIG_IP_TCPDIAG_IPV6=y + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_INET6_TUNNEL=y +CONFIG_IPV6_TUNNEL=y +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=y +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CONNTRACK_MARK=y +# CONFIG_IP_NF_CT_PROTO_SCTP is not set +CONFIG_IP_NF_FTP=y +CONFIG_IP_NF_IRC=y +CONFIG_IP_NF_TFTP=y +# CONFIG_IP_NF_AMANDA is not set +CONFIG_IP_NF_QUEUE=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_LIMIT=y +CONFIG_IP_NF_MATCH_IPRANGE=y +CONFIG_IP_NF_MATCH_MAC=y +CONFIG_IP_NF_MATCH_PKTTYPE=y +CONFIG_IP_NF_MATCH_MARK=y +CONFIG_IP_NF_MATCH_MULTIPORT=y +CONFIG_IP_NF_MATCH_TOS=y +CONFIG_IP_NF_MATCH_RECENT=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_DSCP=y +CONFIG_IP_NF_MATCH_AH_ESP=y +CONFIG_IP_NF_MATCH_LENGTH=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_MATCH_TCPMSS=y +CONFIG_IP_NF_MATCH_HELPER=y +CONFIG_IP_NF_MATCH_STATE=y +CONFIG_IP_NF_MATCH_CONNTRACK=y +CONFIG_IP_NF_MATCH_OWNER=y +CONFIG_IP_NF_MATCH_PHYSDEV=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_REALM=y +# CONFIG_IP_NF_MATCH_SCTP is not set +CONFIG_IP_NF_MATCH_COMMENT=y +CONFIG_IP_NF_MATCH_CONNMARK=y +CONFIG_IP_NF_MATCH_HASHLIMIT=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_IP_NF_TARGET_ULOG=y +CONFIG_IP_NF_TARGET_TCPMSS=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_SAME=y +# CONFIG_IP_NF_NAT_LOCAL is not set +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_IRC=y +CONFIG_IP_NF_NAT_FTP=y +CONFIG_IP_NF_NAT_TFTP=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_TOS=y +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_DSCP=y +CONFIG_IP_NF_TARGET_MARK=y +CONFIG_IP_NF_TARGET_CLASSIFY=y +CONFIG_IP_NF_TARGET_CONNMARK=y +CONFIG_IP_NF_TARGET_CLUSTERIP=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_TARGET_NOTRACK=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_IP6_NF_QUEUE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_LIMIT=y +CONFIG_IP6_NF_MATCH_MAC=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_MULTIPORT=y +CONFIG_IP6_NF_MATCH_OWNER=y +CONFIG_IP6_NF_MATCH_MARK=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_AHESP=y +CONFIG_IP6_NF_MATCH_LENGTH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_PHYSDEV=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_TARGET_MARK=y +CONFIG_IP6_NF_RAW=y + +# +# Bridge: Netfilter Configuration +# +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +CONFIG_ATM=y +# CONFIG_ATM_CLIP is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y +# CONFIG_DECNET is not set +CONFIG_LLC=y +CONFIG_LLC2=y +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_ATM=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_ROUTE4=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_CLS_EGP=y +CONFIG_EGP_DEBUG=y +CONFIG_NET_CLS_EGP_SIMPLE_CMP=y +CONFIG_NET_CLS_EGP_NBYTE=y +CONFIG_NET_CLS_EGP_KMP=y +CONFIG_NET_CLS_EGP_REGEXP=y +CONFIG_NET_CLS_EGP_CMD=y +CONFIG_EGP_CMD_BACK_TTL=4096 +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=y +CONFIG_NET_CLS_RSVP6=y +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +CONFIG_NET_PKTGEN=y +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_NET_SB1000 is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_CS89x0 is not set +# CONFIG_DGRS is not set +# CONFIG_EEPRO100 is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +CONFIG_8139TOO=y +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_OLD_RX_RESET is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPPOE is not set +# CONFIG_PPPOATM is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +CONFIG_SHAPER=y +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +CONFIG_I2C_SIS96X=y +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=y +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=y +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +# CONFIG_IBM_ASM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set +# CONFIG_VIDEO_SELECT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_SEQUENCER=y +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ISA devices +# +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_SGALAXY is not set +# CONFIG_SND_SSCAPE is not set + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=y +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS4281 is not set +CONFIG_SND_EMU10K1=y +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VX222 is not set + +# +# USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_USX2Y is not set + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_OHCI_HCD is not set +CONFIG_USB_UHCI_HCD=y + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_MIDI is not set +# CONFIG_USB_ACM is not set +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +CONFIG_USB_EGALAX=m +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +CONFIG_USB_CYTHERM=m +# CONFIG_USB_PHIDGETKIT is not set +CONFIG_USB_PHIDGETSERVO=m +# CONFIG_USB_TEST is not set + +# +# USB ATM/DSL drivers +# +# CONFIG_USB_ATM is not set +# CONFIG_USB_SPEEDTOUCH is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=y +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=y +CONFIG_EXPORTFS=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHEDSTATS is not set +CONFIG_DEBUG_SLAB=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +CONFIG_EARLY_PRINTK=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_KPROBES is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_4KSTACKS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y + +# +# Security options +# +# CONFIG_KEYS is not set +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=m +CONFIG_SECURITY_ROOTPLUG=m +# CONFIG_SECURITY_SECLVL is not set +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_WP512=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_SERPENT=y +CONFIG_CRYPTO_AES_586=y +CONFIG_CRYPTO_CAST5=y +CONFIG_CRYPTO_CAST6=y +CONFIG_CRYPTO_TEA=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_KHAZAD=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_TEST=y + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_PC=y diff --git a/testsuite/configs/all-police-act b/testsuite/configs/all-police-act new file mode 100644 index 0000000..1c84282 --- /dev/null +++ b/testsuite/configs/all-police-act @@ -0,0 +1,1504 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc2-bk13 +# Wed Dec 8 14:19:17 2004 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_LOCK_KERNEL=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_KOBJECT_UEVENT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_X86_PC=y +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +# CONFIG_X86_GENERICARCH is not set +# CONFIG_X86_ES7000 is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +CONFIG_MPENTIUM4=y +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MEFFICEON is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +# CONFIG_X86_GENERIC is not set +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +# CONFIG_HPET_TIMER is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=8 +# CONFIG_SCHED_SMT is not set +CONFIG_PREEMPT=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +CONFIG_X86_MCE_NONFATAL=y +# CONFIG_X86_MCE_P4THERMAL is not set +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_NOHIGHMEM=y +# CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM64G is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +CONFIG_IRQBALANCE=y +CONFIG_HAVE_DEC_LOCK=y +# CONFIG_REGPARM is not set + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_SOFTWARE_SUSPEND=y +CONFIG_PM_STD_PARTITION="" + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_VIDEO=y +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_ASUS is not set +# CONFIG_ACPI_IBM is not set +# CONFIG_ACPI_TOSHIBA is not set +# CONFIG_ACPI_CUSTOM_DSDT is not set +CONFIG_ACPI_BLACKLIST_YEAR=0 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +# CONFIG_X86_PM_TIMER is not set + +# +# APM (Advanced Power Management) BIOS Support +# +# CONFIG_APM is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +# CONFIG_PCI_MSI is not set +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PC-card bridges +# +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=m +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set +CONFIG_PNPACPI=y + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y +CONFIG_BLK_DEV_NBD=y +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_LBD=y +CONFIG_CDROM_PKTCDVD=y +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +CONFIG_BLK_DEV_CMD640=y +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_IDEPNP is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +CONFIG_BLK_DEV_SIS5513=y +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +# CONFIG_BLK_DEV_SD is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +CONFIG_I2O=y +CONFIG_I2O_CONFIG=y +CONFIG_I2O_BLOCK=y +# CONFIG_I2O_SCSI is not set +CONFIG_I2O_PROC=y + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_TUNNEL=y +CONFIG_IP_TCPDIAG=y +CONFIG_IP_TCPDIAG_IPV6=y + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_INET6_TUNNEL=y +CONFIG_IPV6_TUNNEL=y +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=y +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CONNTRACK_MARK=y +# CONFIG_IP_NF_CT_PROTO_SCTP is not set +CONFIG_IP_NF_FTP=y +CONFIG_IP_NF_IRC=y +CONFIG_IP_NF_TFTP=y +# CONFIG_IP_NF_AMANDA is not set +CONFIG_IP_NF_QUEUE=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_LIMIT=y +CONFIG_IP_NF_MATCH_IPRANGE=y +CONFIG_IP_NF_MATCH_MAC=y +CONFIG_IP_NF_MATCH_PKTTYPE=y +CONFIG_IP_NF_MATCH_MARK=y +CONFIG_IP_NF_MATCH_MULTIPORT=y +CONFIG_IP_NF_MATCH_TOS=y +CONFIG_IP_NF_MATCH_RECENT=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_DSCP=y +CONFIG_IP_NF_MATCH_AH_ESP=y +CONFIG_IP_NF_MATCH_LENGTH=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_MATCH_TCPMSS=y +CONFIG_IP_NF_MATCH_HELPER=y +CONFIG_IP_NF_MATCH_STATE=y +CONFIG_IP_NF_MATCH_CONNTRACK=y +CONFIG_IP_NF_MATCH_OWNER=y +CONFIG_IP_NF_MATCH_PHYSDEV=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_REALM=y +# CONFIG_IP_NF_MATCH_SCTP is not set +CONFIG_IP_NF_MATCH_COMMENT=y +CONFIG_IP_NF_MATCH_CONNMARK=y +CONFIG_IP_NF_MATCH_HASHLIMIT=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_IP_NF_TARGET_ULOG=y +CONFIG_IP_NF_TARGET_TCPMSS=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_SAME=y +# CONFIG_IP_NF_NAT_LOCAL is not set +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_IRC=y +CONFIG_IP_NF_NAT_FTP=y +CONFIG_IP_NF_NAT_TFTP=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_TOS=y +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_DSCP=y +CONFIG_IP_NF_TARGET_MARK=y +CONFIG_IP_NF_TARGET_CLASSIFY=y +CONFIG_IP_NF_TARGET_CONNMARK=y +CONFIG_IP_NF_TARGET_CLUSTERIP=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_TARGET_NOTRACK=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_IP6_NF_QUEUE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_LIMIT=y +CONFIG_IP6_NF_MATCH_MAC=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_MULTIPORT=y +CONFIG_IP6_NF_MATCH_OWNER=y +CONFIG_IP6_NF_MATCH_MARK=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_AHESP=y +CONFIG_IP6_NF_MATCH_LENGTH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_PHYSDEV=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_TARGET_MARK=y +CONFIG_IP6_NF_RAW=y + +# +# Bridge: Netfilter Configuration +# +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +CONFIG_ATM=y +# CONFIG_ATM_CLIP is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y +# CONFIG_DECNET is not set +CONFIG_LLC=y +CONFIG_LLC2=y +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_ATM=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_ROUTE4=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_CLS_EGP=y +CONFIG_EGP_DEBUG=y +CONFIG_NET_CLS_EGP_SIMPLE_CMP=y +CONFIG_NET_CLS_EGP_NBYTE=y +CONFIG_NET_CLS_EGP_KMP=y +CONFIG_NET_CLS_EGP_REGEXP=y +CONFIG_NET_CLS_EGP_CMD=y +CONFIG_EGP_CMD_BACK_TTL=4096 +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=y +CONFIG_NET_CLS_RSVP6=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_IPT=y +CONFIG_NET_ACT_PEDIT=y + +# +# Network testing +# +CONFIG_NET_PKTGEN=y +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_NET_SB1000 is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_CS89x0 is not set +# CONFIG_DGRS is not set +# CONFIG_EEPRO100 is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +CONFIG_8139TOO=y +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_OLD_RX_RESET is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPPOE is not set +# CONFIG_PPPOATM is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +CONFIG_SHAPER=y +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +CONFIG_I2C_SIS96X=y +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=y +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=y +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +# CONFIG_IBM_ASM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set +# CONFIG_VIDEO_SELECT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_SEQUENCER=y +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ISA devices +# +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_SGALAXY is not set +# CONFIG_SND_SSCAPE is not set + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=y +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS4281 is not set +CONFIG_SND_EMU10K1=y +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VX222 is not set + +# +# USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_USX2Y is not set + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_OHCI_HCD is not set +CONFIG_USB_UHCI_HCD=y + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_MIDI is not set +# CONFIG_USB_ACM is not set +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +CONFIG_USB_EGALAX=m +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +CONFIG_USB_CYTHERM=m +# CONFIG_USB_PHIDGETKIT is not set +CONFIG_USB_PHIDGETSERVO=m +# CONFIG_USB_TEST is not set + +# +# USB ATM/DSL drivers +# +# CONFIG_USB_ATM is not set +# CONFIG_USB_SPEEDTOUCH is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=y +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=y +CONFIG_EXPORTFS=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHEDSTATS is not set +CONFIG_DEBUG_SLAB=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +CONFIG_EARLY_PRINTK=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_KPROBES is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_4KSTACKS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y + +# +# Security options +# +# CONFIG_KEYS is not set +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=m +CONFIG_SECURITY_ROOTPLUG=m +# CONFIG_SECURITY_SECLVL is not set +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_WP512=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_SERPENT=y +CONFIG_CRYPTO_AES_586=y +CONFIG_CRYPTO_CAST5=y +CONFIG_CRYPTO_CAST6=y +CONFIG_CRYPTO_TEA=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_KHAZAD=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_TEST=y + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_PC=y diff --git a/testsuite/tests/policer b/testsuite/tests/policer new file mode 100644 index 0000000..eaf16ac --- /dev/null +++ b/testsuite/tests/policer @@ -0,0 +1,13 @@ +#!/bin/sh +$TC qdisc del dev $DEV root >/dev/null 2>&1 +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC filter add dev $DEV parent 10:0 protocol ip prio 10 u32 match ip protocol 1 0xff police rate 2kbit buffer 10k drop flowid 10:12 +$TC qdisc list dev $DEV +$TC filter list dev $DEV parent 10:0 +$TC qdisc del dev $DEV root +$TC qdisc list dev $DEV +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC filter add dev $DEV parent 10:0 protocol ip prio 10 u32 match ip protocol 1 0xff police rate 2kbit buffer 10k drop flowid 10:12 +$TC qdisc del dev $DEV root diff --git a/testsuite/tests/std-cbq b/testsuite/tests/std-cbq new file mode 100644 index 0000000..bff814b --- /dev/null +++ b/testsuite/tests/std-cbq @@ -0,0 +1,10 @@ +#!/bin/sh +$TC qdisc del dev $DEV root >/dev/null 2>&1 +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC qdisc list dev $DEV +$TC qdisc del dev $DEV root +$TC qdisc list dev $DEV +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC qdisc del dev $DEV root