This commit was generated by cvs2svn to compensate for changes in r1650,
authorMark Huang <mlhuang@cs.princeton.edu>
Wed, 22 Feb 2006 21:29:08 +0000 (21:29 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Wed, 22 Feb 2006 21:29:08 +0000 (21:29 +0000)
which included commits to RCS files with non-trunk default branches.

286 files changed:
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Config [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.kernel [new file with mode: 0644]
README [new file with mode: 0644]
README.decnet [new file with mode: 0644]
README.distribution [new file with mode: 0644]
README.iproute2+tc [new file with mode: 0644]
README.lnstat [new file with mode: 0644]
RELNOTES [new file with mode: 0644]
configure [new file with mode: 0755]
doc/Makefile [new file with mode: 0644]
doc/Plan [new file with mode: 0644]
doc/SNAPSHOT.tex [new file with mode: 0644]
doc/actions/gact-usage [new file with mode: 0644]
doc/actions/mirred-usage [new file with mode: 0644]
doc/api-ip6-flowlabels.tex [new file with mode: 0644]
doc/arpd.sgml [new file with mode: 0644]
doc/do-psnup [new file with mode: 0755]
doc/ip-cref.tex [new file with mode: 0644]
doc/ip-tunnels.tex [new file with mode: 0644]
doc/nstat.sgml [new file with mode: 0644]
doc/preamble.tex [new file with mode: 0644]
doc/rtstat.sgml [new file with mode: 0644]
doc/ss.sgml [new file with mode: 0644]
etc/iproute2/rt_dsfield [new file with mode: 0644]
etc/iproute2/rt_dsfield.rt_config [new file with mode: 0644]
etc/iproute2/rt_protos [new file with mode: 0644]
etc/iproute2/rt_protos.rt_config [new file with mode: 0644]
etc/iproute2/rt_realms [new file with mode: 0644]
etc/iproute2/rt_realms.rt_config [new file with mode: 0644]
etc/iproute2/rt_scopes [new file with mode: 0644]
etc/iproute2/rt_scopes.rt_config [new file with mode: 0644]
etc/iproute2/rt_tables [new file with mode: 0644]
etc/iproute2/rt_tables.rt_config [new file with mode: 0644]
examples/SYN-DoS.rate.limit [new file with mode: 0644]
examples/cbqinit.eth1 [new file with mode: 0755]
examples/dhcp-client-script [new file with mode: 0755]
examples/diffserv/Edge1 [new file with mode: 0644]
examples/diffserv/Edge2 [new file with mode: 0644]
examples/diffserv/Edge31-ca-u32 [new file with mode: 0644]
examples/diffserv/Edge31-cb-chains [new file with mode: 0644]
examples/diffserv/Edge32-ca-u32 [new file with mode: 0644]
examples/diffserv/Edge32-cb-chains [new file with mode: 0644]
examples/diffserv/Edge32-cb-u32 [new file with mode: 0644]
examples/diffserv/README [new file with mode: 0644]
examples/diffserv/afcbq [new file with mode: 0644]
examples/diffserv/ef-prio [new file with mode: 0644]
examples/diffserv/efcbq [new file with mode: 0644]
examples/diffserv/regression-testing [new file with mode: 0644]
include/SNAPSHOT.h [new file with mode: 0644]
include/ip6tables.h [new file with mode: 0644]
include/iptables.h [new file with mode: 0644]
include/iptables_common.h [new file with mode: 0644]
include/libiptc/ipt_kernel_headers.h [new file with mode: 0644]
include/libiptc/libip6tc.h [new file with mode: 0644]
include/libiptc/libiptc.h [new file with mode: 0644]
include/libnetlink.h [new file with mode: 0644]
include/linux/gen_stats.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ip_tables.h [new file with mode: 0644]
include/linux/netlink.h [new file with mode: 0644]
include/linux/pkt_cls.h [new file with mode: 0644]
include/linux/pkt_sched.h [new file with mode: 0644]
include/linux/rtnetlink.h [new file with mode: 0644]
include/linux/tc_act/tc_gact.h [new file with mode: 0644]
include/linux/tc_act/tc_ipt.h [new file with mode: 0644]
include/linux/tc_act/tc_mirred.h [new file with mode: 0644]
include/linux/tc_act/tc_pedit.h [new file with mode: 0644]
include/linux/tcp.h [new file with mode: 0644]
include/linux/tcp_diag.h [new file with mode: 0644]
include/linux/xfrm.h [new file with mode: 0644]
include/ll_map.h [new file with mode: 0644]
include/rt_names.h [new file with mode: 0644]
include/rtm_map.h [new file with mode: 0644]
include/utils.h [new file with mode: 0644]
ip/Makefile [new file with mode: 0644]
ip/ifcfg [new file with mode: 0755]
ip/ip [new file with mode: 0755]
ip/ip.c [new file with mode: 0644]
ip/ip.o [new file with mode: 0644]
ip/ip_common.h [new file with mode: 0644]
ip/ipaddress.c [new file with mode: 0644]
ip/ipaddress.o [new file with mode: 0644]
ip/iplink.c [new file with mode: 0644]
ip/iplink.o [new file with mode: 0644]
ip/ipmaddr.c [new file with mode: 0644]
ip/ipmaddr.o [new file with mode: 0644]
ip/ipmonitor.c [new file with mode: 0644]
ip/ipmonitor.o [new file with mode: 0644]
ip/ipmroute.c [new file with mode: 0644]
ip/ipmroute.o [new file with mode: 0644]
ip/ipneigh.c [new file with mode: 0644]
ip/ipneigh.o [new file with mode: 0644]
ip/ipprefix.c [new file with mode: 0644]
ip/ipprefix.o [new file with mode: 0644]
ip/iproute.c [new file with mode: 0644]
ip/iproute.c.initvar [new file with mode: 0644]
ip/iproute.o [new file with mode: 0644]
ip/iprule.c [new file with mode: 0644]
ip/iprule.o [new file with mode: 0644]
ip/iptunnel.c [new file with mode: 0644]
ip/iptunnel.o [new file with mode: 0644]
ip/ipxfrm.c [new file with mode: 0644]
ip/ipxfrm.o [new file with mode: 0644]
ip/routef [new file with mode: 0755]
ip/routel [new file with mode: 0755]
ip/rtm_map.c [new file with mode: 0644]
ip/rtm_map.o [new file with mode: 0644]
ip/rtmon [new file with mode: 0755]
ip/rtmon.c [new file with mode: 0644]
ip/rtmon.o [new file with mode: 0644]
ip/rtpr [new file with mode: 0755]
ip/xfrm.h [new file with mode: 0644]
ip/xfrm_policy.c [new file with mode: 0644]
ip/xfrm_policy.o [new file with mode: 0644]
ip/xfrm_state.c [new file with mode: 0644]
ip/xfrm_state.o [new file with mode: 0644]
lib/Makefile [new file with mode: 0644]
lib/dnet_ntop.c [new file with mode: 0644]
lib/dnet_ntop.o [new file with mode: 0644]
lib/dnet_pton.c [new file with mode: 0644]
lib/dnet_pton.o [new file with mode: 0644]
lib/inet_proto.c [new file with mode: 0644]
lib/inet_proto.o [new file with mode: 0644]
lib/ipx_ntop.c [new file with mode: 0644]
lib/ipx_ntop.o [new file with mode: 0644]
lib/ipx_pton.c [new file with mode: 0644]
lib/ipx_pton.o [new file with mode: 0644]
lib/libnetlink.a [new file with mode: 0644]
lib/libnetlink.c [new file with mode: 0644]
lib/libnetlink.o [new file with mode: 0644]
lib/libutil.a [new file with mode: 0644]
lib/ll_addr.c [new file with mode: 0644]
lib/ll_addr.o [new file with mode: 0644]
lib/ll_map.c [new file with mode: 0644]
lib/ll_map.o [new file with mode: 0644]
lib/ll_proto.c [new file with mode: 0644]
lib/ll_proto.o [new file with mode: 0644]
lib/ll_types.c [new file with mode: 0644]
lib/ll_types.o [new file with mode: 0644]
lib/rt_names.c [new file with mode: 0644]
lib/rt_names.o [new file with mode: 0644]
lib/utils.c [new file with mode: 0644]
lib/utils.o [new file with mode: 0644]
man/man3/libnetlink.3 [new file with mode: 0644]
man/man8/ip.8 [new file with mode: 0644]
man/man8/tc-cbq-details.8 [new file with mode: 0644]
man/man8/tc-cbq.8 [new file with mode: 0644]
man/man8/tc-htb.8 [new file with mode: 0644]
man/man8/tc-pbfifo.8 [new file with mode: 0644]
man/man8/tc-pfifo_fast.8 [new file with mode: 0644]
man/man8/tc-prio.8 [new file with mode: 0644]
man/man8/tc-red.8 [new file with mode: 0644]
man/man8/tc-sfq.8 [new file with mode: 0644]
man/man8/tc-tbf.8 [new file with mode: 0644]
man/man8/tc.8 [new file with mode: 0644]
misc/Makefile [new file with mode: 0644]
misc/arpd [new file with mode: 0755]
misc/arpd.c [new file with mode: 0644]
misc/ifstat [new file with mode: 0755]
misc/ifstat.c [new file with mode: 0644]
misc/lnstat [new file with mode: 0755]
misc/lnstat.c [new file with mode: 0644]
misc/lnstat.h [new file with mode: 0644]
misc/lnstat.o [new file with mode: 0644]
misc/lnstat_util.c [new file with mode: 0644]
misc/lnstat_util.o [new file with mode: 0644]
misc/netbug [new file with mode: 0755]
misc/nstat [new file with mode: 0755]
misc/nstat.c [new file with mode: 0644]
misc/rtacct [new file with mode: 0755]
misc/rtacct.c [new file with mode: 0644]
misc/ss [new file with mode: 0755]
misc/ss.c [new file with mode: 0644]
misc/ss.o [new file with mode: 0644]
misc/ssfilter.c [new file with mode: 0644]
misc/ssfilter.h [new file with mode: 0644]
misc/ssfilter.o [new file with mode: 0644]
misc/ssfilter.y [new file with mode: 0644]
netem/Makefile [new file with mode: 0644]
netem/README.distribution [new file with mode: 0644]
netem/experimental.dat [new file with mode: 0644]
netem/experimental.dist [new file with mode: 0644]
netem/maketable [new file with mode: 0755]
netem/maketable.c [new file with mode: 0644]
netem/normal [new file with mode: 0755]
netem/normal.c [new file with mode: 0644]
netem/normal.dist [new file with mode: 0644]
netem/pareto [new file with mode: 0755]
netem/pareto.c [new file with mode: 0644]
netem/pareto.dist [new file with mode: 0644]
netem/paretonormal [new file with mode: 0755]
netem/paretonormal.c [new file with mode: 0644]
netem/paretonormal.dist [new file with mode: 0644]
tc/Makefile [new file with mode: 0644]
tc/README.last [new file with mode: 0644]
tc/f_fw.c [new file with mode: 0644]
tc/f_fw.o [new file with mode: 0644]
tc/f_route.c [new file with mode: 0644]
tc/f_route.o [new file with mode: 0644]
tc/f_rsvp.c [new file with mode: 0644]
tc/f_rsvp.o [new file with mode: 0644]
tc/f_tcindex.c [new file with mode: 0644]
tc/f_tcindex.o [new file with mode: 0644]
tc/f_u32.c [new file with mode: 0644]
tc/f_u32.o [new file with mode: 0644]
tc/libtc.a [new file with mode: 0644]
tc/m_action.c [new file with mode: 0644]
tc/m_action.o [new file with mode: 0644]
tc/m_estimator.c [new file with mode: 0644]
tc/m_estimator.o [new file with mode: 0644]
tc/m_gact.c [new file with mode: 0644]
tc/m_gact.o [new file with mode: 0644]
tc/m_ipt.c [new file with mode: 0644]
tc/m_ipt.o [new file with mode: 0644]
tc/m_mirred.c [new file with mode: 0644]
tc/m_mirred.o [new file with mode: 0644]
tc/m_pedit.c [new file with mode: 0644]
tc/m_pedit.h [new file with mode: 0644]
tc/m_pedit.o [new file with mode: 0644]
tc/m_police.c [new file with mode: 0644]
tc/m_police.o [new file with mode: 0644]
tc/p_icmp.c [new file with mode: 0644]
tc/p_icmp.o [new file with mode: 0644]
tc/p_ip.c [new file with mode: 0644]
tc/p_ip.o [new file with mode: 0644]
tc/p_tcp.c [new file with mode: 0644]
tc/p_tcp.o [new file with mode: 0644]
tc/p_udp.c [new file with mode: 0644]
tc/p_udp.o [new file with mode: 0644]
tc/q_atm.c [new file with mode: 0644]
tc/q_cbq.c [new file with mode: 0644]
tc/q_cbq.o [new file with mode: 0644]
tc/q_dsmark.c [new file with mode: 0644]
tc/q_dsmark.o [new file with mode: 0644]
tc/q_fifo.c [new file with mode: 0644]
tc/q_fifo.o [new file with mode: 0644]
tc/q_gred.c [new file with mode: 0644]
tc/q_gred.o [new file with mode: 0644]
tc/q_hfsc.c [new file with mode: 0644]
tc/q_hfsc.o [new file with mode: 0644]
tc/q_htb.c [new file with mode: 0644]
tc/q_htb.o [new file with mode: 0644]
tc/q_ingress.c [new file with mode: 0644]
tc/q_ingress.o [new file with mode: 0644]
tc/q_netem.c [new file with mode: 0644]
tc/q_netem.so [new file with mode: 0755]
tc/q_prio.c [new file with mode: 0644]
tc/q_prio.o [new file with mode: 0644]
tc/q_red.c [new file with mode: 0644]
tc/q_red.o [new file with mode: 0644]
tc/q_sfq.c [new file with mode: 0644]
tc/q_sfq.o [new file with mode: 0644]
tc/q_tbf.c [new file with mode: 0644]
tc/q_tbf.o [new file with mode: 0644]
tc/tc [new file with mode: 0755]
tc/tc.c [new file with mode: 0644]
tc/tc.o [new file with mode: 0644]
tc/tc_cbq.c [new file with mode: 0644]
tc/tc_cbq.h [new file with mode: 0644]
tc/tc_cbq.o [new file with mode: 0644]
tc/tc_class.c [new file with mode: 0644]
tc/tc_class.o [new file with mode: 0644]
tc/tc_common.h [new file with mode: 0644]
tc/tc_core.c [new file with mode: 0644]
tc/tc_core.h [new file with mode: 0644]
tc/tc_core.o [new file with mode: 0644]
tc/tc_estimator.c [new file with mode: 0644]
tc/tc_estimator.o [new file with mode: 0644]
tc/tc_filter.c [new file with mode: 0644]
tc/tc_filter.o [new file with mode: 0644]
tc/tc_qdisc.c [new file with mode: 0644]
tc/tc_qdisc.o [new file with mode: 0644]
tc/tc_red.c [new file with mode: 0644]
tc/tc_red.h [new file with mode: 0644]
tc/tc_red.o [new file with mode: 0644]
tc/tc_util.c [new file with mode: 0644]
tc/tc_util.h [new file with mode: 0644]
tc/tc_util.o [new file with mode: 0644]
testsuite/Makefile [new file with mode: 0644]
testsuite/configs/all-2.4 [new file with mode: 0644]
testsuite/configs/all-no-act [new file with mode: 0644]
testsuite/configs/all-police-act [new file with mode: 0644]
testsuite/tests/policer [new file with mode: 0644]
testsuite/tests/std-cbq [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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 (file)
index 0000000..53bd530
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,332 @@
+2005-03-14  Stephen Hemminger  <shemminger@osdl.org>
+
+       * cleanup batch mode, allow continuation, comments etc.
+       * recode reuse of netlink socket
+
+2005-03-14  Boian Bonev <boian@bonev.com>
+       
+       * 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 <tgraf@suug.ch>
+       
+       * ip link command
+         print NO-CARRIER flag if there is no carrier and the link is up.
+
+2005-03-14  Patrick McHardy <kaber@trash.net>
+
+       * bug: Use USER_HZ where necessary
+
+2005-03-10  Jamal Hadi Salim <hadi@znyx.com>
+
+       * Fix bug with register_target
+
+2005-03-10  Stephen Hemminger  <shemminger@osdl.org>
+
+       * 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 <nix@esperi.org.uk>
+
+       * make man3 directory
+       
+2005-03-10 Pasi <Pasi.Eronen@nokia.com>
+       
+       * add ESP-in-UDP encapsulation
+
+2005-03-10 Thomas Graf <tgraf@suug.ch>
+       * [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 <herbert@gondor.apana.org.au>
+       * Trivial typo in ip help
+
+2005-02-09  Stephen Hemminger  <shemminger@osdl.org>
+
+       * netem distribution data reorganization
+
+2005-02-09  Roland Dreier <roland@topspin.com>
+
+       * ip over infiniband address display
+
+2005-02-09  Jim Gifford <lfs@jg555.com>
+
+       * make install fix for ip/
+
+2005-02-07 Mads Martin Joergensen <mmj@suse.de>
+       
+       * Don't mix address families when flushing      
+       
+2005-02-07  Stephen Hemminger  <shemminger@osdl.org>
+
+       * Validate classid is not too large to cause loss of bits.
+
+2005-02-07 Jean-Marc Ranger <jmranger@sympatico.ca>
+
+       * need to call getline() with null for first usage
+       * don't overwrite const arg
+
+2005-02-07  Stephen Hemminger  <shemminger@linux.site>
+
+       * Add experimental distribution
+
+2005-01-18  Yun Mao <maoy@cis.upenn.edu>
+
+       * typo in ss
+
+2005-01-18  Thomas Graf <tgraf@suug.ch>
+       
+       * tc pedit/action cleanups
+       * add addraw_l
+       * rtattr_parse cleanups
+
+2005-01-17  Jamal Hadi Salim <hadi@znyx.com>
+
+       * typo in m_mirred
+       * add support for pedit
+
+2005-01-13  Jim Gifford <lfs@jg555.com>
+       
+       * Fix allocation size error in nomal and paretonormal generation
+         programs.
+
+2005-01-12  Masahide Nakamura <nakam@linux-ipv6.org>
+       
+       * ipmonitor shows IPv6 prefix list notification
+       * update to iproute2 xfrm for ipv6      
+       
+2005-01-12  Stephen Hemminger  <shemminger@osdl.org>
+
+       * Fix compile warnings when building 64bit system since
+         u64 is unsigned long, but format is %llu
+
+2005-01-12  "Catalin(ux aka Dino) BOIE" <util@deuroconsult.ro>
+
+       * Add the possibility to use fwmark in u32 filters
+       
+2005-01-12  Andi Kleen <ak@suse.de>
+
+       * Add netlink manual page
+
+2004-10-20  Stephen Hemminger  <shemminger@osdl.org>
+       
+       * Add warning about "ip route nat" no longer supported
+
+2005-01-12  Thomas Graf <tgraf@suug.ch>
+
+       * Tc testsuite
+
+2005-01-12  Jamal Hadi Salim <hadi@znyx.com>
+
+       * Add iptables tc support. This meant borrowing headers
+         from iptables *ugh*
+
+2004-12-08  Jamal Hadi Salim <hadi@znyx.com>
+
+       * Add mirror and redirect actions
+
+2004-10-20  Stephen Hemminger  <shemminger@osdl.org>
+
+       * Don't include <asm/byteorder.h> 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 <laforge@gnumonks.org>
+
+       * Replace rtstat (and ctstat) with new lnstat
+
+2004-10-19  Mads Martin Joergensen <mmj@suse.de>
+
+       * Ip is using the wrong structure in ipaddress.c when showing stats
+       * Make sure no buffer overflow in nstat
+
+2004-10-19  Michal <md@lnet.pl>
+
+       * fix scaling in print_rates (for bits)
+
+2004-09-28  Stephen Hemminger  <shemminger@osdl.org>
+
+       * fix build problems with arpd and pthread
+       * add pkt_sched.h
+
+2004-09-28  Mike Frysinger <vapier@gentoo.org>
+       
+       * make man8 directory
+       * install ifcfg and rtpr scripts
+
+2004-09-28  Andreas Haumer <andreas@xss.co.at>
+
+       * make install symlink fix.
+
+2004-09-28  Masahide Nakamura <nakam@linux-ipv6.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * Fix ip command to not crash when interface name is too long.
+         always use strncpy(.., IFNAMSIZ)
+
+2004-08-31  Stephen Hemminger  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * Add xfrm message formatting from
+         Masahide Nakamura <nakam@linux-ipv6.org>
+
+2004-08-09  Stephen Hemminger  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * Add support for vegas info to ss
+
+2004-06-02  Stephen Hemminger  <shemminger@osdl.org>
+
+       * 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  <shemminger@osdl.org>
+
+       * Add the delay (network simulation scheduler)
+
+2004-04-15  Stephen Hemminger  <shemminger@osdl.org>
+
+       * Starting point baseline based on iproute2-2.4.7-ss020116-try
+
diff --git a/Config b/Config
new file mode 100644 (file)
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 (file)
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 (file)
index 0000000..abd5aab
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..4d7453a
--- /dev/null
@@ -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 <SteveW@ACM.org>
+
diff --git a/README.distribution b/README.distribution
new file mode 100644 (file)
index 0000000..fe78fb4
--- /dev/null
@@ -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 (file)
index 0000000..edd79c0
--- /dev/null
@@ -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 (file)
index 0000000..057925f
--- /dev/null
@@ -0,0 +1,81 @@
+lnstat - linux networking statistics
+(C) 2004 Harald Welte <laforge@gnumonks.org
+======================================================================
+
+This tool is a generalized and more feature-complete replacement for the old
+'rtstat' program.
+
+In addition to routing cache statistics, it supports any kind of statistics
+the linux kernel exports via a file in /proc/net/stat.  In a stock 2.6.9
+kernel, this is 
+       per-protocol neighbour cache statistics 
+               (ipv4, ipv6, atm, decnet)
+       routing cache statistics
+               (ipv4)
+       connection tracking statistics
+               (ipv4)
+
+Please note that lnstat will adopt to any additional statistics that might be
+added to the kernel at some later point
+
+I personally always like examples more than any reference documentation, so I
+list the following examples.  If somebody wants to do a manpage, feel free
+to send me a patch :)
+
+EXAMPLES:
+
+In order to get a list of supported statistics files, you can run
+
+       lnstat -d
+
+It will display something like
+/proc/net/stat/arp_cache:
+         1: entries
+         2: allocs
+         3: destroys
+[...]
+/proc/net/stat/rt_cache:
+         1: entries
+         2: in_hit
+         3: in_slow_tot
+
+You can now select the files/keys you are interested by something like
+
+       lnstat -k arp_cache:entries,rt_cache:in_hit,arp_cache:destroys
+
+arp_cach|rt_cache|arp_cach|
+ entries|  in_hit|destroys|
+       6|       6|       0|
+       6|       0|       0|
+       6|       2|       0|
+
+
+You can specify the interval (e.g. 10 seconds) by:
+       
+       lnstat -i 10
+
+You can specify to only use one particular statistics file:
+
+       lnstat -f ip_conntrack
+
+You can specify individual field widths 
+
+       lnstat -k arp_cache:entries,rt_cache:entries -w 20,8
+
+You can specify not to print a header at all
+       
+       lnstat -s 0
+
+You can specify to print a header only at start of the program
+
+       lnstat -s 1
+
+You can specify to print a header at start and every 20 lines:
+
+       lnstat -s 20
+
+You can specify the number of samples you want to take (e.g. 5):
+       
+       lnstat -c 5
+
diff --git a/RELNOTES b/RELNOTES
new file mode 100644 (file)
index 0000000..17f0011
--- /dev/null
+++ b/RELNOTES
@@ -0,0 +1,168 @@
+[020116]
+! 1. Compile with rh-7.2
+! 2. What the hell some people blame on socklen_t defined in unistd.h? Check.
+ * Kim Woelders <kim@woelders.dk>, 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 <kad@blackcatlinux.com>: various flaws in ss
+ * Alexandr D. Kanevskiy <kad@blackcatlinux.com>: 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" <babydr@baby-dragons.com>
+   doc: option to produce ps output for non-a4 and not only 2 pages/sheet. 
+ * Jamal's patch for ingres qdisc.
+ * Bernd Eckenfels <ecki@lina.inka.de>: deleted orphaned bogus #include
+   in include/utils.h.
+ * Julian Anastasov <ja@ssi.bg>: 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 <ahu@ds9a.nl> who raised the issue in netdev.
+   Thanks and apologies to Terry Schmidt <terry@nycwireless.net>,
+   Ruben Puettmann <ruben.puettmann@freenet-ag.de>,
+   Mark Ivens <mivens@clara.net>.
+ * willy tarreau <wtarreau@yahoo.fr>: "make install" target.
+ * Tunable limit for sch_sfq. Patch to kernel activating this
+   is about to be submitted. Reminded by Adi Nugroho <Adi@iNterNUX.co.id>.
+
+[010824]
+ * ip address add sets scope of loopback addreses to "host".
+   Advised by David Miller.
+ * ZIP! <zip@killerlabs.com> and David Ford <david@blue-labs.org>
+   Some strcpy's changed to strncpy's.
+ * David Ford <david@blue-labs.org>, test for compilation with gcc3.
+ * David Ford <david@blue-labs.org>. 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 <kunihiro@zebra.org>.
+ * Rafal Maszkowski <rzm@icm.edu.pl>, batch mode tc. The most old patch.
+ * Updates list of data protocol ids.
+   Lots of reporters. I bring my apologies.
+ * Jan Rekorajski <baggins@sith.mimuw.edu.pl>. Updated list of datalink types. 
+ * Christina Chen <chenchristina@cwc.nus.edu.sg>. Bug in parsing IPv6 address match in u32. 
+ * Pekka Savola <pekkas@netcore.fi>. ip -6 route flush dev lo stuck
+   on deleting root of the table.
+ * Werner. dsmark fixes.
+ * Alexander Demenshin <aldem-reply@aldem.net>. Old miracleous bug
+   in ip monitor. It was puzzle, people permanently blame that
+   it prints some crap.
+ * Rui Prior <rprior@inescporto.pt>. f_route failed to resolve fromif.
+   Werner also noticed this and sent patch. Bad place... [RETHINK]
+ * Kim Woelders <kim@woelders.dk>. 
+   - 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 <berndj@prism.co.za>. Some sanitizations of tc.c
+!* Marian Jancar <marian.jancar@infonet.cz>. He say q_tbf prints wrong latency!
+!  Seems, he is wrong.
+ * Werner (and Nikolai Vladychevski <niko@isl.net.mx>) 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 <raf2@zip.com.au>.
+  * RTAX_REORDERING support.
+  * IFLA_MASTER support.
+  * Bug in rtnl_talk(), libnetlink.c. Reported by David P. Olshfski
+       <olshef@us.ibm.com>
+
+[000305]
+  * Bugs in RESOLVE_HOSTNAMES. Bratislav Ilich <bilik@@zepter.ru>
+  * 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 <vlad@alis.tusur.ru>
+  * "ip rule" parsed >INT_MAX values of metric incorrectly.
+      Matthew G. Marsh <mgm@paktronix.com>
+  * 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 <almesber@lrc.di.epfl.ch>
+       Jamal Hadi Salim <hadi@nortelnetworks.com> 
+  * DECnet support.
+       Steve Whitehouse <SteveW@ACM.org>
+  * Some minor tweaks in docs and code.
+
+[990530]
+  * routel script. Stephen R. van den Berg <srb@cuci.nl>
+  * Bug in tc/q_prio.c resetting priomap. Reported by
+       Ole Husgaard <sparre@login.dknet.dk> and
+       Jan Kasprzak <kas@informatics.muni.cz>
+  * 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 <karn@ka9q.ampr.org>
+  * bug in tc/q_tbf.c preventing setting peak_rate, Martin Mares <mj@ucw.cz>
+  * 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 <rani@magic.metawire.com> 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 (executable)
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 <<EOF
+#include <atm.h>
+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 (file)
index 0000000..84836a2
--- /dev/null
@@ -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) $< </dev/null 2>&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 (file)
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 (file)
index 0000000..7ed0298
--- /dev/null
@@ -0,0 +1 @@
+\def\Draft{020116}
diff --git a/doc/actions/gact-usage b/doc/actions/gact-usage
new file mode 100644 (file)
index 0000000..de1308d
--- /dev/null
@@ -0,0 +1,79 @@
+
+gact <ACTION> [RAND] [INDEX]
+
+Where: 
+       ACTION := reclassify | drop | continue | pass | ok 
+       RAND := random <RANDTYPE> <ACTION> <VAL>
+       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 (file)
index 0000000..3e135a0
--- /dev/null
@@ -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 <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> 
+where: 
+DIRECTION := <ingress | egress>
+ACTION := <mirror | redirect>
+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 (file)
index 0000000..aa34e94
--- /dev/null
@@ -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 <sys/socket.h>
+#include <netinet/in6.h>
+
+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 <Tspec>
+\end{verbatim}
+
+Receiver makes reservation with command:
+\begin{verbatim}
+RTAP> reserve ff 3ffe:2400::1/FL0xA1234 <Flowspec>
+\end{verbatim}
+
+\end{document}
diff --git a/doc/arpd.sgml b/doc/arpd.sgml
new file mode 100644 (file)
index 0000000..0ab79c6
--- /dev/null
@@ -0,0 +1,130 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<title>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 (executable)
index 0000000..2dce848
--- /dev/null
@@ -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 (file)
index 0000000..5eaa4a8
--- /dev/null
@@ -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 (file)
index 0000000..0a8c930
--- /dev/null
@@ -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 (file)
index 0000000..be9d8bc
--- /dev/null
@@ -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 (file)
index 0000000..80ca508
--- /dev/null
@@ -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 (file)
index 0000000..07391c3
--- /dev/null
@@ -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 (file)
index 0000000..0b1b533
--- /dev/null
@@ -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/&amp/, <tt/&amp&amp/, <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 &gt= :1024
+      dport != :22
+      sport &lt :32000
+</verb></tscreen>
+      etc.
+
+      All the relations: <tt/&lt/, <tt/&gt/, <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/&amp/,
+<tt/&amp&amp/.
+
+<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/&lt=/, <tt/&gt=/, <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 (file)
index 0000000..2b36e49
--- /dev/null
@@ -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 (file)
index 0000000..110061a
--- /dev/null
@@ -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 (file)
index 0000000..2569edf
--- /dev/null
@@ -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 (file)
index 0000000..8c985d7
--- /dev/null
@@ -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 (file)
index 0000000..332179d
--- /dev/null
@@ -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 (file)
index 0000000..eedd76d
--- /dev/null
@@ -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 (file)
index 0000000..36fbc01
--- /dev/null
@@ -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 (file)
index 0000000..8514bc1
--- /dev/null
@@ -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 (file)
index 0000000..558716b
--- /dev/null
@@ -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 (file)
index 0000000..541abfd
--- /dev/null
@@ -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 (file)
index 0000000..8766b67
--- /dev/null
@@ -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 (executable)
index 0000000..226ec1c
--- /dev/null
@@ -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 (executable)
index 0000000..7207b57
--- /dev/null
@@ -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 (file)
index 0000000..4ddffdd
--- /dev/null
@@ -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 (file)
index 0000000..2f78da2
--- /dev/null
@@ -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 (file)
index 0000000..25e6c0b
--- /dev/null
@@ -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 (file)
index 0000000..d7faae9
--- /dev/null
@@ -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 (file)
index 0000000..edf21e4
--- /dev/null
@@ -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 (file)
index 0000000..804fad1
--- /dev/null
@@ -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 (file)
index 0000000..cc2ebb4
--- /dev/null
@@ -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 (file)
index 0000000..ec91d63
--- /dev/null
@@ -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 (file)
index 0000000..10d6d93
--- /dev/null
@@ -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 (file)
index 0000000..48611bd
--- /dev/null
@@ -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 (file)
index 0000000..bcc437b
--- /dev/null
@@ -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 (file)
index 0000000..0ec705c
--- /dev/null
@@ -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 (file)
index 0000000..8375d76
--- /dev/null
@@ -0,0 +1 @@
+static char SNAPSHOT[] = "050314";
diff --git a/include/ip6tables.h b/include/ip6tables.h
new file mode 100644 (file)
index 0000000..8360617
--- /dev/null
@@ -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 (file)
index 0000000..5aca69a
--- /dev/null
@@ -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 (file)
index 0000000..e3b99aa
--- /dev/null
@@ -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 (file)
index 0000000..18861fe
--- /dev/null
@@ -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 (file)
index 0000000..7a247c4
--- /dev/null
@@ -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 (file)
index 0000000..7628bda
--- /dev/null
@@ -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 (file)
index 0000000..63cc3c8
--- /dev/null
@@ -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 (file)
index 0000000..13f4e74
--- /dev/null
@@ -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 (file)
index 0000000..7346ead
--- /dev/null
@@ -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 (file)
index 0000000..13828e5
--- /dev/null
@@ -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 (file)
index 0000000..741d15b
--- /dev/null
@@ -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 (file)
index 0000000..73d84c0
--- /dev/null
@@ -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 (file)
index 0000000..1facfe9
--- /dev/null
@@ -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 (file)
index 0000000..23a03eb
--- /dev/null
@@ -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 (file)
index 0000000..4b6f7b6
--- /dev/null
@@ -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 (file)
index 0000000..71d6340
--- /dev/null
@@ -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 (file)
index 0000000..83e56e3
--- /dev/null
@@ -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 (file)
index 0000000..9703d6b
--- /dev/null
@@ -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 (file)
index 0000000..ceee962
--- /dev/null
@@ -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 (file)
index 0000000..f0df02a
--- /dev/null
@@ -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 (file)
index 0000000..3bff5e9
--- /dev/null
@@ -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 (file)
index 0000000..249231e
--- /dev/null
@@ -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 (file)
index 0000000..70bda7d
--- /dev/null
@@ -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 (file)
index 0000000..906e394
--- /dev/null
@@ -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 (file)
index 0000000..bcc419b
--- /dev/null
@@ -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 (executable)
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 (executable)
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 (file)
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 (file)
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 (file)
index 0000000..688d384
--- /dev/null
@@ -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 (file)
index 0000000..92f0089
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..520280e
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..1cdab0b
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..cdaeb6f
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..b24caee
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..e8ab291
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..61d12f9
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..1e23e49
--- /dev/null
@@ -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 (file)
index 0000000..b2ddb6e
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..764edc8
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..2da3df1
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..fc0f0d9
--- /dev/null
@@ -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 (file)
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 (executable)
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 (executable)
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 (file)
index 0000000..21e818b
--- /dev/null
@@ -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 (file)
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 (executable)
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 (file)
index 0000000..5ce7731
--- /dev/null
@@ -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 (file)
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 (executable)
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 (file)
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 (file)
index 0000000..c1331a4
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..b5b6214
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..bc270bf
--- /dev/null
@@ -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 (file)
index 0000000..9500df8
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..bd7727a
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..a55e0e7
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..b2d6790
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..1a52b7f
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..4cd2b2a
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..ea3d660
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..89c0d20
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..98c67fe
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..5b0f106
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..03df086
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..73ce865
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..145f38d
--- /dev/null
@@ -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 (file)
index 0000000..cca6d1c
--- /dev/null
@@ -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 (file)
index 0000000..e47da62
--- /dev/null
@@ -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 (file)
index 0000000..79fb93b
--- /dev/null
@@ -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 (file)
index 0000000..f61b818
--- /dev/null
@@ -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 (file)
index 0000000..8dda4bb
--- /dev/null
@@ -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 (file)
index 0000000..43ab166
--- /dev/null
@@ -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 (file)
index 0000000..e942e62
--- /dev/null
@@ -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 (file)
index 0000000..d02b411
--- /dev/null
@@ -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 (file)
index 0000000..337c795
--- /dev/null
@@ -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 (file)
index 0000000..3abb238
--- /dev/null
@@ -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 (file)
index 0000000..b9b8039
--- /dev/null
@@ -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 (file)
index 0000000..2ddf950
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..85b2a1c
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..1379a81
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..03e6f3f
--- /dev/null
@@ -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 (file)
index 0000000..06774ab
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..6ff3779
--- /dev/null
@@ -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 (file)
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 (executable)
index 0000000..6d13c8e
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..f2887ec
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..5c6748b
--- /dev/null
@@ -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 (executable)
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 (file)
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(&current_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(&current_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(&current_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(&current_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(&current_filter);
+       if (current_filter.dbs & PACKET_DBM)
+               packet_show(&current_filter);
+       if (current_filter.dbs & UNIX_DBM)
+               unix_show(&current_filter);
+       if (current_filter.dbs & (1<<RAW_DB))
+               raw_show(&current_filter);
+       if (current_filter.dbs & (1<<UDP_DB))
+               udp_show(&current_filter);
+       if (current_filter.dbs & (1<<TCP_DB))
+               tcp_show(&current_filter);
+       return 0;
+}
diff --git a/misc/ss.o b/misc/ss.o
new file mode 100644 (file)
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 (file)
index 0000000..fa73a87
--- /dev/null
@@ -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
+
+\f
+
+#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 */
+
+\f
+
+#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;
+    }
+}
+\f
+
+/* 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"
+\f
+  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 (file)
index 0000000..00b92e3
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..8be5368
--- /dev/null
@@ -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 (file)
index 0000000..881ed9f
--- /dev/null
@@ -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 (file)
index 0000000..23f7ecb
--- /dev/null
@@ -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 (file)
index 0000000..3663a3e
--- /dev/null
@@ -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 (file)
index 0000000..025c198
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..ce09176
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..e6683db
--- /dev/null
@@ -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 (file)
index 0000000..8cfe768
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..8aa647b
--- /dev/null
@@ -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 (file)
index 0000000..295e6fb
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..c793df6
--- /dev/null
@@ -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 (file)
index 0000000..61be1b8
--- /dev/null
@@ -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 (file)
index 0000000..06546f9
--- /dev/null
@@ -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 (file)
index 0000000..9400438
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..a41b9d5
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..13fcf97
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..39ac75a
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..50dc4df
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..2d2b0ed
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..78eda7a
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..4bb5041
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..518e4a3
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..6ade2a8
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..5031c62
--- /dev/null
@@ -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 (file)
index 0000000..0a6d24e
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..71adb59
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..f9ddbe3
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..aab37a6
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..95ed993
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..4c8dc0b
--- /dev/null
@@ -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 (file)
index 0000000..40c0228
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..384e749
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..9f3b3eb
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..0526c75
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..f09c606
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..828d4b1
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..71fbd49
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..f696cc3
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..d696e1b
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..1743f6c
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..05385cf
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..6ed5e0b
--- /dev/null
@@ -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 (file)
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 (executable)
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 (file)
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 (file)
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 (file)
index 0000000..0abcc9d
--- /dev/null
@@ -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 (file)
index 0000000..8f95649
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..c4b27eb
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..7e13582
--- /dev/null
@@ -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 (file)
index 0000000..07cf2fa
--- /dev/null
@@ -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 (file)
index 0000000..1537f95
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..434db0f
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..f6de840
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..7802d52
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..385e7af
--- /dev/null
@@ -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 (file)
index 0000000..6f6b09e
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..9cde144
--- /dev/null
@@ -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 (file)
index 0000000..1aa1bda
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..5661cea
--- /dev/null
@@ -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 (file)
index 0000000..cc4131c
--- /dev/null
@@ -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 (file)
index 0000000..baeed43
--- /dev/null
@@ -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 (file)
index 0000000..1c84282
--- /dev/null
@@ -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 (file)
index 0000000..eaf16ac
--- /dev/null
@@ -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 (file)
index 0000000..bff814b
--- /dev/null
@@ -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