am 5c3eb4d7: am 2462513a: Merge "We no longer need our own icmp6.h --- bionic\'s is good enough."

* commit '5c3eb4d72bcb2411e0946f4c7ed44afe1cf3baa6':
  We no longer need our own icmp6.h --- bionic's is good enough.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3ba2632
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+static-syms.h
+config.*
+Config
+*.o
+*.a
+*.so
+*~
+\#*#
+
+# cscope
+cscope.*
+ncscope.*
+TAGS
+
+# git files that we don't want to ignore even it they are dot-files
+!.gitignore
+!.mailmap
+
+# for patch generation
+*.diff
+*.patch
+*.orig
+*.rej
+
+# for quilt
+.pc
+patches
+series
+
+# for gdb
+.gdbinit
+.gdb_history
+*.gdb
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index 3590a64..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,563 +0,0 @@
-2006-03-21  Stephen Hemminger  <shemminger@freekitty.pdx.osdl.net>
-
-	* Back out the 2.4 utsname patch
-
-2006-03-21  James Lentini <jlentini@netapp.com>
-	
-	* Increase size of hw address allowed for ip neigh to allow
-	  for IB.
-
-2006-03-14  Russell Stuart <russell-lartc@stuart.id.au>
-	
-	* Fix missing memset in tc sample
-	* Fixes for tc hash samples
-	* Add sample divisor
-
-2006-03-10  Alpt <alpt@freaknet.org>
-
-	* Add more rt_proto values
-
-2006-03-10 Dale Sedivec <darkness@caliginous.net>
-	
-	* Warn when using "handle" instead of "classid" with "tc class"
-
-2006-03-10  Jean Tourrilhes <jt@hpl.hp.com>
-
-	* Fix endless loop in netlink error handling
-
-
-2006-03-10  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Change default lnstat count to 1
-	* Update to 2.6.16 headers
-	* Add fake version of include/linux/socket.h to fix warnings
-
-2006-01-12  Patrick McHardy <kaber@trash.net>
-
-	* Handle DCCP in ipxfrm.c to allow using port numbers in the selector.
-
-2006-01-10  Masahide NAKAMURA <nakam@linux-ipv6.org>
-
-	* Add ip link ntable
-
-2006-01-10  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Update headers to santized kernel 2.6.15
-	* Fix ipv6 priority option in u32 
-	
-2006-01-03 Alpt <alpt@freaknet.org>
-
-	* Ip man page addition
-
-2006-01-03  Jamal Hadi Salim <hadi@znyx.com>
-
-	* Documentation for ifb
-
-2005-12-09  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Add corrupt feature to netem
-
-2005-12-02  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Backout ambigious ip command matches
-
-2005-11-22  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Handle ambigious ip command matches
-
-2005-11-22  Patrick McHardy <kaber@trash.net>
-
-	* Add back ip command aliases
-
-2005-11-07  Masahide NAKAMURA <nakam@linux-ipv6.org>
-
-	* Updating for 2.6.14
-	- Show UPD{SA,POLICY} message information from kernel instead of error
-	- Add lengh check of deleting message from kernel
-	- Use macro for struct xfrm_user{sa,policy}_id
-
-	* Minor fix:
-	- Add fflush at the end of normal dump
-
-2005-11-01  Jamal Hadi Salim <hadi@znyx.com>
-
-	* Fix handling of XFRM monitor and state
-
-2005-11-01  Stephen Hemminger  <shemminger@osdl.org
-
-	* Update to 2.6.14 sanitized headers
-
-2005-10-24  Patrick McHardy <kaber@trash.net>
-
-	* Fix ip commnad shortcuts
-
-2005-10-12  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Add more CBQ examples from Fedora Core
-	* Fix buffer overrun in iproute because of bits vs. bytes confusion
-	
-2005-10-12  Jamal Hadi Salim <hadi@znyx.com>
-
-	* Fix ip rule flush, need to reopen rtnl
-
-2005-10-07  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Reenable ip mroute
-
-2005-10-07  Mike Frysinger <vapier@gentoo.org>
-
-	* Handle pfifo_fast that has no qopt without segfaulting
-
-2005-10-05  Mads Martin Joergensen <mmj@suse.de>
-
-	* Trivial netem ccopts
-
-2005-10-04  Jerome Borsboom <j.borsboom@erasmusmc.nl>
-
-	* Fix regression in ip addr (libnetlink) handling
-
-2005-09-21  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Fix uninitialized memory and leaks with valgrind
-	  Reported by Redhat
-
-2005-09-01   Mike Frysinger <vapier@gentoo.org>
-
-	* Fix build issues with netem tables (parallel make and HOSTCC)
-	
-2005-09-01  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Integrate support for DCCP into 'ss' (from acme)
-	* Add -batch option to ip.
-	* Update to 2.6.14 headers
-
-2005-09-01  Eric Dumazet <dada1@cosmosbay.com>
-	
-	* Fix lnstat : First column should not be summed
-
-2005-08-16  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Limit ip route flush to 10 rounds.
-	* Cleanup ip rule flush error message
-	
-2005-08-08  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Update to 2.6.13+ kernel headers
-	* Fix array overrun in paretonormal
-	* Fix ematch to not include dropped fields from skb.
-	
-2005-07-14  Thomas Graf <tgraf@suug.ch>
-
-	* Make ematch bison/lex build with common flex
-	
-2005-07-10  Stephen Hemminger  <shemminger@osdl.org>
-	
-	* Fix Gcc 4.0 build warnings signed/unsigned
-
-2005-06-23  Jamal Hadi Salim <hadi@znyx.com>
-
-	* Fix for options process with ipt
-
-2005-06-23  Thomas Graf <tgraf@suug.ch>
-	
-	* Add extended matches (nbyte, cmp, u32, meta)
-	* Add basic classifier
-	* Fix clean/distclean makefile targets
-	* update local header file copies
-	* IPv4 multipath algorithm selection support
-	* cscope Makefile target
-	* Fix off-by-one while generating argument vector
-	  in batched mode.
-	* Assume stdin if no argument is given to -batch
-
-2005-06-22  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Update include files to 2.6.12
-	* Add ss support for TCP_CONG
-
-2005-06-13  Steven Whitehouse <steve@chygwyn.com>
-
-	* Decnet doc's update
-
-2005-06-07  Stephen Hemminger  <shemminger@osdl.org>
-
-	* Fix 'ip link' map to handle case where device gets autoloaded
-	  by using if_nametoindex as fallback
-	* Device indices are unsigned not int.
-
-2005-06-07   Masahide NAKAMURA <nakam@linux-ipv6.org>
-	
-	* [ip] show timestamp when using '-t' option.
-	* [ip] remove duplicated code for expired message of xfrm.
-	* [ip] add "deleteall" command for xfrm;
-	  "flush" uses kernel's flush interface and
-	  "deleteall" uses legacy iproute2's flush feature like
-	   getting-and-deleting-for-each.
-
-2005-03-30  Stephen Hemminger  <shemminger@osdl.org>
-
-	* include/linux/netfilter_ipv4/ip_tables.h dont include compiler.h
-	  because it isn't needed and not on all systems
-	* Update rtnetlink.h and pkt_cls.h to be stripped versions
-	  of headers from 2.6.12-rc1
-
-2005-03-30  Jamal Hadi Salim <hadi@znyx.com>
-
-	* Proper verison of iptables headers (from 1.3.1)
-	* Set revision file in m_ipt
-	* Fix action_util naming in mirred
-	* don't call ll_init_map in mirred
-
-2005-03-19  Thomas Graf <tgraf@suug.ch>
-
-	* Warn about wildcard deletions and provide IFA_ADDRESS upon
-	  deletions to enforce prefix length validation for IPv4.
-	* Fix netlink message alignment when the last routing attribute added
-	  has a data length not aligned to RTA_ALIGNTO.
-	
-2005-03-30  Masahide NAKAMURA <nakam@linux-ipv6.org>
-	
-	* ipv6 xfrm allocspi and monitor support.
-	
-2005-03-29  Stephen Hemminger  <shemminger@osdl.org>
-
-	* switch to stack for netem tables
-
-2005-03-18  Stephen Hemminger  <shemminger@osdl.org>
-
-	* add -force option to batch mode
-	* handle midline comments in batch mode
-	* sum per cpu fields in lnstat correctly
-
-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@osdl.org>
-
-	* 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/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_GPL
+++ /dev/null
diff --git a/Makefile b/Makefile
index 77a85c6..c107955 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,11 @@
-DESTDIR=/usr/
 ROOTDIR=$(DESTDIR)
-LIBDIR=/usr/lib/
+PREFIX=/usr
+LIBDIR=$(PREFIX)/lib
 SBINDIR=/sbin
 CONFDIR=/etc/iproute2
-DOCDIR=/share/doc/iproute2
-MANDIR=/share/man
+DATADIR=$(PREFIX)/share
+DOCDIR=$(DATADIR)/doc/iproute2
+MANDIR=$(DATADIR)/man
 ARPDDIR=/var/lib/arpd
 
 # Path to db_185.h include
@@ -17,6 +18,8 @@
 DEFINES+= -DNO_SHARED_LIBS
 endif
 
+DEFINES+=-DCONFDIR=\"$(CONFDIR)\"
+
 #options if you have a bind>=4.9.4 libresolv (or, maybe, glibc)
 LDLIBS=-lresolv
 ADDLIB=
@@ -29,15 +32,16 @@
 
 CC = gcc
 HOSTCC = gcc
-CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
-CFLAGS = $(CCOPTS) -I../include $(DEFINES)
+DEFINES += -D_GNU_SOURCE
+CCOPTS = -O2
+WFLAGS = -Wall -Wstrict-prototypes
+CFLAGS = $(WFLAGS) $(CCOPTS) -I../include $(DEFINES)
 YACCFLAGS = -d -t -v
 
-LDLIBS += -L../lib -lnetlink -lutil
-
-SUBDIRS=lib ip tc misc netem genl
+SUBDIRS=lib ip tc misc netem genl man
 
 LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
+LDLIBS += $(LIBNETLINK)
 
 all: Config
 	@set -e; \
@@ -59,27 +63,19 @@
 		$(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 tc-bfifo.8  $(DESTDIR)$(MANDIR)/man8/tc-pfifo.8
-	ln -sf lnstat.8  $(DESTDIR)$(MANDIR)/man8/rtstat.8
-	ln -sf lnstat.8  $(DESTDIR)$(MANDIR)/man8/ctstat.8
-	ln -sf rtacct.8  $(DESTDIR)$(MANDIR)/man8/nstat.8
-	ln -sf routel.8  $(DESTDIR)$(MANDIR)/man8/routef.8
-	install -m 0755 -d $(DESTDIR)$(MANDIR)/man3
-	install -m 0644 $(shell find man/man3 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man3
 
 snapshot:
 	echo "static const char SNAPSHOT[] = \""`date +%y%m%d`"\";" \
 		> include/SNAPSHOT.h
 
 clean:
-	rm -f cscope.*
 	@for i in $(SUBDIRS) doc; \
 	do $(MAKE) $(MFLAGS) -C $$i clean; done
 
-clobber: clean
-	rm -f Config
+clobber:
+	touch Config
+	$(MAKE) $(MFLAGS) clean
+	rm -f Config cscope.*
 
 distclean: clobber
 
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index 3912109..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,340 +0,0 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <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/README b/README
index 7d54bd2..99d1aeb 100644
--- a/README
+++ b/README
@@ -1,25 +1,27 @@
-Primary site is:
-	http://developer.osdl.org/dev/iproute2
+This is a set of utilities for Linux networking.
 
-Original FTP site is:
-	ftp://ftp.inr.ac.ru/ip-routing/
+Information:
+    http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
+
+Download:
+    http://devresources.linuxfoundation.org/dev/iproute2/download
+
+Repository:
+    git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
 
 How to compile this.
 --------------------
-1. Look at start of Makefile and set correct values for:
+1. libdbm
 
-KERNEL_INCLUDE should point to correct linux kernel include directory.
-Default (/usr/src/linux/include) is right as rule.
-
-arpd needs to have the db4 development libraries. For debian
+arpd needs to have the db4 development libraries. For Debian
 users this is the package with a name like libdb4.x-dev.
 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
+is the include file used by arpd to get to the old format Berkeley
 database routines.  Often this is in the db-devel package.
 
 2. make
 
-The makefile will automatically build a file Config which
+The makefile will automatically build a Config file which
 contains whether or not ATM is available, etc.
 
 3. To make documentation, cd to doc/ directory , then
@@ -29,8 +31,13 @@
    and make there. It assumes, that latex, dvips and psnup
    are in your path.
 
+4. This package includes matching sanitized kernel headers because
+   the build environment may not have up to date versions. See Makefile
+   if you have special requirements and need to point at different
+   kernel include files.
+
 Stephen Hemminger
-shemminger@osdl.org
+shemminger@linux-foundation.org
 
 Alexey Kuznetsov
 kuznet@ms2.inr.ac.ru
diff --git a/README.devel b/README.devel
new file mode 100644
index 0000000..9b7d11e
--- /dev/null
+++ b/README.devel
@@ -0,0 +1,15 @@
+Iproute2 development is closely tied to Linux kernel networking
+development. Most new features require a kernel and a utility component.
+
+Please submit both to the Linux networking mailing list
+   <netdev@vger.kernel.org>
+
+The current source is in the git repository:
+    git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
+
+The master branch contains the source corresponding to the current
+code in the mainline Linux kernel (ie follows Linus). The net-next
+branch is a temporary branch that tracks the code intended for the
+next release; it corresponds with networking development branch in
+the kernel.
+
diff --git a/README.distribution b/README.distribution
index fe78fb4..4e2e8ea 100644
--- a/README.distribution
+++ b/README.distribution
@@ -40,7 +40,7 @@
 
 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
+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
diff --git a/README.iproute2+tc b/README.iproute2+tc
index edd79c0..6aa5d18 100644
--- a/README.iproute2+tc
+++ b/README.iproute2+tc
@@ -45,7 +45,7 @@
 papers. 
 
 
-Pairs X:Y are class handles, X:0 are qdisc heandles.
+Pairs X:Y are class handles, X:0 are qdisc handles.
 weight should be proportional to rate for leaf classes
 (I choosed it ten times less, but it is not necessary)
 
diff --git a/RELNOTES b/RELNOTES
deleted file mode 100644
index 17f0011..0000000
--- a/RELNOTES
+++ /dev/null
@@ -1,168 +0,0 @@
-[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
index 600fa96..0f4444f 100755
--- a/configure
+++ b/configure
@@ -3,11 +3,13 @@
 #
 INCLUDE=${1:-"$PWD/include"}
 
-TABLES=
+# Make a temp directory in build tree.
+TMPDIR=$(mktemp -d config.XXXXXX)
+trap 'status=$?; rm -rf $TMPDIRa; exit $status' EXIT HUP INT QUIT TERM
 
 check_atm()
 {
-cat >/tmp/atmtest.c <<EOF
+cat >$TMPDIR/atmtest.c <<EOF
 #include <atm.h>
 int main(int argc, char **argv) {
 	struct atm_qos qos;
@@ -15,7 +17,7 @@
 	return 0;
 }
 EOF
-gcc -I$INCLUDE -o /tmp/atmtest /tmp/atmtest.c -latm >/dev/null 2>&1 
+gcc -I$INCLUDE -o $TMPDIR/atmtest $TMPDIR/atmtest.c -latm >/dev/null 2>&1 
 if [ $? -eq 0 ]
 then
     echo "TC_CONFIG_ATM:=y" >>Config
@@ -23,13 +25,13 @@
 else
     echo no
 fi
-rm -f /tmp/atmtest.c /tmp/atmtest
+rm -f $TMPDIR/atmtest.c $TMPDIR/atmtest
 }
 
 check_xt()
 {
 #check if we have xtables from iptables >= 1.4.5.
-cat >/tmp/ipttest.c <<EOF
+cat >$TMPDIR/ipttest.c <<EOF
 #include <xtables.h>
 #include <linux/netfilter.h>
 static struct xtables_globals test_globals = {
@@ -49,12 +51,12 @@
 
 EOF
 
-if gcc -I$INCLUDE $IPTC -o /tmp/ipttest /tmp/ipttest.c $IPTL -ldl -lxtables >/dev/null 2>&1
+if gcc -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL $(pkg-config xtables --cflags --libs) -ldl >/dev/null 2>&1
 then
 	echo "TC_CONFIG_XT:=y" >>Config
 	echo "using xtables"
 fi
-rm -f /tmp/ipttest.c /tmp/ipttest
+rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
 check_xt_old()
@@ -65,8 +67,8 @@
 	return
 fi
 
-#check if we need dont our internal header ..
-cat >/tmp/ipttest.c <<EOF
+#check if we dont need our internal header ..
+cat >$TMPDIR/ipttest.c <<EOF
 #include <xtables.h>
 char *lib_dir;
 unsigned int global_option_offset = 0;
@@ -86,14 +88,14 @@
 }
 
 EOF
-gcc -I$INCLUDE $IPTC -o /tmp/ipttest /tmp/ipttest.c $IPTL -ldl >/dev/null 2>&1
+gcc -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1
 
 if [ $? -eq 0 ]
 then
 	echo "TC_CONFIG_XT_OLD:=y" >>Config
 	echo "using old xtables (no need for xt-internal.h)"
 fi
-rm -f /tmp/ipttest.c /tmp/ipttest
+rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
 check_xt_old_internal_h()
@@ -105,7 +107,7 @@
 fi
 
 #check if we need our own internal.h
-cat >/tmp/ipttest.c <<EOF
+cat >$TMPDIR/ipttest.c <<EOF
 #include <xtables.h>
 #include "xt-internal.h"
 char *lib_dir;
@@ -126,14 +128,14 @@
 }
 
 EOF
-gcc -I$INCLUDE $IPTC -o /tmp/ipttest /tmp/ipttest.c $IPTL -ldl >/dev/null 2>&1
+gcc -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1
 
 if [ $? -eq 0 ]
 then
 	echo "using old xtables with xt-internal.h"
 	echo "TC_CONFIG_XT_OLD_H:=y" >>Config
 fi
-rm -f /tmp/ipttest.c /tmp/ipttest
+rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
 check_ipt()
@@ -141,9 +143,6 @@
 	if ! grep TC_CONFIG_XT Config > /dev/null
 	then
 		echo "using iptables"
-		TABLES="iptables"
-	else
-		TABLES="xtables"
 	fi
 }
 
@@ -152,10 +151,10 @@
 	IPT_LIB_DIR=""
 	for dir in /lib /usr/lib /usr/local/lib
 	do
-		for file in $dir/$TABLES/lib*t_*so ; do
+		for file in $dir/{xtables,iptables}/lib*t_*so ; do
 			if [ -f $file ]; then
-				echo $dir/$TABLES
-				echo "IPT_LIB_DIR:=$dir/$TABLES" >> Config
+				echo ${file%/*}
+				echo "IPT_LIB_DIR:=${file%/*}" >> Config
 				return
 			fi
 		done
@@ -163,6 +162,27 @@
 	echo "not found!"
 }
 
+check_setns()
+{
+cat >$TMPDIR/setnstest.c <<EOF
+#include <sched.h>
+int main(int argc, char **argv) 
+{
+	(void)setns(0,0);
+	return 0;
+}
+EOF
+gcc -I$INCLUDE -o $TMPDIR/setnstest $TMPDIR/setnstest.c >/dev/null 2>&1
+if [ $? -eq 0 ]
+then
+	echo "IP_CONFIG_SETNS:=y" >>Config
+	echo "yes"
+else
+	echo "no"
+fi
+rm -f $TMPDIR/setnstest.c $TMPDIR/setnstest
+}
+
 echo "# Generated config based on" $INCLUDE >Config
 
 echo "TC schedulers"
@@ -178,3 +198,6 @@
 
 echo -n "iptables modules directory: "
 check_ipt_lib_dir
+
+echo -n "libc has setns: "
+check_setns
diff --git a/doc/Makefile b/doc/Makefile
index 84836a2..1df6081 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -15,6 +15,7 @@
 
 HTMLFILES=$(subst .sgml,.html,$(shell echo *.sgml))
 DVIFILES=$(subst .ps,.dvi,$(PSFILES))
+PDFFILES=$(subst .ps,.pdf,$(PSFILES))
 
 
 all: pstwocol
@@ -25,6 +26,8 @@
 
 dvi: $(DVIFILES)
 
+pdf: $(PDFFILES)
+
 print: $(PSFILES)
 	$(LPR) $(PSFILES)
 
@@ -41,6 +44,11 @@
 		echo "Re-running LaTeX $<, $${pass}d pass"; pass=$$[$$pass + 1]; \
 	done
 
+#%.pdf: %.tex
+#	pdflatex $<
+%.pdf: %.ps
+	ps2pdf $<
+
 %.ps: %.dvi
 	$(DVIPS) $< -o $@
 
diff --git a/doc/ip-cref.tex b/doc/ip-cref.tex
index 056e3bf..d8fed66 100644
--- a/doc/ip-cref.tex
+++ b/doc/ip-cref.tex
@@ -1675,6 +1675,41 @@
 \item \verb|used| --- the number of lookups of this route since its creation.
 \end{itemize}
 
+\subsection{{\tt ip route save} -- save routing tables}
+\label{IP-ROUTE-SAVE}
+
+\paragraph{Description:} this command saves the contents of the routing
+tables or the route(s) selected by some criteria to standard output.
+
+\paragraph{Arguments:} \verb|ip route save| has the same arguments as
+\verb|ip route show|.
+
+\paragraph{Example:} This saves all the routes to the {\tt saved\_routes}
+file:
+\begin{verbatim}
+dan@caffeine:~ # ip route save > saved_routes
+\end{verbatim}
+
+\paragraph{Output format:} The format of the data stream provided by
+\verb|ip route save| is that of \verb|rtnetlink|.  See
+\verb|rtnetlink(7)| for more information.
+
+\subsection{{\tt ip route restore} -- restore routing tables}
+\label{IP-ROUTE-RESTORE}
+
+\paragraph{Description:} this command restores the contents of the routing
+tables according to a data stream as provided by \verb|ip route save| via
+standard input.  Note that any routes already in the table are left unchanged.
+Any routes in the input stream that already exist in the tables are ignored.
+
+\paragraph{Arguments:} This command takes no arguments.
+
+\paragraph{Example:} This restores all routes that were saved to the
+{\tt saved\_routes} file:
+
+\begin{verbatim}
+dan@caffeine:~ # ip route restore < saved_routes
+\end{verbatim}
 
 \subsection{{\tt ip route flush} --- flush routing tables}
 \label{IP-ROUTE-FLUSH}
diff --git a/etc/iproute2/group b/etc/iproute2/group
new file mode 100644
index 0000000..6f000b2
--- /dev/null
+++ b/etc/iproute2/group
@@ -0,0 +1,2 @@
+# device group names
+0	default
diff --git a/examples/dhcp-client-script b/examples/dhcp-client-script
index 7207b57..f39bc10 100644
--- a/examples/dhcp-client-script
+++ b/examples/dhcp-client-script
@@ -14,7 +14,7 @@
 # we should install and preserve.
 #
 
-exec >> /tmp/DHS.log 2>&1
+exec >> /var/log/DHS.log 2>&1
 
 echo dhc-script $* reason=$reason
 set | grep "^\(old_\|new_\|check_\)"
diff --git a/genl/Makefile b/genl/Makefile
index 44bb83c..03d1f26 100644
--- a/genl/Makefile
+++ b/genl/Makefile
@@ -3,6 +3,8 @@
 include ../Config
 SHARED_LIBS ?= y
 
+CFLAGS += -fno-strict-aliasing
+
 GENLMODULES :=
 GENLMODULES += ctrl.o
 
diff --git a/genl/ctrl.c b/genl/ctrl.c
index 30ea4d7..6d97c26 100644
--- a/genl/ctrl.c
+++ b/genl/ctrl.c
@@ -67,7 +67,7 @@
 
 	addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
 
-	if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
+	if (rtnl_talk(&rth, nlh, 0, 0, nlh) < 0) {
 		fprintf(stderr, "Error talking to the kernel\n");
 		goto errout;
 	}
@@ -104,7 +104,7 @@
 			goto errout;
 		}
 
-		ret = *(__u16 *) RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
+		ret = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
 	}
 
 errout:
@@ -334,7 +334,7 @@
 			goto ctrl_done;
 		}
 
-		if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
+		if (rtnl_talk(&rth, nlh, 0, 0, nlh) < 0) {
 			fprintf(stderr, "Error talking to the kernel\n");
 			goto ctrl_done;
 		}
@@ -350,12 +350,12 @@
 		nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
 		nlh->nlmsg_seq = rth.dump = ++rth.seq;
 
-		if (rtnl_send(&rth, (const char *) nlh, nlh->nlmsg_len) < 0) {
+		if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
 			perror("Failed to send dump request\n");
 			goto ctrl_done;
 		}
 
-		rtnl_dump_filter(&rth, print_ctrl, stdout, NULL, NULL);
+		rtnl_dump_filter(&rth, print_ctrl, stdout);
 
         }
 
diff --git a/genl/genl.c b/genl/genl.c
index 3ae75ae..49b6596 100644
--- a/genl/genl.c
+++ b/genl/genl.c
@@ -109,14 +109,6 @@
 
 int main(int argc, char **argv)
 {
-	char *basename;
-
-	basename = strrchr(argv[0], '/');
-	if (basename == NULL)
-		basename = argv[0];
-	else
-		basename++;
-
 	while (argc > 1) {
 		if (argv[1][0] != '-')
 			break;
@@ -144,18 +136,13 @@
 		int ret;
 		struct genl_util *a = NULL;
 		a = get_genl_kind(argv[1]);
-		if (NULL == a) {
-			fprintf(stderr,"bad genl %s\n",argv[1]);
+		if (!a) {
+			fprintf(stderr,"bad genl %s\n", argv[1]);
+			exit(-1);
 		}
 
 		ret = a->parse_genlopt(a, argc-1, argv+1);
 		return ret;
-
-		if (matches(argv[1], "help") == 0)
-			usage();
-		fprintf(stderr, "Object \"%s\" is unknown, try \"genl "
-			"-help\".\n", argv[1]);
-		exit(-1);
 	}
 
 	usage();
diff --git a/include/SNAPSHOT.h b/include/SNAPSHOT.h
index de41b1b..9395cab 100644
--- a/include/SNAPSHOT.h
+++ b/include/SNAPSHOT.h
@@ -1 +1 @@
-static const char SNAPSHOT[] = "100804";
+static const char SNAPSHOT[] = "120521";
diff --git a/include/libnetlink.h b/include/libnetlink.h
index 07bd707..81649af 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -1,6 +1,7 @@
 #ifndef __LIBNETLINK_H__
 #define __LIBNETLINK_H__ 1
 
+#include <string.h>
 #include <asm/types.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
@@ -32,25 +33,24 @@
 {
 	rtnl_filter_t filter;
 	void *arg1;
-	rtnl_filter_t junk;
-	void *arg2;
 };
 
 extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
 			      const struct rtnl_dump_filter_arg *arg);
 extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
-			    void *arg1,
-			    rtnl_filter_t junk,
-			    void *arg2);
-
+			    void *arg);
 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 rtnl_send_check(struct rtnl_handle *rth, const char *buf, int);
+		     unsigned groups, struct nlmsghdr *answer);
+extern int rtnl_send(struct rtnl_handle *rth, const void *buf, int);
+extern int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int);
 
+extern int addattr(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data);
+extern int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data);
 extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data);
+extern int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *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 struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
@@ -68,8 +68,31 @@
 	(parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
 
 #define parse_rtattr_nested_compat(tb, max, rta, data, len) \
-({	data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
-	__parse_rtattr_nested_compat(tb, max, rta, len); })
+	({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL;	\
+		__parse_rtattr_nested_compat(tb, max, rta, len); })
+
+static inline __u8 rta_getattr_u8(const struct rtattr *rta)
+{
+	return *(__u8 *)RTA_DATA(rta);
+}
+static inline __u16 rta_getattr_u16(const struct rtattr *rta)
+{
+	return *(__u16 *)RTA_DATA(rta);
+}
+static inline __u32 rta_getattr_u32(const struct rtattr *rta)
+{
+	return *(__u32 *)RTA_DATA(rta);
+}
+static inline __u64 rta_getattr_u64(const struct rtattr *rta)
+{
+	__u64 tmp;
+	memcpy(&tmp, RTA_DATA(rta), sizeof(__u64));
+	return tmp;
+}
+static inline const char *rta_getattr_str(const struct rtattr *rta)
+{
+	return (const char *)RTA_DATA(rta);
+}
 
 extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
 		       void *jarg);
diff --git a/include/linux/can/netlink.h b/include/linux/can/netlink.h
index 3250de9..14966dd 100644
--- a/include/linux/can/netlink.h
+++ b/include/linux/can/netlink.h
@@ -5,8 +5,6 @@
  *
  * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
  *
- * Send feedback to <socketcan-users@lists.berlios.de>
- *
  */
 
 #ifndef CAN_NETLINK_H
@@ -17,7 +15,7 @@
 /*
  * CAN bit-timing parameters
  *
- * For futher information, please read chapter "8 BIT TIMING
+ * For further information, please read chapter "8 BIT TIMING
  * REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
  * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
  */
diff --git a/include/linux/if.h b/include/linux/if.h
index a272e37..b3383a9 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -71,8 +71,17 @@
 					 * release skb->dst
 					 */
 #define IFF_DONT_BRIDGE 0x800		/* disallow bridging this ether dev */
-#define IFF_IN_NETPOLL	0x1000		/* whether we are processing netpoll */
-#define IFF_DISABLE_NETPOLL	0x2000	/* disable netpoll at run-time */
+#define IFF_DISABLE_NETPOLL	0x1000	/* disable netpoll at run-time */
+#define IFF_MACVLAN_PORT	0x2000	/* device used as macvlan port */
+#define IFF_BRIDGE_PORT	0x4000		/* device used as bridge port */
+#define IFF_OVS_DATAPATH	0x8000	/* device used as Open vSwitch
+					 * datapath port */
+#define IFF_TX_SKB_SHARING	0x10000	/* The interface supports sharing
+					 * skbs on transmit */
+#define IFF_UNICAST_FLT	0x20000		/* Supports unicast filtering	*/
+#define IFF_TEAM_PORT	0x40000		/* device used as team port */
+#define IFF_SUPP_NOFCS	0x80000		/* device supports sending custom FCS */
+
 
 #define IF_GET_IFACE	0x0001		/* for querying only */
 #define IF_GET_PROTO	0x0002
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 7e275c4..8b35afe 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -72,16 +72,25 @@
 #define ETH_P_MPLS_UC	0x8847		/* MPLS Unicast traffic		*/
 #define ETH_P_MPLS_MC	0x8848		/* MPLS Multicast traffic	*/
 #define ETH_P_ATMMPOA	0x884c		/* MultiProtocol Over ATM	*/
+#define ETH_P_LINK_CTL	0x886c		/* HPNA, wlan link local tunnel */
 #define ETH_P_ATMFATE	0x8884		/* Frame-based ATM Transport
 					 * over Ethernet
 					 */
 #define ETH_P_PAE	0x888E		/* Port Access Entity (IEEE 802.1X) */
 #define ETH_P_AOE	0x88A2		/* ATA over Ethernet		*/
+#define ETH_P_8021AD	0x88A8          /* 802.1ad Service VLAN		*/
+#define ETH_P_802_EX1	0x88B5		/* 802.1 Local Experimental 1.  */
 #define ETH_P_TIPC	0x88CA		/* TIPC 			*/
+#define ETH_P_8021AH	0x88E7          /* 802.1ah Backbone Service Tag */
 #define ETH_P_1588	0x88F7		/* IEEE 1588 Timesync */
 #define ETH_P_FCOE	0x8906		/* Fibre Channel over Ethernet  */
+#define ETH_P_TDLS	0x890D          /* TDLS */
 #define ETH_P_FIP	0x8914		/* FCoE Initialization Protocol */
+#define ETH_P_QINQ1	0x9100		/* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_QINQ2	0x9200		/* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_QINQ3	0x9300		/* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_EDSA	0xDADA		/* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_AF_IUCV   0xFBFB		/* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
 
 /*
  *	Non DIX types. Won't clash for 1500 types.
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 01bcaa6..06a3a47 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -4,7 +4,7 @@
 #include <linux/types.h>
 #include <linux/netlink.h>
 
-/* The struct should be in sync with struct net_device_stats */
+/* This struct should be in sync with struct rtnl_link_stats64 */
 struct rtnl_link_stats {
 	__u32	rx_packets;		/* total packets received	*/
 	__u32	tx_packets;		/* total packets transmitted	*/
@@ -37,6 +37,7 @@
 	__u32	tx_compressed;
 };
 
+/* The main device statistics structure */
 struct rtnl_link_stats64 {
 	__u64	rx_packets;		/* total packets received	*/
 	__u64	tx_packets;		/* total packets transmitted	*/
@@ -79,6 +80,24 @@
 	__u8	port;
 };
 
+/*
+ * IFLA_AF_SPEC
+ *   Contains nested attributes for address family specific attributes.
+ *   Each address family may create a attribute with the address family
+ *   number as type and create its own attribute structure in it.
+ *
+ *   Example:
+ *   [IFLA_AF_SPEC] = {
+ *       [AF_INET] = {
+ *           [IFLA_INET_CONF] = ...,
+ *       },
+ *       [AF_INET6] = {
+ *           [IFLA_INET6_FLAGS] = ...,
+ *           [IFLA_INET6_CONF] = ...,
+ *       }
+ *   }
+ */
+
 enum {
 	IFLA_UNSPEC,
 	IFLA_ADDRESS,
@@ -115,6 +134,10 @@
 	IFLA_STATS64,
 	IFLA_VF_PORTS,
 	IFLA_PORT_SELF,
+	IFLA_AF_SPEC,
+	IFLA_GROUP,		/* Group the device belongs to */
+	IFLA_NET_NS_FD,
+	IFLA_EXT_MASK,		/* Extended info mask, VFs, etc */
 	__IFLA_MAX
 };
 
@@ -125,6 +148,14 @@
 #define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
 #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
 
+enum {
+	IFLA_INET_UNSPEC,
+	IFLA_INET_CONF,
+	__IFLA_INET_MAX,
+};
+
+#define IFLA_INET_MAX (__IFLA_INET_MAX - 1)
+
 /* ifi_flags.
 
    IFF_* flags.
@@ -229,9 +260,10 @@
 	MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */
 	MACVLAN_MODE_VEPA    = 2, /* talk to other ports through ext bridge */
 	MACVLAN_MODE_BRIDGE  = 4, /* talk to bridge ports directly */
+	MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
 };
 
-/* SR-IOV virtual function managment section */
+/* SR-IOV virtual function management section */
 
 enum {
 	IFLA_VF_INFO_UNSPEC,
@@ -246,6 +278,7 @@
 	IFLA_VF_MAC,		/* Hardware queue specific attributes */
 	IFLA_VF_VLAN,
 	IFLA_VF_TX_RATE,	/* TX Bandwidth Allocation */
+	IFLA_VF_SPOOFCHK,	/* Spoof Checking on/off switch */
 	__IFLA_VF_MAX,
 };
 
@@ -267,12 +300,9 @@
 	__u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
 };
 
-struct ifla_vf_info {
+struct ifla_vf_spoofchk {
 	__u32 vf;
-	__u8 mac[32];
-	__u32 vlan;
-	__u32 qos;
-	__u32 tx_rate;
+	__u32 setting;
 };
 
 /* VF ports management section
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index 329b354..5b808eb 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -54,7 +54,7 @@
 		unsigned int skb_priority;
 		unsigned int name_type;
 		unsigned int bind_type;
-		unsigned int flag; /* Matches vlan_dev_info flags */
+		unsigned int flag; /* Matches vlan_dev_priv flags */
         } u;
 
 	short vlan_qos;   
diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h
index 1107ed2..7e31238 100644
--- a/include/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -34,6 +34,15 @@
 	__u32	idiag_dbs;		/* Tables to dump (NI) */
 };
 
+struct inet_diag_req_v2 {
+	__u8	sdiag_family;
+	__u8	sdiag_protocol;
+	__u8	idiag_ext;
+	__u8	pad;
+	__u32	idiag_states;
+	struct inet_diag_sockid id;
+};
+
 enum {
 	INET_DIAG_REQ_NONE,
 	INET_DIAG_REQ_BYTECODE,
@@ -97,9 +106,12 @@
 	INET_DIAG_INFO,
 	INET_DIAG_VEGASINFO,
 	INET_DIAG_CONG,
+	INET_DIAG_TOS,
+	INET_DIAG_TCLASS,
+	INET_DIAG_SKMEMINFO,
 };
 
-#define INET_DIAG_MAX INET_DIAG_CONG
+#define INET_DIAG_MAX INET_DIAG_SKMEMINFO
 
 
 /* INET_DIAG_MEM */
diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h
index acb9ad6..bf22b03 100644
--- a/include/linux/ip6_tunnel.h
+++ b/include/linux/ip6_tunnel.h
@@ -16,6 +16,8 @@
 #define IP6_TNL_F_MIP6_DEV 0x8
 /* copy DSCP from the outer packet */
 #define IP6_TNL_F_RCV_DSCP_COPY 0x10
+/* copy fwmark from inner packet */
+#define IP6_TNL_F_USE_ORIG_FWMARK 0x20
 
 struct ip6_tnl_parm {
 	char name[IFNAMSIZ];	/* name of tunnel device */
diff --git a/include/linux/l2tp.h b/include/linux/l2tp.h
new file mode 100644
index 0000000..5ca74dd
--- /dev/null
+++ b/include/linux/l2tp.h
@@ -0,0 +1,160 @@
+/*
+ * L2TP-over-IP socket for L2TPv3.
+ *
+ * Author: James Chapman <jchapman@katalix.com>
+ */
+
+#ifndef _LINUX_L2TP_H_
+#define _LINUX_L2TP_H_
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+
+#define IPPROTO_L2TP		115
+
+/**
+ * struct sockaddr_l2tpip - the sockaddr structure for L2TP-over-IP sockets
+ * @l2tp_family:  address family number AF_L2TPIP.
+ * @l2tp_addr:    protocol specific address information
+ * @l2tp_conn_id: connection id of tunnel
+ */
+#define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
+struct sockaddr_l2tpip {
+	/* The first fields must match struct sockaddr_in */
+	__kernel_sa_family_t l2tp_family; /* AF_INET */
+	__be16		l2tp_unused;	/* INET port number (unused) */
+	struct in_addr	l2tp_addr;	/* Internet address */
+
+	__u32		l2tp_conn_id;	/* Connection ID of tunnel */
+
+	/* Pad to size of `struct sockaddr'. */
+	unsigned char	__pad[sizeof(struct sockaddr) -
+			      sizeof(__kernel_sa_family_t) -
+			      sizeof(__be16) - sizeof(struct in_addr) -
+			      sizeof(__u32)];
+};
+
+/*****************************************************************************
+ *  NETLINK_GENERIC netlink family.
+ *****************************************************************************/
+
+/*
+ * Commands.
+ * Valid TLVs of each command are:-
+ * TUNNEL_CREATE	- CONN_ID, pw_type, netns, ifname, ipinfo, udpinfo, udpcsum, vlanid
+ * TUNNEL_DELETE	- CONN_ID
+ * TUNNEL_MODIFY	- CONN_ID, udpcsum
+ * TUNNEL_GETSTATS	- CONN_ID, (stats)
+ * TUNNEL_GET		- CONN_ID, (...)
+ * SESSION_CREATE	- SESSION_ID, PW_TYPE, offset, data_seq, cookie, peer_cookie, offset, l2spec
+ * SESSION_DELETE	- SESSION_ID
+ * SESSION_MODIFY	- SESSION_ID, data_seq
+ * SESSION_GET		- SESSION_ID, (...)
+ * SESSION_GETSTATS	- SESSION_ID, (stats)
+ *
+ */
+enum {
+	L2TP_CMD_NOOP,
+	L2TP_CMD_TUNNEL_CREATE,
+	L2TP_CMD_TUNNEL_DELETE,
+	L2TP_CMD_TUNNEL_MODIFY,
+	L2TP_CMD_TUNNEL_GET,
+	L2TP_CMD_SESSION_CREATE,
+	L2TP_CMD_SESSION_DELETE,
+	L2TP_CMD_SESSION_MODIFY,
+	L2TP_CMD_SESSION_GET,
+	__L2TP_CMD_MAX,
+};
+
+#define L2TP_CMD_MAX			(__L2TP_CMD_MAX - 1)
+
+/*
+ * ATTR types defined for L2TP
+ */
+enum {
+	L2TP_ATTR_NONE,			/* no data */
+	L2TP_ATTR_PW_TYPE,		/* u16, enum l2tp_pwtype */
+	L2TP_ATTR_ENCAP_TYPE,		/* u16, enum l2tp_encap_type */
+	L2TP_ATTR_OFFSET,		/* u16 */
+	L2TP_ATTR_DATA_SEQ,		/* u16 */
+	L2TP_ATTR_L2SPEC_TYPE,		/* u8, enum l2tp_l2spec_type */
+	L2TP_ATTR_L2SPEC_LEN,		/* u8, enum l2tp_l2spec_type */
+	L2TP_ATTR_PROTO_VERSION,	/* u8 */
+	L2TP_ATTR_IFNAME,		/* string */
+	L2TP_ATTR_CONN_ID,		/* u32 */
+	L2TP_ATTR_PEER_CONN_ID,		/* u32 */
+	L2TP_ATTR_SESSION_ID,		/* u32 */
+	L2TP_ATTR_PEER_SESSION_ID,	/* u32 */
+	L2TP_ATTR_UDP_CSUM,		/* u8 */
+	L2TP_ATTR_VLAN_ID,		/* u16 */
+	L2TP_ATTR_COOKIE,		/* 0, 4 or 8 bytes */
+	L2TP_ATTR_PEER_COOKIE,		/* 0, 4 or 8 bytes */
+	L2TP_ATTR_DEBUG,		/* u32 */
+	L2TP_ATTR_RECV_SEQ,		/* u8 */
+	L2TP_ATTR_SEND_SEQ,		/* u8 */
+	L2TP_ATTR_LNS_MODE,		/* u8 */
+	L2TP_ATTR_USING_IPSEC,		/* u8 */
+	L2TP_ATTR_RECV_TIMEOUT,		/* msec */
+	L2TP_ATTR_FD,			/* int */
+	L2TP_ATTR_IP_SADDR,		/* u32 */
+	L2TP_ATTR_IP_DADDR,		/* u32 */
+	L2TP_ATTR_UDP_SPORT,		/* u16 */
+	L2TP_ATTR_UDP_DPORT,		/* u16 */
+	L2TP_ATTR_MTU,			/* u16 */
+	L2TP_ATTR_MRU,			/* u16 */
+	L2TP_ATTR_STATS,		/* nested */
+	__L2TP_ATTR_MAX,
+};
+
+#define L2TP_ATTR_MAX			(__L2TP_ATTR_MAX - 1)
+
+/* Nested in L2TP_ATTR_STATS */
+enum {
+	L2TP_ATTR_STATS_NONE,		/* no data */
+	L2TP_ATTR_TX_PACKETS,		/* u64 */
+	L2TP_ATTR_TX_BYTES,		/* u64 */
+	L2TP_ATTR_TX_ERRORS,		/* u64 */
+	L2TP_ATTR_RX_PACKETS,		/* u64 */
+	L2TP_ATTR_RX_BYTES,		/* u64 */
+	L2TP_ATTR_RX_SEQ_DISCARDS,	/* u64 */
+	L2TP_ATTR_RX_OOS_PACKETS,	/* u64 */
+	L2TP_ATTR_RX_ERRORS,		/* u64 */
+	__L2TP_ATTR_STATS_MAX,
+};
+
+#define L2TP_ATTR_STATS_MAX		(__L2TP_ATTR_STATS_MAX - 1)
+
+enum l2tp_pwtype {
+	L2TP_PWTYPE_NONE = 0x0000,
+	L2TP_PWTYPE_ETH_VLAN = 0x0004,
+	L2TP_PWTYPE_ETH = 0x0005,
+	L2TP_PWTYPE_PPP = 0x0007,
+	L2TP_PWTYPE_PPP_AC = 0x0008,
+	L2TP_PWTYPE_IP = 0x000b,
+	__L2TP_PWTYPE_MAX
+};
+
+enum l2tp_l2spec_type {
+	L2TP_L2SPECTYPE_NONE,
+	L2TP_L2SPECTYPE_DEFAULT,
+};
+
+enum l2tp_encap_type {
+	L2TP_ENCAPTYPE_UDP,
+	L2TP_ENCAPTYPE_IP,
+};
+
+enum l2tp_seqmode {
+	L2TP_SEQ_NONE = 0,
+	L2TP_SEQ_IP = 1,
+	L2TP_SEQ_ALL = 2,
+};
+
+/*
+ * NETLINK_GENERIC related info
+ */
+#define L2TP_GENL_NAME		"l2tp"
+#define L2TP_GENL_VERSION	0x1
+
+#endif
diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h
index a7003b7..b188f68 100644
--- a/include/linux/neighbour.h
+++ b/include/linux/neighbour.h
@@ -116,6 +116,7 @@
 	NDTPA_PROXY_DELAY,		/* u64, msecs */
 	NDTPA_PROXY_QLEN,		/* u32 */
 	NDTPA_LOCKTIME,			/* u64, msecs */
+	NDTPA_QUEUE_LENBYTES,		/* u32 */
 	__NDTPA_MAX
 };
 #define NDTPA_MAX (__NDTPA_MAX - 1)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 3dbf0cc..6c10e02 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -33,43 +33,9 @@
 
 #define MAX_ADDR_LEN	32		/* Largest hardware address length */
 
+/* Initial net device group. All devices belong to group 0 by default. */
+#define INIT_NETDEV_GROUP	0
 
-/*
- *	Network device statistics. Akin to the 2.0 ether stats but
- *	with byte counters.
- */
-
-struct net_device_stats {
-	unsigned long	rx_packets;		/* total packets received	*/
-	unsigned long	tx_packets;		/* total packets transmitted	*/
-	unsigned long	rx_bytes;		/* total bytes received 	*/
-	unsigned long	tx_bytes;		/* total bytes transmitted	*/
-	unsigned long	rx_errors;		/* bad packets received		*/
-	unsigned long	tx_errors;		/* packet transmit problems	*/
-	unsigned long	rx_dropped;		/* no space in linux buffers	*/
-	unsigned long	tx_dropped;		/* no space available in linux	*/
-	unsigned long	multicast;		/* multicast packets received	*/
-	unsigned long	collisions;
-
-	/* detailed rx_errors: */
-	unsigned long	rx_length_errors;
-	unsigned long	rx_over_errors;		/* receiver ring buff overflow	*/
-	unsigned long	rx_crc_errors;		/* recved pkt with crc error	*/
-	unsigned long	rx_frame_errors;	/* recv'd frame alignment error */
-	unsigned long	rx_fifo_errors;		/* recv'r fifo overrun		*/
-	unsigned long	rx_missed_errors;	/* receiver missed packet	*/
-
-	/* detailed tx_errors */
-	unsigned long	tx_aborted_errors;
-	unsigned long	tx_carrier_errors;
-	unsigned long	tx_fifo_errors;
-	unsigned long	tx_heartbeat_errors;
-	unsigned long	tx_window_errors;
-	
-	/* for cslip etc */
-	unsigned long	rx_compressed;
-	unsigned long	tx_compressed;
-};
 
 
 /* Media selection options. */
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 2eb00b6..5477131 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -3,6 +3,7 @@
 
 #include <linux/types.h>
 
+#include <linux/sysctl.h>
 
 /* Responses from hook functions. */
 #define NF_DROP 0
@@ -14,14 +15,20 @@
 #define NF_MAX_VERDICT NF_STOP
 
 /* we overload the higher bits for encoding auxiliary data such as the queue
- * number. Not nice, but better than additional function arguments. */
-#define NF_VERDICT_MASK 0x0000ffff
-#define NF_VERDICT_BITS 16
+ * number or errno values. Not nice, but better than additional function
+ * arguments. */
+#define NF_VERDICT_MASK 0x000000ff
 
+/* extra verdict flags have mask 0x0000ff00 */
+#define NF_VERDICT_FLAG_QUEUE_BYPASS	0x00008000
+
+/* queue number (NF_QUEUE) or errno (NF_DROP) */
 #define NF_VERDICT_QMASK 0xffff0000
 #define NF_VERDICT_QBITS 16
 
-#define NF_QUEUE_NR(x) ((((x) << NF_VERDICT_BITS) & NF_VERDICT_QMASK) | NF_QUEUE)
+#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP)
 
 /* only for userspace compatibility */
 /* Generic cache responses from hook functions.
@@ -29,6 +36,9 @@
 #define NFC_UNKNOWN 0x4000
 #define NFC_ALTERED 0x8000
 
+/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */
+#define NF_VERDICT_BITS 16
+
 enum nf_inet_hooks {
 	NF_INET_PRE_ROUTING,
 	NF_INET_LOCAL_IN,
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index fa2d957..4120970 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -66,6 +66,11 @@
 	int verdict;
 };
 
+struct xt_error_target {
+	struct xt_entry_target target;
+	char errorname[XT_FUNCTION_MAXNAMELEN];
+};
+
 /* The argument to IPT_SO_GET_REVISION_*.  Returns highest revision
  * kernel supports, if >= revision. */
 struct xt_get_revision {
diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h
index 4d7ba3e..5490309 100644
--- a/include/linux/netfilter_ipv4.h
+++ b/include/linux/netfilter_ipv4.h
@@ -8,6 +8,9 @@
 #include <linux/netfilter.h>
 
 /* only for userspace compatibility */
+
+#include <limits.h> /* for INT_MIN, INT_MAX */
+
 /* IP Cache bits. */
 /* Src IP address. */
 #define NFC_IP_SRC		0x0001
diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
index 735f4b1..38542b4 100644
--- a/include/linux/netfilter_ipv4/ip_tables.h
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -27,6 +27,41 @@
 #define ipt_target xt_target
 #define ipt_table xt_table
 #define ipt_get_revision xt_get_revision
+#define ipt_entry_match xt_entry_match
+#define ipt_entry_target xt_entry_target
+#define ipt_standard_target xt_standard_target
+#define ipt_error_target xt_error_target
+#define ipt_counters xt_counters
+#define IPT_CONTINUE XT_CONTINUE
+#define IPT_RETURN XT_RETURN
+
+/* This group is older than old (iptables < v1.4.0-rc1~89) */
+#include <linux/netfilter/xt_tcpudp.h>
+#define ipt_udp xt_udp
+#define ipt_tcp xt_tcp
+#define IPT_TCP_INV_SRCPT	XT_TCP_INV_SRCPT
+#define IPT_TCP_INV_DSTPT	XT_TCP_INV_DSTPT
+#define IPT_TCP_INV_FLAGS	XT_TCP_INV_FLAGS
+#define IPT_TCP_INV_OPTION	XT_TCP_INV_OPTION
+#define IPT_TCP_INV_MASK	XT_TCP_INV_MASK
+#define IPT_UDP_INV_SRCPT	XT_UDP_INV_SRCPT
+#define IPT_UDP_INV_DSTPT	XT_UDP_INV_DSTPT
+#define IPT_UDP_INV_MASK	XT_UDP_INV_MASK
+
+/* The argument to IPT_SO_ADD_COUNTERS. */
+#define ipt_counters_info xt_counters_info
+/* Standard return verdict, or do jump. */
+#define IPT_STANDARD_TARGET XT_STANDARD_TARGET
+/* Error verdict. */
+#define IPT_ERROR_TARGET XT_ERROR_TARGET
+
+/* fn returns 0 to continue iteration */
+#define IPT_MATCH_ITERATE(e, fn, args...) \
+	XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)
+
+/* fn returns 0 to continue iteration */
+#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
+	XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
 
 /* Yes, Virginia, you have to zero the padding. */
 struct ipt_ip {
@@ -38,20 +73,14 @@
 	unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
 
 	/* Protocol, 0 = ANY */
-	u_int16_t proto;
+	__u16 proto;
 
 	/* Flags word */
-	u_int8_t flags;
+	__u8 flags;
 	/* Inverse flags */
-	u_int8_t invflags;
+	__u8 invflags;
 };
 
-#define ipt_entry_match xt_entry_match
-#define ipt_entry_target xt_entry_target
-#define ipt_standard_target xt_standard_target
-
-#define ipt_counters xt_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_GOTO		0x02	/* Set if jump is a goto */
@@ -77,9 +106,9 @@
 	unsigned int nfcache;
 
 	/* Size of ipt_entry + matches */
-	u_int16_t target_offset;
+	__u16 target_offset;
 	/* Size of ipt_entry + matches + target */
-	u_int16_t next_offset;
+	__u16 next_offset;
 
 	/* Back pointer */
 	unsigned int comefrom;
@@ -110,28 +139,11 @@
 #define IPT_SO_GET_REVISION_TARGET	(IPT_BASE_CTL + 3)
 #define IPT_SO_GET_MAX			IPT_SO_GET_REVISION_TARGET
 
-#define IPT_CONTINUE XT_CONTINUE
-#define IPT_RETURN XT_RETURN
-
-#include <linux/netfilter/xt_tcpudp.h>
-#define ipt_udp xt_udp
-#define ipt_tcp xt_tcp
-
-#define IPT_TCP_INV_SRCPT	XT_TCP_INV_SRCPT
-#define IPT_TCP_INV_DSTPT	XT_TCP_INV_DSTPT
-#define IPT_TCP_INV_FLAGS	XT_TCP_INV_FLAGS
-#define IPT_TCP_INV_OPTION	XT_TCP_INV_OPTION
-#define IPT_TCP_INV_MASK	XT_TCP_INV_MASK
-
-#define IPT_UDP_INV_SRCPT	XT_UDP_INV_SRCPT
-#define IPT_UDP_INV_DSTPT	XT_UDP_INV_DSTPT
-#define IPT_UDP_INV_MASK	XT_UDP_INV_MASK
-
 /* 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 */
+	__u8 type;				/* type to match */
+	__u8 code[2];				/* range of code */
+	__u8 invflags;				/* Inverse flags */
 };
 
 /* Values for "inv" field for struct ipt_icmp. */
@@ -140,7 +152,7 @@
 /* The argument to IPT_SO_GET_INFO */
 struct ipt_getinfo {
 	/* Which table: caller fills this in. */
-	char name[IPT_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* Kernel fills these in. */
 	/* Which hook entry points are valid: bitmask */
@@ -162,7 +174,7 @@
 /* The argument to IPT_SO_SET_REPLACE. */
 struct ipt_replace {
 	/* Which table. */
-	char name[IPT_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* Which hook entry points are valid: bitmask.  You can't
            change this. */
@@ -190,13 +202,10 @@
 	struct ipt_entry entries[0];
 };
 
-/* The argument to IPT_SO_ADD_COUNTERS. */
-#define ipt_counters_info xt_counters_info
-
 /* The argument to IPT_SO_GET_ENTRIES. */
 struct ipt_get_entries {
 	/* Which table: user fills this in. */
-	char name[IPT_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* User fills this in: total entry size. */
 	unsigned int size;
@@ -205,26 +214,13 @@
 	struct ipt_entry entrytable[0];
 };
 
-/* Standard return verdict, or do jump. */
-#define IPT_STANDARD_TARGET XT_STANDARD_TARGET
-/* Error verdict. */
-#define IPT_ERROR_TARGET XT_ERROR_TARGET
-
 /* Helper functions */
-static __inline__ struct ipt_entry_target *
+static __inline__ struct xt_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...) \
-	XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)
-
-/* fn returns 0 to continue iteration */
-#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
-	XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
-
 /*
  *	Main firewall chains definitions and global var's definitions.
  */
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 958b7f8..5c4f087 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -1,14 +1,14 @@
 #ifndef __LINUX_NETLINK_H
 #define __LINUX_NETLINK_H
 
-#include <linux/socket.h> /* for sa_family_t */
+#include <linux/socket.h> /* for __kernel_sa_family_t */
 #include <linux/types.h>
 
 #define NETLINK_ROUTE		0	/* Routing/device hook				*/
 #define NETLINK_UNUSED		1	/* Unused number				*/
 #define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
 #define NETLINK_FIREWALL	3	/* Firewalling hook				*/
-#define NETLINK_INET_DIAG	4	/* INET socket monitoring			*/
+#define NETLINK_SOCK_DIAG	4	/* socket monitoring				*/
 #define NETLINK_NFLOG		5	/* netfilter/iptables ULOG */
 #define NETLINK_XFRM		6	/* ipsec */
 #define NETLINK_SELINUX		7	/* SELinux event notifications */
@@ -24,13 +24,15 @@
 /* leave room for NETLINK_DM (DM Events) */
 #define NETLINK_SCSITRANSPORT	18	/* SCSI Transports */
 #define NETLINK_ECRYPTFS	19
+#define NETLINK_RDMA		20
+#define NETLINK_CRYPTO		21	/* Crypto layer */
+
+#define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
 
 #define MAX_LINKS 32		
 
-struct net;
-
 struct sockaddr_nl {
-	sa_family_t	nl_family;	/* AF_NETLINK	*/
+	__kernel_sa_family_t	nl_family;	/* AF_NETLINK	*/
 	unsigned short	nl_pad;		/* zero		*/
 	__u32		nl_pid;		/* port ID	*/
        	__u32		nl_groups;	/* multicast groups mask */
@@ -50,6 +52,7 @@
 #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 		*/
+#define NLM_F_DUMP_INTR		16	/* Dump was inconsistent due to sequence change */
 
 /* Modifiers to GET request */
 #define NLM_F_ROOT	0x100	/* specify tree	root	*/
@@ -72,7 +75,7 @@
    Check		NLM_F_EXCL
  */
 
-#define NLMSG_ALIGNTO	4
+#define NLMSG_ALIGNTO	4U
 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
 #define NLMSG_HDRLEN	 ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
 #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index 7f6ba86..defbde2 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -332,6 +332,7 @@
 	FLOW_KEY_SKUID,
 	FLOW_KEY_SKGID,
 	FLOW_KEY_VLAN_TAG,
+	FLOW_KEY_RXHASH,
 	__FLOW_KEY_MAX,
 };
 
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 2cfa4bc..410b33d 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -30,7 +30,7 @@
  */
 
 struct tc_stats {
-	__u64	bytes;			/* NUmber of enqueues bytes */
+	__u64	bytes;			/* Number of enqueued bytes */
 	__u32	packets;		/* Number of enqueued packets	*/
 	__u32	drops;			/* Packets dropped because of lack of resources */
 	__u32	overlimits;		/* Number of throttle events when this
@@ -127,6 +127,27 @@
 	__u16	max_bands;		/* Maximum number of queues */
 };
 
+/* PLUG section */
+
+#define TCQ_PLUG_BUFFER                0
+#define TCQ_PLUG_RELEASE_ONE           1
+#define TCQ_PLUG_RELEASE_INDEFINITE    2
+#define TCQ_PLUG_LIMIT                 3
+
+struct tc_plug_qopt {
+	/* TCQ_PLUG_BUFFER: Inset a plug into the queue and
+	 *  buffer any incoming packets
+	 * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head
+	 *   to beginning of the next plug.
+	 * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue.
+	 *   Stop buffering packets until the next TCQ_PLUG_BUFFER
+	 *   command is received (just act as a pass-thru queue).
+	 * TCQ_PLUG_LIMIT: Increase/decrease queue size
+	 */
+	int             action;
+	__u32           limit;
+};
+
 /* TBF section */
 
 struct tc_tbf_qopt {
@@ -162,25 +183,44 @@
 	unsigned	flows;		/* Maximal number of flows  */
 };
 
+struct tc_sfqred_stats {
+	__u32           prob_drop;      /* Early drops, below max threshold */
+	__u32           forced_drop;	/* Early drops, after max threshold */
+	__u32           prob_mark;      /* Marked packets, below max threshold */
+	__u32           forced_mark;    /* Marked packets, after max threshold */
+	__u32           prob_mark_head; /* Marked packets, below max threshold */
+	__u32           forced_mark_head;/* Marked packets, after max threshold */
+};
+
+struct tc_sfq_qopt_v1 {
+	struct tc_sfq_qopt v0;
+	unsigned int	depth;		/* max number of packets per flow */
+	unsigned int	headdrop;
+/* SFQRED parameters */
+	__u32		limit;		/* HARD maximal flow 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;
+	__u32		max_P;		/* probability, high resolution */
+/* SFQRED stats */
+	struct tc_sfqred_stats stats;
+};
+
+
 struct tc_sfq_xstats {
 	__s32		allot;
 };
 
-/*
- *  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_P,
 	__TCA_RED_MAX,
 };
 
@@ -194,8 +234,9 @@
 	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
-#define TC_RED_HARDDROP	2
+#define TC_RED_ECN		1
+#define TC_RED_HARDDROP		2
+#define TC_RED_ADAPTATIVE	4
 };
 
 struct tc_red_xstats {
@@ -214,6 +255,7 @@
        TCA_GRED_PARMS,
        TCA_GRED_STAB,
        TCA_GRED_DPS,
+       TCA_GRED_MAX_P,
 	   __TCA_GRED_MAX,
 };
 
@@ -223,7 +265,7 @@
 	__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		DP;           /* up to 2^32 DPs */
 	__u32		backlog;
 	__u32		qave;
 	__u32		forced;
@@ -247,6 +289,36 @@
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	TCA_CHOKE_MAX_P,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* Hard queue length (packets)	*/
+	__u32		qth_min;	/* Min average threshold (packets) */
+	__u32		qth_max;	/* Max average threshold (packets) */
+	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;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32		early;          /* Early drops */
+	__u32		pdrop;          /* Drops due to queue limits */
+	__u32		other;          /* Drops due to drop() calls */
+	__u32		marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
@@ -268,7 +340,7 @@
 	__u32 debug;		/* debug flags */
 
 	/* stats */
-	__u32 direct_pkts; /* count of non shapped packets */
+	__u32 direct_pkts; /* count of non shaped packets */
 };
 enum {
 	TCA_HTB_UNSPEC,
@@ -435,6 +507,8 @@
 	TCA_NETEM_DELAY_DIST,
 	TCA_NETEM_REORDER,
 	TCA_NETEM_CORRUPT,
+	TCA_NETEM_LOSS,
+	TCA_NETEM_RATE,
 	__TCA_NETEM_MAX,
 };
 
@@ -465,7 +539,40 @@
 	__u32	correlation;
 };
 
+struct tc_netem_rate {
+	__u32	rate;	/* byte/s */
+	__s32	packet_overhead;
+	__u32	cell_size;
+	__s32	cell_overhead;
+};
+
+enum {
+	NETEM_LOSS_UNSPEC,
+	NETEM_LOSS_GI,		/* General Intuitive - 4 state model */
+	NETEM_LOSS_GE,		/* Gilbert Elliot models */
+	__NETEM_LOSS_MAX
+};
+#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1)
+
+/* State transition probabilities for 4 state model */
+struct tc_netem_gimodel {
+	__u32	p13;
+	__u32	p31;
+	__u32	p32;
+	__u32	p14;
+	__u32	p23;
+};
+
+/* Gilbert-Elliot models */
+struct tc_netem_gemodel {
+	__u32 p;
+	__u32 r;
+	__u32 h;
+	__u32 k1;
+};
+
 #define NETEM_DIST_SCALE	8192
+#define NETEM_DIST_MAX		16384
 
 /* DRR */
 
@@ -481,4 +588,70 @@
 	__u32	deficit;
 };
 
+/* MQPRIO */
+#define TC_QOPT_BITMASK 15
+#define TC_QOPT_MAX_QUEUE 16
+
+struct tc_mqprio_qopt {
+	__u8	num_tc;
+	__u8	prio_tc_map[TC_QOPT_BITMASK + 1];
+	__u8	hw;
+	__u16	count[TC_QOPT_MAX_QUEUE];
+	__u16	offset[TC_QOPT_MAX_QUEUE];
+};
+
+/* SFB */
+
+enum {
+	TCA_SFB_UNSPEC,
+	TCA_SFB_PARMS,
+	__TCA_SFB_MAX,
+};
+
+#define TCA_SFB_MAX (__TCA_SFB_MAX - 1)
+
+/*
+ * Note: increment, decrement are Q0.16 fixed-point values.
+ */
+struct tc_sfb_qopt {
+	__u32 rehash_interval;	/* delay between hash move, in ms */
+	__u32 warmup_time;	/* double buffering warmup time in ms (warmup_time < rehash_interval) */
+	__u32 max;		/* max len of qlen_min */
+	__u32 bin_size;		/* maximum queue length per bin */
+	__u32 increment;	/* probability increment, (d1 in Blue) */
+	__u32 decrement;	/* probability decrement, (d2 in Blue) */
+	__u32 limit;		/* max SFB queue length */
+	__u32 penalty_rate;	/* inelastic flows are rate limited to 'rate' pps */
+	__u32 penalty_burst;
+};
+
+struct tc_sfb_xstats {
+	__u32 earlydrop;
+	__u32 penaltydrop;
+	__u32 bucketdrop;
+	__u32 queuedrop;
+	__u32 childdrop; /* drops in child qdisc */
+	__u32 marked;
+	__u32 maxqlen;
+	__u32 maxprob;
+	__u32 avgprob;
+};
+
+#define SFB_MAX_PROB 0xFFFF
+
+/* QFQ */
+enum {
+	TCA_QFQ_UNSPEC,
+	TCA_QFQ_WEIGHT,
+	TCA_QFQ_LMAX,
+	__TCA_QFQ_MAX
+};
+
+#define TCA_QFQ_MAX	(__TCA_QFQ_MAX - 1)
+
+struct tc_qfq_stats {
+	__u32 weight;
+	__u32 lmax;
+};
+
 #endif
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 0d8ef9e..d5b7fdd 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -282,6 +282,7 @@
 	RTA_SESSION, /* no longer used */
 	RTA_MP_ALGO, /* no longer used */
 	RTA_TABLE,
+	RTA_MARK,
 	__RTA_MAX
 };
 
@@ -582,6 +583,8 @@
 #define RTNLGRP_PHONET_IFADDR	RTNLGRP_PHONET_IFADDR
 	RTNLGRP_PHONET_ROUTE,
 #define RTNLGRP_PHONET_ROUTE	RTNLGRP_PHONET_ROUTE
+	RTNLGRP_DCB,
+#define RTNLGRP_DCB		RTNLGRP_DCB
 	__RTNLGRP_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
@@ -597,6 +600,9 @@
 #define TCA_ACT_TAB 1 /* attr type must be >=1 */	
 #define TCAA_MAX 1
 
+/* New extended info filters for IFLA_EXT_MASK */
+#define RTEXT_FILTER_VF		(1 << 0)
+
 /* End of information exported to user level */
 
 
diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h
new file mode 100644
index 0000000..39e4b1c
--- /dev/null
+++ b/include/linux/sock_diag.h
@@ -0,0 +1,25 @@
+#ifndef __SOCK_DIAG_H__
+#define __SOCK_DIAG_H__
+
+#include <linux/types.h>
+
+#define SOCK_DIAG_BY_FAMILY 20
+
+struct sock_diag_req {
+	__u8	sdiag_family;
+	__u8	sdiag_protocol;
+};
+
+enum {
+	SK_MEMINFO_RMEM_ALLOC,
+	SK_MEMINFO_RCVBUF,
+	SK_MEMINFO_WMEM_ALLOC,
+	SK_MEMINFO_SNDBUF,
+	SK_MEMINFO_FWD_ALLOC,
+	SK_MEMINFO_WMEM_QUEUED,
+	SK_MEMINFO_OPTMEM,
+
+	SK_MEMINFO_VARS,
+};
+
+#endif
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 1057869..8c1e501 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -8,8 +8,10 @@
 #define _K_SS_ALIGNSIZE	(__alignof__ (struct sockaddr *))
 				/* Implementation specific desired alignment */
 
+typedef unsigned short __kernel_sa_family_t;
+
 struct __kernel_sockaddr_storage {
-	unsigned short	ss_family;		/* address family */
+	__kernel_sa_family_t	ss_family;		/* address family */
 	/* Following field(s) are implementation specific */
 	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
 				/* space to achieve desired size, */
diff --git a/include/linux/tc_act/tc_csum.h b/include/linux/tc_act/tc_csum.h
new file mode 100644
index 0000000..a047c49
--- /dev/null
+++ b/include/linux/tc_act/tc_csum.h
@@ -0,0 +1,32 @@
+#ifndef __LINUX_TC_CSUM_H
+#define __LINUX_TC_CSUM_H
+
+#include <linux/types.h>
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_CSUM 16
+
+enum {
+	TCA_CSUM_UNSPEC,
+	TCA_CSUM_PARMS,
+	TCA_CSUM_TM,
+	__TCA_CSUM_MAX
+};
+#define TCA_CSUM_MAX (__TCA_CSUM_MAX - 1)
+
+enum {
+	TCA_CSUM_UPDATE_FLAG_IPV4HDR = 1,
+	TCA_CSUM_UPDATE_FLAG_ICMP    = 2,
+	TCA_CSUM_UPDATE_FLAG_IGMP    = 4,
+	TCA_CSUM_UPDATE_FLAG_TCP     = 8,
+	TCA_CSUM_UPDATE_FLAG_UDP     = 16,
+	TCA_CSUM_UPDATE_FLAG_UDPLITE = 32
+};
+
+struct tc_csum {
+	tc_gen;
+
+	__u32 update_flags;
+};
+
+#endif /* __LINUX_TC_CSUM_H */
diff --git a/include/linux/tc_ematch/tc_em_meta.h b/include/linux/tc_ematch/tc_em_meta.h
index 0864206..b11f8ce 100644
--- a/include/linux/tc_ematch/tc_em_meta.h
+++ b/include/linux/tc_ematch/tc_em_meta.h
@@ -67,7 +67,7 @@
 	TCF_META_ID_SK_FORWARD_ALLOCS,
 	TCF_META_ID_SK_SNDBUF,
  	TCF_META_ID_SK_ALLOCS,
- 	TCF_META_ID_SK_ROUTE_CAPS,
+	__TCF_META_ID_SK_ROUTE_CAPS,	/* unimplemented but in ABI already */
  	TCF_META_ID_SK_HASH,
  	TCF_META_ID_SK_LINGERTIME,
  	TCF_META_ID_SK_ACK_BACKLOG,
@@ -79,6 +79,7 @@
  	TCF_META_ID_SK_SENDMSG_OFF,
  	TCF_META_ID_SK_WRITE_PENDING,
 	TCF_META_ID_VLAN_TAG,
+	TCF_META_ID_RXHASH,
 	__TCF_META_ID_MAX
 };
 #define TCF_META_ID_MAX (__TCF_META_ID_MAX - 1)
diff --git a/include/linux/tc_ematch/tc_em_text.h b/include/linux/tc_ematch/tc_em_text.h
new file mode 100644
index 0000000..5aac404
--- /dev/null
+++ b/include/linux/tc_ematch/tc_em_text.h
@@ -0,0 +1,19 @@
+#ifndef __LINUX_TC_EM_TEXT_H
+#define __LINUX_TC_EM_TEXT_H
+
+#include <linux/types.h>
+#include <linux/pkt_cls.h>
+
+#define TC_EM_TEXT_ALGOSIZ	16
+
+struct tcf_em_text {
+	char		algo[TC_EM_TEXT_ALGOSIZ];
+	__u16		from_offset;
+	__u16		to_offset;
+	__u16		pattern_len;
+	__u8		from_layer:4;
+	__u8		to_layer:4;
+	__u8		pad;
+};
+
+#endif
diff --git a/include/linux/types.h b/include/linux/types.h
index 8b483c8..23ea78f 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -34,5 +34,18 @@
 typedef __u16 __bitwise __sum16;
 typedef __u32 __bitwise __wsum;
 
+/*
+ * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid
+ * common 32/64-bit compat problems.
+ * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other
+ * architectures) and to 8-byte boundaries on 64-bit architectures.  The new
+ * aligned_64 type enforces 8-byte alignment so that structs containing
+ * aligned_64 values have the same alignment on 32-bit and 64-bit architectures.
+ * No conversions are necessary between 32-bit user-space and a 64-bit kernel.
+ */
+#define __aligned_u64 __u64 __attribute__((aligned(8)))
+#define __aligned_be64 __be64 __attribute__((aligned(8)))
+#define __aligned_le64 __le64 __attribute__((aligned(8)))
+
 #endif /*  __ASSEMBLY__ */
 #endif /* _LINUX_TYPES_H */
diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h
new file mode 100644
index 0000000..b1d2bf1
--- /dev/null
+++ b/include/linux/unix_diag.h
@@ -0,0 +1,54 @@
+#ifndef __UNIX_DIAG_H__
+#define __UNIX_DIAG_H__
+
+#include <linux/types.h>
+
+struct unix_diag_req {
+	__u8	sdiag_family;
+	__u8	sdiag_protocol;
+	__u16	pad;
+	__u32	udiag_states;
+	__u32	udiag_ino;
+	__u32	udiag_show;
+	__u32	udiag_cookie[2];
+};
+
+#define UDIAG_SHOW_NAME		0x00000001	/* show name (not path) */
+#define UDIAG_SHOW_VFS		0x00000002	/* show VFS inode info */
+#define UDIAG_SHOW_PEER		0x00000004	/* show peer socket info */
+#define UDIAG_SHOW_ICONS	0x00000008	/* show pending connections */
+#define UDIAG_SHOW_RQLEN	0x00000010	/* show skb receive queue len */
+#define UDIAG_SHOW_MEMINFO	0x00000020	/* show memory info of a socket */
+
+struct unix_diag_msg {
+	__u8	udiag_family;
+	__u8	udiag_type;
+	__u8	udiag_state;
+	__u8	pad;
+
+	__u32	udiag_ino;
+	__u32	udiag_cookie[2];
+};
+
+enum {
+	UNIX_DIAG_NAME,
+	UNIX_DIAG_VFS,
+	UNIX_DIAG_PEER,
+	UNIX_DIAG_ICONS,
+	UNIX_DIAG_RQLEN,
+	UNIX_DIAG_MEMINFO,
+
+	UNIX_DIAG_MAX,
+};
+
+struct unix_diag_vfs {
+	__u32	udiag_vfs_ino;
+	__u32	udiag_vfs_dev;
+};
+
+struct unix_diag_rqlen {
+	__u32	udiag_rqueue;
+	__u32	udiag_wqueue;
+};
+
+#endif
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 07f2b63..0aa3805 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -84,6 +84,16 @@
 	__u32	bitmap;
 };
 
+struct xfrm_replay_state_esn {
+	unsigned int	bmp_len;
+	__u32		oseq;
+	__u32		seq;
+	__u32		oseq_hi;
+	__u32		seq_hi;
+	__u32		replay_window;
+	__u32		bmp[0];
+};
+
 struct xfrm_algo {
 	char		alg_name[64];
 	unsigned int	alg_key_len;    /* in bits */
@@ -283,6 +293,8 @@
 	XFRMA_KMADDRESS,        /* struct xfrm_user_kmaddress */
 	XFRMA_ALG_AUTH_TRUNC,	/* struct xfrm_algo_auth */
 	XFRMA_MARK,		/* struct xfrm_mark */
+	XFRMA_TFCPAD,		/* __u32 */
+	XFRMA_REPLAY_ESN_VAL,	/* struct xfrm_replay_esn */
 	__XFRMA_MAX
 
 #define XFRMA_MAX (__XFRMA_MAX - 1)
@@ -349,6 +361,8 @@
 #define XFRM_STATE_WILDRECV	8
 #define XFRM_STATE_ICMP		16
 #define XFRM_STATE_AF_UNSPEC	32
+#define XFRM_STATE_ALIGN4	64
+#define XFRM_STATE_ESN		128
 };
 
 struct xfrm_usersa_id {
diff --git a/include/netinet/tcp.h b/include/netinet/tcp.h
index 282b29c..3f890a1 100644
--- a/include/netinet/tcp.h
+++ b/include/netinet/tcp.h
@@ -172,6 +172,7 @@
 # define TCPI_OPT_SACK		2
 # define TCPI_OPT_WSCALE	4
 # define TCPI_OPT_ECN		8
+# define TCPI_OPT_ECN_SEEN	16
 
 /* Values for tcpi_state.  */
 enum tcp_ca_state
diff --git a/include/rt_names.h b/include/rt_names.h
index 07a10e0..e5dbd45 100644
--- a/include/rt_names.h
+++ b/include/rt_names.h
@@ -13,6 +13,7 @@
 int rtnl_rttable_a2n(__u32 *id, char *arg);
 int rtnl_rtrealm_a2n(__u32 *id, char *arg);
 int rtnl_dsfield_a2n(__u32 *id, char *arg);
+int rtnl_group_a2n(int *id, char *arg);
 
 const char *inet_proto_n2a(int proto, char *buf, int len);
 int inet_proto_a2n(char *buf);
diff --git a/include/utils.h b/include/utils.h
index f7ef939..496db68 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -17,6 +17,7 @@
 extern int oneline;
 extern int timestamp;
 extern char * _SL_;
+extern int max_flush_loops;
 
 #ifndef IPPROTO_ESP
 #define IPPROTO_ESP	50
@@ -78,12 +79,13 @@
 
 extern int get_integer(int *val, const char *arg, int base);
 extern int get_unsigned(unsigned *val, const char *arg, int base);
-extern int get_jiffies(unsigned *val, const char *arg, int base, int *raw);
+extern int get_time_rtt(unsigned *val, const char *arg, int *raw);
 #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_s32(__s32 *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);
@@ -150,5 +152,6 @@
 
 struct iplink_req;
 int iplink_parse(int argc, char **argv, struct iplink_req *req,
-		char **name, char **type, char **link, char **dev);
+		char **name, char **type, char **link, char **dev,
+		int *group);
 #endif /* __UTILS_H__ */
diff --git a/ip/Android.mk b/ip/Android.mk
index cedc7f5..f0b63b8 100644
--- a/ip/Android.mk
+++ b/ip/Android.mk
@@ -1,11 +1,12 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := ip.c ipaddress.c ipaddrlabel.c iproute.c iprule.c   \
-	rtm_map.c iptunnel.c ip6tunnel.c tunnel.c ipneigh.c ipntable.c \
-	iplink.c ipmaddr.c ipmonitor.c ipmroute.c ipprefix.c ipxfrm.c  \
-	xfrm_state.c xfrm_policy.c xfrm_monitor.c iplink_vlan.c        \
-	link_veth.c link_gre.c iplink_can.c iptuntap.c
+LOCAL_SRC_FILES := ip.c ipaddress.c ipaddrlabel.c iproute.c iprule.c ipnetns.c \
+        rtm_map.c iptunnel.c ip6tunnel.c tunnel.c ipneigh.c ipntable.c iplink.c \
+        ipmaddr.c ipmonitor.c ipmroute.c ipprefix.c iptuntap.c \
+        ipxfrm.c xfrm_state.c xfrm_policy.c xfrm_monitor.c \
+        iplink_vlan.c link_veth.c link_gre.c iplink_can.c \
+        iplink_macvlan.c iplink_macvtap.c ipl2tp.c
 
 LOCAL_MODULE := ip
 
diff --git a/ip/Makefile b/ip/Makefile
index 2f223ca..e029ea1 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -1,21 +1,28 @@
-IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o \
+IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \
     ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \
     ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
     iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
-    iplink_macvlan.o
+    iplink_macvlan.o iplink_macvtap.o ipl2tp.o
 
 RTMONOBJ=rtmon.o
 
+include ../Config
+
+ifeq ($(IP_CONFIG_SETNS),y)
+	CFLAGS += -DHAVE_SETNS
+endif
+
 ALLOBJ=$(IPOBJ) $(RTMONOBJ)
 SCRIPTS=ifcfg rtpr routel routef
 TARGETS=ip rtmon
 
-all: $(TARGETS) $(SCRIPTS) $(LIBS)
+all: $(TARGETS) $(SCRIPTS)
 
-ip: $(IPOBJ) $(LIBNETLINK) $(LIBUTIL)
+ip: $(IPOBJ) $(LIBNETLINK)
 
-rtmon: $(RTMONOBJ) $(LIBNETLINK)
+
+rtmon: $(RTMONOBJ)
 
 install: all
 	install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR)
diff --git a/ip/ip.c b/ip/ip.c
index 9d6f169..6486685 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -32,6 +32,8 @@
 char * _SL_ = NULL;
 char *batch_file = NULL;
 int force = 0;
+int max_flush_loops = 10;
+
 struct rtnl_handle rth = { .fd = -1 };
 
 static void usage(void) __attribute__((noreturn));
@@ -42,9 +44,11 @@
 "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
 "       ip [ -force ] -batch filename\n"
 "where  OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n"
-"                   tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm }\n"
+"                   tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm |\n"
+"                   netns | l2tp }\n"
 "       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
 "                    -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
+"                    -l[oops] { maximum-addr-flush-attempts } |\n"
 "                    -o[neline] | -t[imestamp] | -b[atch] [filename] |\n"
 "                    -rc[vbuf] [size]}\n");
 	exit(-1);
@@ -69,6 +73,7 @@
 	{ "ntable",	do_ipntable },
 	{ "ntbl",	do_ipntable },
 	{ "link",	do_iplink },
+	{ "l2tp",	do_ipl2tp },
 	{ "tunnel",	do_iptunnel },
 	{ "tunl",	do_iptunnel },
 	{ "tuntap",	do_iptuntap },
@@ -77,6 +82,7 @@
 	{ "xfrm",	do_xfrm },
 	{ "mroute",	do_multiroute },
 	{ "mrule",	do_multirule },
+	{ "netns",	do_netns },
 	{ "help",	do_help },
 	{ 0,		0 }
 };
@@ -86,12 +92,13 @@
 	const struct cmd *c;
 
 	for (c = cmds; c->cmd; ++c) {
-		if (matches(argv0, c->cmd) == 0)
-			return c->func(argc-1, argv+1);
+		if (matches(argv0, c->cmd) == 0) {
+			return -(c->func(argc-1, argv+1));
+		}
 	}
 
 	fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv0);
-	return -1;
+	return EXIT_FAILURE;
 }
 
 #ifndef ANDROID
@@ -99,19 +106,19 @@
 {
 	char *line = NULL;
 	size_t len = 0;
-	int ret = 0;
+	int ret = EXIT_SUCCESS;
 
 	if (name && strcmp(name, "-") != 0) {
 		if (freopen(name, "r", stdin) == NULL) {
 			fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n",
 				name, strerror(errno));
-			return -1;
+			return EXIT_FAILURE;
 		}
 	}
 
 	if (rtnl_open(&rth, 0) < 0) {
 		fprintf(stderr, "Cannot open rtnetlink\n");
-		return -1;
+		return EXIT_FAILURE;
 	}
 
 	cmdlineno = 0;
@@ -125,7 +132,7 @@
 
 		if (do_cmd(largv[0], largc, largv)) {
 			fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
-			ret = 1;
+			ret = EXIT_FAILURE;
 			if (!force)
 				break;
 		}
@@ -158,7 +165,13 @@
 			break;
 		if (opt[1] == '-')
 			opt++;
-		if (matches(opt, "-family") == 0) {
+		if (matches(opt, "-loops") == 0) {
+			argc--;
+			argv++;
+			if (argc <= 1)
+				usage();
+                        max_flush_loops = atoi(argv[1]);
+                } else if (matches(opt, "-family") == 0) {
 			argc--;
 			argv++;
 			if (argc <= 1)
diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c
index 203e4a3..d5bee36 100644
--- a/ip/ip6tunnel.c
+++ b/ip/ip6tunnel.c
@@ -36,6 +36,7 @@
 
 #include "utils.h"
 #include "tunnel.h"
+#include "ip_common.h"
 
 #define IP6_FLOWINFO_TCLASS	htonl(0x0FF00000)
 #define IP6_FLOWINFO_FLOWLABEL	htonl(0x000FFFFF)
@@ -75,7 +76,7 @@
 	printf("%s: %s/ipv6 remote %s local %s",
 	       p->name, tnl_strproto(p->proto), remote, local);
 	if (p->link) {
-		char *n = tnl_ioctl_get_ifname(p->link);
+		const char *n = ll_index_to_name(p->link);
 		if (n)
 			printf(" dev %s", n);
 	}
@@ -105,8 +106,9 @@
 		printf(" dscp inherit");
 }
 
-static int parse_args(int argc, char **argv, struct ip6_tnl_parm *p)
+static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
 {
+	int count = 0;
 	char medium[IFNAMSIZ];
 
 	memset(medium, 0, sizeof(medium));
@@ -206,11 +208,19 @@
 			if (p->name[0])
 				duparg2("name", *argv);
 			strncpy(p->name, *argv, IFNAMSIZ - 1);
+			if (cmd == SIOCCHGTUNNEL && count == 0) {
+				struct ip6_tnl_parm old_p;
+				memset(&old_p, 0, sizeof(old_p));
+				if (tnl_get_ioctl(*argv, &old_p))
+					return -1;
+				*p = old_p;
+			}
 		}
+		count++;
 		argc--; argv++;
 	}
 	if (medium[0]) {
-		p->link = tnl_ioctl_get_ifindex(medium);
+		p->link = ll_name_to_index(medium);
 		if (p->link == 0)
 			return -1;
 	}
@@ -261,12 +271,15 @@
 	}
 
 	/* skip two lines at the begenning of the file */
-	fgets(buf, sizeof(buf), fp);
-	fgets(buf, sizeof(buf), fp);
+	if (!fgets(buf, sizeof(buf), fp) ||
+	    !fgets(buf, sizeof(buf), fp)) {
+		fprintf(stderr, "/proc/net/dev read error\n");
+		return -1;
+	}
 
 	while (fgets(buf, sizeof(buf), fp) != NULL) {
 		char name[IFNAMSIZ];
-		int type;
+		int index, type;
 		unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
 			rx_fifo, rx_frame,
 			tx_bytes, tx_packets, tx_errs, tx_drops,
@@ -288,7 +301,10 @@
 			continue;
 		if (p->name[0] && strcmp(p->name, name))
 			continue;
-		type = tnl_ioctl_get_iftype(name);
+		index = ll_name_to_index(name);
+		if (index == 0)
+			continue;
+		type = ll_index_to_type(index);
 		if (type == -1) {
 			fprintf(stderr, "Failed to get type of [%s]\n", name);
 			continue;
@@ -298,7 +314,7 @@
 		memset(&p1, 0, sizeof(p1));
 		ip6_tnl_parm_init(&p1, 0);
 		strcpy(p1.name, name);
-		p1.link = tnl_ioctl_get_ifindex(p1.name);
+		p1.link = ll_name_to_index(p1.name);
 		if (p1.link == 0)
 			continue;
 		if (tnl_get_ioctl(p1.name, &p1))
@@ -329,10 +345,11 @@
 {
         struct ip6_tnl_parm p;
 
+	ll_init_map(&rth);
 	ip6_tnl_parm_init(&p, 0);
 	p.proto = 0;  /* default to any */
 
-        if (parse_args(argc, argv, &p) < 0)
+        if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
                 return -1;
 
 	if (!p.name[0] || show_stats)
@@ -353,7 +370,7 @@
 
 	ip6_tnl_parm_init(&p, 1);
 
-	if (parse_args(argc, argv, &p) < 0)
+	if (parse_args(argc, argv, cmd, &p) < 0)
 		return -1;
 
 	return tnl_add_ioctl(cmd,
@@ -367,7 +384,7 @@
 
 	ip6_tnl_parm_init(&p, 1);
 
-	if (parse_args(argc, argv, &p) < 0)
+	if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
 		return -1;
 
 	return tnl_del_ioctl(p.name[0] ? p.name : "ip6tnl0", p.name, &p);
diff --git a/ip/ip_common.h b/ip/ip_common.h
index a114186..5fa2cc0 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -1,3 +1,4 @@
+extern int get_operstate(const char *name);
 extern int print_linkinfo(const struct sockaddr_nl *who,
 			  struct nlmsghdr *n,
 			  void *arg);
@@ -38,13 +39,15 @@
 extern int do_multiaddr(int argc, char **argv);
 extern int do_multiroute(int argc, char **argv);
 extern int do_multirule(int argc, char **argv);
+extern int do_netns(int argc, char **argv);
 extern int do_xfrm(int argc, char **argv);
+extern int do_ipl2tp(int argc, char **argv);
 
 static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
 {
 	__u32 table = r->rtm_table;
 	if (tb[RTA_TABLE])
-		table = *(__u32*) RTA_DATA(tb[RTA_TABLE]);
+		table = rta_getattr_u32(tb[RTA_TABLE]);
 	return table;
 }
 
@@ -64,6 +67,7 @@
 };
 
 struct link_util *get_link_kind(const char *kind);
+int get_netns_fd(const char *name);
 
 #ifndef	INFINITY_LIFE_TIME
 #define     INFINITY_LIFE_TIME      0xFFFFFFFFU
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 3a411b1..9ab65ec 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <inttypes.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
@@ -33,7 +34,6 @@
 #include "ll_map.h"
 #include "ip_common.h"
 
-#define MAX_ROUNDS 10
 
 static struct
 {
@@ -50,6 +50,7 @@
 	char *flushb;
 	int flushp;
 	int flushe;
+	int group;
 } filter;
 
 static int do_link;
@@ -130,26 +131,55 @@
 		fprintf(f, "state %s ", oper_states[state]);
 }
 
-static void print_queuelen(FILE *f, const char *name)
+int get_operstate(const char *name)
 {
-	struct ifreq ifr;
-	int s;
+	int i;
 
-	s = socket(AF_INET, SOCK_STREAM, 0);
-	if (s < 0)
-		return;
+	for (i = 0; i < sizeof(oper_states)/sizeof(oper_states[0]); i++)
+		if (strcasecmp(name, oper_states[i]) == 0)
+			return i;
+	return -1;
+}
 
-	memset(&ifr, 0, sizeof(ifr));
-	strcpy(ifr.ifr_name, name);
-	if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
-		fprintf(f, "ioctl(SIOCGIFXQLEN) failed: %s\n", strerror(errno));
+static void print_queuelen(FILE *f, struct rtattr *tb[IFLA_MAX + 1])
+{
+	int qlen;
+
+	if (tb[IFLA_TXQLEN])
+		qlen = *(int *)RTA_DATA(tb[IFLA_TXQLEN]);
+	else {
+		struct ifreq ifr;
+		int s = socket(AF_INET, SOCK_STREAM, 0);
+
+		if (s < 0)
+			return;
+
+		memset(&ifr, 0, sizeof(ifr));
+		strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME]));
+		if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
+			fprintf(f, "ioctl(SIOCGIFXQLEN) failed: %s\n", strerror(errno));
+			close(s);
+			return;
+		}
 		close(s);
-		return;
+		qlen = ifr.ifr_qlen;
 	}
-	close(s);
+	if (qlen)
+		fprintf(f, "qlen %d", qlen);
+}
 
-	if (ifr.ifr_qlen)
-		fprintf(f, "qlen %d", ifr.ifr_qlen);
+static const char *link_modes[] = {
+	"DEFAULT", "DORMANT"
+};
+
+static void print_linkmode(FILE *f, struct rtattr *tb)
+{
+	unsigned int mode = rta_getattr_u8(tb);
+
+	if (mode >= sizeof(link_modes) / sizeof(link_modes[0]))
+		fprintf(f, "mode %d ", mode);
+	else
+		fprintf(f, "mode %s ", link_modes[mode]);
 }
 
 static void print_linktype(FILE *fp, struct rtattr *tb)
@@ -192,7 +222,9 @@
 	struct ifla_vf_mac *vf_mac;
 	struct ifla_vf_vlan *vf_vlan;
 	struct ifla_vf_tx_rate *vf_tx_rate;
+	struct ifla_vf_spoofchk *vf_spoofchk;
 	struct rtattr *vf[IFLA_VF_MAX+1];
+	struct rtattr *tmp;
 	SPRINT_BUF(b1);
 
 	if (vfinfo->rta_type != IFLA_VF_INFO) {
@@ -206,6 +238,17 @@
 	vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
 	vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
 
+	/* Check if the spoof checking vf info type is supported by
+	 * this kernel.
+	 */
+	tmp = (struct rtattr *)((char *)vf[IFLA_VF_TX_RATE] +
+			vf[IFLA_VF_TX_RATE]->rta_len);
+
+	if (tmp->rta_type != IFLA_VF_SPOOFCHK)
+		vf_spoofchk = NULL;
+	else
+		vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
+
 	fprintf(fp, "\n    vf %d MAC %s", vf_mac->vf,
 		ll_addr_n2a((unsigned char *)&vf_mac->mac,
 		ETH_ALEN, 0, b1, sizeof(b1)));
@@ -215,6 +258,103 @@
 		fprintf(fp, ", qos %d", vf_vlan->qos);
 	if (vf_tx_rate->rate)
 		fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate);
+	if (vf_spoofchk && vf_spoofchk->setting != -1) {
+		if (vf_spoofchk->setting)
+			fprintf(fp, ", spoof checking on");
+		else
+			fprintf(fp, ", spoof checking off");
+	}
+}
+
+static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s) {
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+		s->rx_compressed ? "compressed" : "", _SL_);
+	fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+		(uint64_t)s->rx_bytes,
+		(uint64_t)s->rx_packets,
+		(uint64_t)s->rx_errors,
+		(uint64_t)s->rx_dropped,
+		(uint64_t)s->rx_over_errors,
+		(uint64_t)s->multicast);
+	if (s->rx_compressed)
+		fprintf(fp, " %-7"PRIu64"",
+			(uint64_t)s->rx_compressed);
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
+		fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+			(uint64_t)s->rx_length_errors,
+			(uint64_t)s->rx_crc_errors,
+			(uint64_t)s->rx_frame_errors,
+			(uint64_t)s->rx_fifo_errors,
+			(uint64_t)s->rx_missed_errors);
+	}
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+		(uint64_t)s->tx_compressed ? "compressed" : "", _SL_);
+	fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+		(uint64_t)s->tx_bytes,
+		(uint64_t)s->tx_packets,
+		(uint64_t)s->tx_errors,
+		(uint64_t)s->tx_dropped,
+		(uint64_t)s->tx_carrier_errors,
+		(uint64_t)s->collisions);
+	if (s->tx_compressed)
+		fprintf(fp, " %-7"PRIu64"",
+			(uint64_t)s->tx_compressed);
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
+		fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+			(uint64_t)s->tx_aborted_errors,
+			(uint64_t)s->tx_fifo_errors,
+			(uint64_t)s->tx_window_errors,
+			(uint64_t)s->tx_heartbeat_errors);
+	}
+}
+
+static void print_link_stats(FILE *fp, const struct rtnl_link_stats *s)
+{
+	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
+			);
+	}
 }
 
 int print_linkinfo(const struct sockaddr_nl *who,
@@ -247,11 +387,17 @@
 	    fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
 		return 0;
 
+	if (tb[IFLA_GROUP]) {
+		int group = *(int*)RTA_DATA(tb[IFLA_GROUP]);
+		if (group != filter.group)
+			return -1;
+	}
+
 	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>");
+		tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
 
 	if (tb[IFLA_LINK]) {
 		SPRINT_BUF(b1);
@@ -271,18 +417,20 @@
 	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
+		fprintf(fp, "qdisc %s ", rta_getattr_str(tb[IFLA_QDISC]));
 	if (tb[IFLA_MASTER]) {
 		SPRINT_BUF(b1);
 		fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
 	}
-#endif
+
 	if (tb[IFLA_OPERSTATE])
-		print_operstate(fp, *(__u8 *)RTA_DATA(tb[IFLA_OPERSTATE]));
-		
+		print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
+
+	if (do_link && tb[IFLA_LINKMODE])
+		print_linkmode(fp, tb[IFLA_LINKMODE]);
+
 	if (filter.showqueue)
-		print_queuelen(fp, (char*)RTA_DATA(tb[IFLA_IFNAME]));
+		print_queuelen(fp, tb);
 
 	if (!filter.family || filter.family == AF_PACKET) {
 		SPRINT_BUF(b1);
@@ -312,108 +460,15 @@
 
 	if (do_link && tb[IFLA_IFALIAS])
 		fprintf(fp,"\n    alias %s", 
-			(const char *) RTA_DATA(tb[IFLA_IFALIAS]));
+			rta_getattr_str(tb[IFLA_IFALIAS]));
 
-	if (do_link && tb[IFLA_STATS64] && show_stats) {
-		struct rtnl_link_stats64 slocal;
-		struct rtnl_link_stats64 *s = RTA_DATA(tb[IFLA_STATS64]);
-		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, "    %-10llu %-8llu %-7llu %-7llu %-7llu %-7llu",
-			(unsigned long long)s->rx_bytes,
-			(unsigned long long)s->rx_packets,
-			(unsigned long long)s->rx_errors,
-			(unsigned long long)s->rx_dropped,
-			(unsigned long long)s->rx_over_errors,
-			(unsigned long long)s->multicast);
-		if (s->rx_compressed)
-			fprintf(fp, " %-7llu",
-				(unsigned long long)s->rx_compressed);
-		if (show_stats > 1) {
-			fprintf(fp, "%s", _SL_);
-			fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
-			fprintf(fp, "               %-7llu  %-7llu %-7llu %-7llu %-7llu",
-				(unsigned long long)s->rx_length_errors,
-				(unsigned long long)s->rx_crc_errors,
-				(unsigned long long)s->rx_frame_errors,
-				(unsigned long long)s->rx_fifo_errors,
-				(unsigned long long)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, "    %-10llu %-8llu %-7llu %-7llu %-7llu %-7llu",
-			(unsigned long long)s->tx_bytes,
-			(unsigned long long)s->tx_packets,
-			(unsigned long long)s->tx_errors,
-			(unsigned long long)s->tx_dropped,
-			(unsigned long long)s->tx_carrier_errors,
-			(unsigned long long)s->collisions);
-		if (s->tx_compressed)
-			fprintf(fp, " %-7llu",
-				(unsigned long long)s->tx_compressed);
-		if (show_stats > 1) {
-			fprintf(fp, "%s", _SL_);
-			fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
-			fprintf(fp, "               %-7llu  %-7llu %-7llu %-7llu",
-				(unsigned long long)s->tx_aborted_errors,
-				(unsigned long long)s->tx_fifo_errors,
-				(unsigned long long)s->tx_window_errors,
-				(unsigned long long)s->tx_heartbeat_errors);
-		}
+	if (do_link && show_stats) {
+		if (tb[IFLA_STATS64])
+			print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]));
+		else if (tb[IFLA_STATS])
+			print_link_stats(fp, RTA_DATA(tb[IFLA_STATS]));
 	}
-	if (do_link && !tb[IFLA_STATS64] && 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
-				);
-		}
-	}
+
 	if (do_link && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) {
 		struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST];
 		int rem = RTA_PAYLOAD(vflist);
@@ -453,6 +508,8 @@
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	int deprecated = 0;
+	/* Use local copy of ifa_flags to not interfere with filtering code */
+	unsigned int ifa_flags;
 	struct rtattr * rta_tb[IFA_MAX+1];
 	char abuf[256];
 	SPRINT_BUF(b1);
@@ -572,61 +629,60 @@
 				    abuf, sizeof(abuf)));
 	}
 	fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+	ifa_flags = ifa->ifa_flags;
 	if (ifa->ifa_flags&IFA_F_SECONDARY) {
-		ifa->ifa_flags &= ~IFA_F_SECONDARY;
+		ifa_flags &= ~IFA_F_SECONDARY;
 		if (ifa->ifa_family == AF_INET6)
 			fprintf(fp, "temporary ");
 		else
 			fprintf(fp, "secondary ");
 	}
 	if (ifa->ifa_flags&IFA_F_TENTATIVE) {
-		ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+		ifa_flags &= ~IFA_F_TENTATIVE;
 		fprintf(fp, "tentative ");
 	}
 	if (ifa->ifa_flags&IFA_F_DEPRECATED) {
-		ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+		ifa_flags &= ~IFA_F_DEPRECATED;
 		deprecated = 1;
 		fprintf(fp, "deprecated ");
 	}
 	if (ifa->ifa_flags&IFA_F_HOMEADDRESS) {
-		ifa->ifa_flags &= ~IFA_F_HOMEADDRESS;
+		ifa_flags &= ~IFA_F_HOMEADDRESS;
 		fprintf(fp, "home ");
 	}
 	if (ifa->ifa_flags&IFA_F_NODAD) {
-		ifa->ifa_flags &= ~IFA_F_NODAD;
+		ifa_flags &= ~IFA_F_NODAD;
 		fprintf(fp, "nodad ");
 	}
 	if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
 		fprintf(fp, "dynamic ");
 	} else
-		ifa->ifa_flags &= ~IFA_F_PERMANENT;
+		ifa_flags &= ~IFA_F_PERMANENT;
 	if (ifa->ifa_flags&IFA_F_DADFAILED) {
-		ifa->ifa_flags &= ~IFA_F_DADFAILED;
+		ifa_flags &= ~IFA_F_DADFAILED;
 		fprintf(fp, "dadfailed ");
 	}
-	if (ifa->ifa_flags)
-		fprintf(fp, "flags %02x ", ifa->ifa_flags);
+	if (ifa_flags)
+		fprintf(fp, "flags %02x ", ifa_flags);
 	if (rta_tb[IFA_LABEL])
-		fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
+		fprintf(fp, "%s", rta_getattr_str(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_);
+		fprintf(fp, "       valid_lft ");
 		if (ci->ifa_valid == INFINITY_LIFE_TIME)
-			sprintf(buf, "valid_lft forever");
+			fprintf(fp, "forever");
 		else
-			sprintf(buf, "valid_lft %usec", ci->ifa_valid);
+			fprintf(fp, "%usec", ci->ifa_valid);
+		fprintf(fp, " preferred_lft ");
 		if (ci->ifa_prefered == INFINITY_LIFE_TIME)
-			sprintf(buf+strlen(buf), " preferred_lft forever");
+			fprintf(fp, "forever");
 		else {
 			if (deprecated)
-				sprintf(buf+strlen(buf), " preferred_lft %dsec",
-					ci->ifa_prefered);
+				fprintf(fp, "%dsec", ci->ifa_prefered);
 			else
-				sprintf(buf+strlen(buf), " preferred_lft %usec",
-					ci->ifa_prefered);
+				fprintf(fp, "%usec", ci->ifa_prefered);
 		}
-		fprintf(fp, "       %s", buf);
 	}
 	fprintf(fp, "\n");
 	fflush(fp);
@@ -638,7 +694,7 @@
 {
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
 
-	if (!ifa->ifa_flags & IFA_F_SECONDARY)
+	if (ifa->ifa_flags & IFA_F_SECONDARY)
 		return 0;
 
 	return print_addrinfo(who, n, arg);
@@ -649,7 +705,7 @@
 {
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
 
-	if (ifa->ifa_flags & IFA_F_SECONDARY)
+	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
 		return 0;
 
 	return print_addrinfo(who, n, arg);
@@ -718,9 +774,12 @@
 	if (filter.family == AF_UNSPEC)
 		filter.family = preferred_family;
 
+	filter.group = INIT_NETDEV_GROUP;
+
 	if (flush) {
 		if (argc <= 0) {
 			fprintf(stderr, "Flush requires arguments.\n");
+
 			return -1;
 		}
 		if (filter.family == AF_PACKET) {
@@ -779,6 +838,10 @@
 		} else if (strcmp(*argv, "label") == 0) {
 			NEXT_ARG();
 			filter.label = *argv;
+		} else if (strcmp(*argv, "group") == 0) {
+			NEXT_ARG();
+			if (rtnl_group_a2n(&filter.group, *argv))
+				invarg("Invalid \"group\" value\n", *argv);
 		} else {
 			if (strcmp(*argv, "dev") == 0) {
 				NEXT_ARG();
@@ -797,7 +860,7 @@
 		exit(1);
 	}
 
-	if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
@@ -818,25 +881,19 @@
 		filter.flushp = 0;
 		filter.flushe = sizeof(flushb);
 
-		while (round < MAX_ROUNDS) {
+		while ((max_flush_loops == 0) || (round < max_flush_loops)) {
 			const struct rtnl_dump_filter_arg a[3] = {
 				{
 					.filter = print_addrinfo_secondary,
 					.arg1 = stdout,
-					.junk = NULL,
-					.arg2 = NULL
 				},
 				{
 					.filter = print_addrinfo_primary,
 					.arg1 = stdout,
-					.junk = NULL,
-					.arg2 = NULL
 				},
 				{
 					.filter = NULL,
 					.arg1 = NULL,
-					.junk = NULL,
-					.arg2 = NULL
 				},
 			};
 			if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
@@ -849,6 +906,7 @@
 				exit(1);
 			}
 			if (filter.flushed == 0) {
+flush_done:
 				if (show_stats) {
 					if (round == 0)
 						printf("Nothing to flush.\n");
@@ -866,8 +924,17 @@
 				printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed);
 				fflush(stdout);
 			}
+
+			/* If we are flushing, and specifying primary, then we
+			 * want to flush only a single round.  Otherwise, we'll
+			 * start flushing secondaries that were promoted to
+			 * primaries.
+			 */
+			if (!(filter.flags & IFA_F_SECONDARY) && (filter.flagmask & IFA_F_SECONDARY))
+				goto flush_done;
 		}
-		fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", MAX_ROUNDS); fflush(stderr);
+		fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", max_flush_loops);
+		fflush(stderr);
 		return 1;
 	}
 
@@ -877,7 +944,7 @@
 			exit(1);
 		}
 
-		if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
+		if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
@@ -1168,7 +1235,7 @@
 			  sizeof(cinfo));
 	}
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		return -2;
 
 	return 0;
diff --git a/ip/ipaddrlabel.c b/ip/ipaddrlabel.c
index 95e813d..eb6a48c 100644
--- a/ip/ipaddrlabel.c
+++ b/ip/ipaddrlabel.c
@@ -59,7 +59,6 @@
 	FILE *fp = (FILE*)arg;
 	struct ifaddrlblmsg *ifal = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	int host_len = -1;
 	struct rtattr *tb[IFAL_MAX+1];
 	char abuf[256];
 
@@ -72,11 +71,6 @@
 
 	parse_rtattr(tb, IFAL_MAX, IFAL_RTA(ifal), len);
 
-	if (ifal->ifal_family == AF_INET)
-		host_len = 32;
-	else if (ifal->ifal_family == AF_INET6)
-		host_len = 128;
-
 	if (n->nlmsg_type == RTM_DELADDRLABEL)
 		fprintf(fp, "Deleted ");
 
@@ -120,7 +114,7 @@
 		return 1;
 	}
 
-	if (rtnl_dump_filter(&rth, print_addrlabel, stdout, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, print_addrlabel, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
@@ -189,7 +183,7 @@
 	if (req.ifal.ifal_family == AF_UNSPEC)
 		req.ifal.ifal_family = AF_INET6;
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		return 2;
 
 	return 0;
@@ -216,7 +210,7 @@
 		if (rtnl_open(&rth2, 0) < 0)
 			return -1;
 
-		if (rtnl_talk(&rth2, n, 0, 0, NULL, NULL, NULL) < 0)
+		if (rtnl_talk(&rth2, n, 0, 0, NULL) < 0)
 			return -2;
 
 		rtnl_close(&rth2);
@@ -242,7 +236,7 @@
 		return 1;
 	}
 
-	if (rtnl_dump_filter(&rth, flush_addrlabel, NULL, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, flush_addrlabel, NULL) < 0) {
 		fprintf(stderr, "Flush terminated\n");
 		return 1;
 	}
diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
new file mode 100644
index 0000000..c5683f5
--- /dev/null
+++ b/ip/ipl2tp.c
@@ -0,0 +1,809 @@
+/*
+ * ipl2tp.c	       "ip l2tp"
+ *
+ *		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.
+ *
+ * Original Author:	James Chapman <jchapman@katalix.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+
+#include <linux/genetlink.h>
+#include <linux/l2tp.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+enum {
+	L2TP_ADD,
+	L2TP_CHG,
+	L2TP_DEL,
+	L2TP_GET
+};
+
+struct l2tp_parm {
+	uint32_t tunnel_id;
+	uint32_t peer_tunnel_id;
+	uint32_t session_id;
+	uint32_t peer_session_id;
+	uint32_t offset;
+	uint32_t peer_offset;
+	enum l2tp_encap_type encap;
+	uint16_t local_udp_port;
+	uint16_t peer_udp_port;
+	int cookie_len;
+	uint8_t cookie[8];
+	int peer_cookie_len;
+	uint8_t peer_cookie[8];
+	struct in_addr local_ip;
+	struct in_addr peer_ip;
+
+	uint16_t pw_type;
+	uint16_t mtu;
+	int udp_csum:1;
+	int recv_seq:1;
+	int send_seq:1;
+	int lns_mode:1;
+	int data_seq:2;
+	int tunnel:1;
+	int session:1;
+	int reorder_timeout;
+	const char *ifname;
+};
+
+struct l2tp_stats {
+	uint64_t data_rx_packets;
+	uint64_t data_rx_bytes;
+	uint64_t data_rx_errors;
+	uint64_t data_rx_oos_packets;
+	uint64_t data_rx_oos_discards;
+	uint64_t data_tx_packets;
+	uint64_t data_tx_bytes;
+	uint64_t data_tx_errors;
+};
+
+struct l2tp_data {
+	struct l2tp_parm config;
+	struct l2tp_stats stats;
+};
+
+/* netlink socket */
+static struct rtnl_handle genl_rth;
+static int genl_family = -1;
+
+/*****************************************************************************
+ * Netlink actions
+ *****************************************************************************/
+
+static int create_tunnel(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_TUNNEL_CREATE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
+	addattr8(&req.n, 1024, L2TP_ATTR_PROTO_VERSION, 3);
+	addattr16(&req.n, 1024, L2TP_ATTR_ENCAP_TYPE, p->encap);
+
+	addattr32(&req.n, 1024, L2TP_ATTR_IP_SADDR, p->local_ip.s_addr);
+	addattr32(&req.n, 1024, L2TP_ATTR_IP_DADDR, p->peer_ip.s_addr);
+	if (p->encap == L2TP_ENCAPTYPE_UDP) {
+		addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port);
+		addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port);
+	}
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static int delete_tunnel(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_TUNNEL_DELETE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id);
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static int create_session(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_SESSION_CREATE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_PEER_SESSION_ID, p->peer_session_id);
+	addattr16(&req.n, 1024, L2TP_ATTR_PW_TYPE, p->pw_type);
+
+	if (p->mtu)		addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu);
+	if (p->recv_seq)	addattr(&req.n, 1024, L2TP_ATTR_RECV_SEQ);
+	if (p->send_seq)	addattr(&req.n, 1024, L2TP_ATTR_SEND_SEQ);
+	if (p->lns_mode)	addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE);
+	if (p->data_seq)	addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq);
+	if (p->reorder_timeout) addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT,
+					  p->reorder_timeout);
+	if (p->offset)		addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset);
+	if (p->cookie_len)	addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE,
+					  p->cookie, p->cookie_len);
+	if (p->peer_cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE,
+					  p->peer_cookie,  p->peer_cookie_len);
+	if (p->ifname && p->ifname[0])
+		addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname);
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static int delete_session(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_SESSION_DELETE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static void print_cookie(char *name, const uint8_t *cookie, int len)
+{
+	printf("  %s %02x%02x%02x%02x", name,
+	       cookie[0], cookie[1],
+	       cookie[2], cookie[3]);
+	if (len == 8)
+		printf("%02x%02x%02x%02x",
+		       cookie[4], cookie[5],
+		       cookie[6], cookie[7]);
+}
+
+static void print_tunnel(const struct l2tp_data *data)
+{
+	const struct l2tp_parm *p = &data->config;
+
+	printf("Tunnel %u, encap %s\n",
+	       p->tunnel_id,
+	       p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
+	       p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
+	printf("  From %s ", inet_ntoa(p->local_ip));
+	printf("to %s\n", inet_ntoa(p->peer_ip));
+	printf("  Peer tunnel %u\n",
+	       p->peer_tunnel_id);
+
+	if (p->encap == L2TP_ENCAPTYPE_UDP)
+		printf("  UDP source / dest ports: %hu/%hu\n",
+		       p->local_udp_port, p->peer_udp_port);
+}
+
+static void print_session(struct l2tp_data *data)
+{
+	struct l2tp_parm *p = &data->config;
+
+	printf("Session %u in tunnel %u\n",
+	       p->session_id, p->tunnel_id);
+	printf("  Peer session %u, tunnel %u\n",
+	       p->peer_session_id, p->peer_tunnel_id);
+
+	if (p->ifname != NULL) {
+		printf("  interface name: %s\n", p->ifname);
+	}
+	printf("  offset %u, peer offset %u\n",
+	       p->offset, p->peer_offset);
+	if (p->cookie_len > 0)
+		print_cookie("cookie", p->cookie, p->cookie_len);
+	if (p->peer_cookie_len > 0)
+		print_cookie("peer cookie", p->peer_cookie, p->peer_cookie_len);
+
+	if (p->reorder_timeout != 0) {
+		printf("  reorder timeout: %u\n", p->reorder_timeout);
+	}
+}
+
+static int get_response(struct nlmsghdr *n, void *arg)
+{
+	struct genlmsghdr *ghdr;
+	struct l2tp_data *data = arg;
+	struct l2tp_parm *p = &data->config;
+	struct rtattr *attrs[L2TP_ATTR_MAX + 1];
+	struct rtattr *nla_stats;
+	int len;
+
+	/* Validate message and parse attributes */
+	if (n->nlmsg_type == NLMSG_ERROR)
+		return -EBADMSG;
+
+	ghdr = NLMSG_DATA(n);
+	len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ghdr));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr(attrs, L2TP_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
+
+	if (attrs[L2TP_ATTR_PW_TYPE])
+		p->pw_type = rta_getattr_u16(attrs[L2TP_ATTR_PW_TYPE]);
+	if (attrs[L2TP_ATTR_ENCAP_TYPE])
+		p->encap = rta_getattr_u16(attrs[L2TP_ATTR_ENCAP_TYPE]);
+	if (attrs[L2TP_ATTR_OFFSET])
+		p->offset = rta_getattr_u16(attrs[L2TP_ATTR_OFFSET]);
+	if (attrs[L2TP_ATTR_DATA_SEQ])
+		p->data_seq = rta_getattr_u16(attrs[L2TP_ATTR_DATA_SEQ]);
+	if (attrs[L2TP_ATTR_CONN_ID])
+		p->tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_CONN_ID]);
+	if (attrs[L2TP_ATTR_PEER_CONN_ID])
+		p->peer_tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_CONN_ID]);
+	if (attrs[L2TP_ATTR_SESSION_ID])
+		p->session_id = rta_getattr_u32(attrs[L2TP_ATTR_SESSION_ID]);
+	if (attrs[L2TP_ATTR_PEER_SESSION_ID])
+		p->peer_session_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_SESSION_ID]);
+
+	p->udp_csum = !!attrs[L2TP_ATTR_UDP_CSUM];
+	if (attrs[L2TP_ATTR_COOKIE])
+		memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]),
+		       p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE]));
+
+	if (attrs[L2TP_ATTR_PEER_COOKIE])
+		memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]),
+		       p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE]));
+
+	p->recv_seq = !!attrs[L2TP_ATTR_RECV_SEQ];
+	p->send_seq = !!attrs[L2TP_ATTR_SEND_SEQ];
+
+	if (attrs[L2TP_ATTR_RECV_TIMEOUT])
+		p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]);
+	if (attrs[L2TP_ATTR_IP_SADDR])
+		p->local_ip.s_addr = rta_getattr_u32(attrs[L2TP_ATTR_IP_SADDR]);
+	if (attrs[L2TP_ATTR_IP_DADDR])
+		p->peer_ip.s_addr = rta_getattr_u32(attrs[L2TP_ATTR_IP_DADDR]);
+	if (attrs[L2TP_ATTR_UDP_SPORT])
+		p->local_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_SPORT]);
+	if (attrs[L2TP_ATTR_UDP_DPORT])
+		p->peer_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_DPORT]);
+	if (attrs[L2TP_ATTR_MTU])
+		p->mtu = rta_getattr_u16(attrs[L2TP_ATTR_MTU]);
+	if (attrs[L2TP_ATTR_IFNAME])
+		p->ifname = rta_getattr_str(attrs[L2TP_ATTR_IFNAME]);
+
+	nla_stats = attrs[L2TP_ATTR_STATS];
+	if (nla_stats) {
+		struct rtattr *tb[L2TP_ATTR_STATS_MAX + 1];
+
+		parse_rtattr_nested(tb, L2TP_ATTR_STATS_MAX, nla_stats);
+
+		if (tb[L2TP_ATTR_TX_PACKETS])
+			data->stats.data_tx_packets = rta_getattr_u64(tb[L2TP_ATTR_TX_PACKETS]);
+		if (tb[L2TP_ATTR_TX_BYTES])
+			data->stats.data_tx_bytes = rta_getattr_u64(tb[L2TP_ATTR_TX_BYTES]);
+		if (tb[L2TP_ATTR_TX_ERRORS])
+			data->stats.data_tx_errors = rta_getattr_u64(tb[L2TP_ATTR_TX_ERRORS]);
+		if (tb[L2TP_ATTR_RX_PACKETS])
+			data->stats.data_rx_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_PACKETS]);
+		if (tb[L2TP_ATTR_RX_BYTES])
+			data->stats.data_rx_bytes = rta_getattr_u64(tb[L2TP_ATTR_RX_BYTES]);
+		if (tb[L2TP_ATTR_RX_ERRORS])
+			data->stats.data_rx_errors = rta_getattr_u64(tb[L2TP_ATTR_RX_ERRORS]);
+		if (tb[L2TP_ATTR_RX_SEQ_DISCARDS])
+			data->stats.data_rx_oos_discards = rta_getattr_u64(tb[L2TP_ATTR_RX_SEQ_DISCARDS]);
+		if (tb[L2TP_ATTR_RX_OOS_PACKETS])
+			data->stats.data_rx_oos_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_OOS_PACKETS]);
+	}
+
+	return 0;
+}
+
+static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	int ret = get_response(n, arg);
+
+	if (ret == 0)
+		print_session(arg);
+
+	return ret;
+}
+
+static int get_session(struct l2tp_data *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char buf[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
+
+	req.g.cmd = L2TP_CMD_SESSION_GET;
+	req.g.version = L2TP_GENL_VERSION;
+
+	if (p->config.tunnel_id && p->config.session_id) {
+		addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
+		addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, p->config.session_id);
+	}
+
+	if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
+		return -2;
+
+	if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	int ret = get_response(n, arg);
+
+	if (ret == 0)
+		print_tunnel(arg);
+
+	return ret;
+}
+
+static int get_tunnel(struct l2tp_data *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
+
+	req.g.cmd = L2TP_CMD_TUNNEL_GET;
+	req.g.version = L2TP_GENL_VERSION;
+
+	if (p->config.tunnel_id)
+		addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
+
+	if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
+		return -2;
+
+	if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+ * Command parser
+ *****************************************************************************/
+
+static int hex(char ch)
+{
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	if ((ch >= 'A') && (ch <= 'F'))
+		return ch - 'A' + 10;
+	return -1;
+}
+
+static int hex2mem(const char *buf, uint8_t *mem, int count)
+{
+	int i, j;
+	int c;
+
+	for (i = 0, j = 0; i < count; i++, j += 2) {
+		c = hex(buf[j]);
+		if (c < 0)
+			goto err;
+
+		mem[i] = c << 4;
+
+		c = hex(buf[j + 1]);
+		if (c < 0)
+			goto err;
+
+		mem[i] |= c;
+	}
+
+	return 0;
+
+err:
+	return -1;
+}
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip l2tp add tunnel\n");
+	fprintf(stderr, "          remote ADDR local ADDR\n");
+	fprintf(stderr, "          tunnel_id ID peer_tunnel_id ID\n");
+	fprintf(stderr, "          [ encap { ip | udp } ]\n");
+	fprintf(stderr, "          [ udp_sport PORT ] [ udp_dport PORT ]\n");
+	fprintf(stderr, "Usage: ip l2tp add session [ name NAME ]\n");
+	fprintf(stderr, "          tunnel_id ID\n");
+	fprintf(stderr, "          session_id ID peer_session_id ID\n");
+	fprintf(stderr, "          [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n");
+	fprintf(stderr, "          [ offset OFFSET ] [ peer_offset OFFSET ]\n");
+	fprintf(stderr, "       ip l2tp del tunnel tunnel_id ID\n");
+	fprintf(stderr, "       ip l2tp del session tunnel_id ID session_id ID\n");
+	fprintf(stderr, "       ip l2tp show tunnel [ tunnel_id ID ]\n");
+	fprintf(stderr, "       ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME   := STRING\n");
+	fprintf(stderr, "       ADDR   := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       PORT   := { 0..65535 }\n");
+	fprintf(stderr, "       ID     := { 1..4294967295 }\n");
+	fprintf(stderr, "       HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n");
+	exit(-1);
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
+{
+	memset(p, 0, sizeof(*p));
+
+	if (argc == 0)
+		usage();
+
+	while (argc > 0) {
+		if (strcmp(*argv, "encap") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "ip") == 0) {
+				p->encap = L2TP_ENCAPTYPE_IP;
+			} else if (strcmp(*argv, "udp") == 0) {
+				p->encap = L2TP_ENCAPTYPE_UDP;
+			} else {
+				fprintf(stderr, "Unknown tunnel encapsulation.\n");
+				exit(-1);
+			}
+		} else if (strcmp(*argv, "name") == 0) {
+			NEXT_ARG();
+			p->ifname = *argv;
+		} else if (strcmp(*argv, "remote") == 0) {
+			NEXT_ARG();
+			p->peer_ip.s_addr = get_addr32(*argv);
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			p->local_ip.s_addr = get_addr32(*argv);
+		} else if ((strcmp(*argv, "tunnel_id") == 0) ||
+			   (strcmp(*argv, "tid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->tunnel_id = uval;
+		} else if ((strcmp(*argv, "peer_tunnel_id") == 0) ||
+			   (strcmp(*argv, "ptid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->peer_tunnel_id = uval;
+		} else if ((strcmp(*argv, "session_id") == 0) ||
+			   (strcmp(*argv, "sid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->session_id = uval;
+		} else if ((strcmp(*argv, "peer_session_id") == 0) ||
+			   (strcmp(*argv, "psid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->peer_session_id = uval;
+		} else if (strcmp(*argv, "udp_sport") == 0) {
+			__u16 uval;
+			NEXT_ARG();
+			if (get_u16(&uval, *argv, 0))
+				invarg("invalid port\n", *argv);
+			p->local_udp_port = uval;
+		} else if (strcmp(*argv, "udp_dport") == 0) {
+			__u16 uval;
+			NEXT_ARG();
+			if (get_u16(&uval, *argv, 0))
+				invarg("invalid port\n", *argv);
+			p->peer_udp_port = uval;
+		} else if (strcmp(*argv, "offset") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid offset\n", *argv);
+			p->offset = uval;
+		} else if (strcmp(*argv, "peer_offset") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid offset\n", *argv);
+			p->peer_offset = uval;
+		} else if (strcmp(*argv, "cookie") == 0) {
+			int slen;
+			NEXT_ARG();
+			slen = strlen(*argv);
+			if ((slen != 8) && (slen != 16))
+				invarg("cookie must be either 8 or 16 hex digits\n", *argv);
+
+			p->cookie_len = slen / 2;
+			if (hex2mem(*argv, p->cookie, p->cookie_len) < 0)
+				invarg("cookie must be a hex string\n", *argv);
+		} else if (strcmp(*argv, "peer_cookie") == 0) {
+			int slen;
+			NEXT_ARG();
+			slen = strlen(*argv);
+			if ((slen != 8) && (slen != 16))
+				invarg("cookie must be either 8 or 16 hex digits\n", *argv);
+
+			p->peer_cookie_len = slen / 2;
+			if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0)
+				invarg("cookie must be a hex string\n", *argv);
+		} else if (strcmp(*argv, "tunnel") == 0) {
+			p->tunnel = 1;
+		} else if (strcmp(*argv, "session") == 0) {
+			p->session = 1;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			fprintf(stderr, "Unknown command: %s\n", *argv);
+			usage();
+		}
+
+		argc--; argv++;
+	}
+
+	return 0;
+}
+
+
+static int do_add(int argc, char **argv)
+{
+	struct l2tp_parm p;
+	int ret = 0;
+
+	if (parse_args(argc, argv, L2TP_ADD, &p) < 0)
+		return -1;
+
+	if (!p.tunnel && !p.session)
+		missarg("tunnel or session");
+
+	if (p.tunnel_id == 0)
+		missarg("tunnel_id");
+
+	/* session_id and peer_session_id must be provided for sessions */
+	if ((p.session) && (p.peer_session_id == 0))
+		missarg("peer_session_id");
+	if ((p.session) && (p.session_id == 0))
+		missarg("session_id");
+
+	/* peer_tunnel_id is needed for tunnels */
+	if ((p.tunnel) && (p.peer_tunnel_id == 0))
+		missarg("peer_tunnel_id");
+
+	if (p.tunnel) {
+		if (p.local_ip.s_addr == 0)
+			missarg("local");
+
+		if (p.peer_ip.s_addr == 0)
+			missarg("remote");
+
+		if (p.encap == L2TP_ENCAPTYPE_UDP) {
+			if (p.local_udp_port == 0)
+				missarg("udp_sport");
+			if (p.peer_udp_port == 0)
+				missarg("udp_dport");
+		}
+
+		ret = create_tunnel(&p);
+	}
+
+	if (p.session) {
+		/* Only ethernet pseudowires supported */
+		p.pw_type = L2TP_PWTYPE_ETH;
+
+		ret = create_session(&p);
+	}
+
+	return ret;
+}
+
+static int do_del(int argc, char **argv)
+{
+	struct l2tp_parm p;
+
+	if (parse_args(argc, argv, L2TP_DEL, &p) < 0)
+		return -1;
+
+	if (!p.tunnel && !p.session)
+		missarg("tunnel or session");
+
+	if ((p.tunnel) && (p.tunnel_id == 0))
+		missarg("tunnel_id");
+	if ((p.session) && (p.session_id == 0))
+		missarg("session_id");
+
+	if (p.session_id)
+		return delete_session(&p);
+	else
+		return delete_tunnel(&p);
+
+	return -1;
+}
+
+static int do_show(int argc, char **argv)
+{
+	struct l2tp_data data;
+	struct l2tp_parm *p = &data.config;
+
+	if (parse_args(argc, argv, L2TP_GET, p) < 0)
+		return -1;
+
+	if (!p->tunnel && !p->session)
+		missarg("tunnel or session");
+
+	if (p->session)
+		get_session(&data);
+	else
+		get_tunnel(&data);
+
+	return 0;
+}
+
+static int genl_parse_getfamily(struct nlmsghdr *nlh)
+{
+	struct rtattr *tb[CTRL_ATTR_MAX + 1];
+	struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
+	int len = nlh->nlmsg_len;
+	struct rtattr *attrs;
+
+	if (nlh->nlmsg_type != GENL_ID_CTRL) {
+		fprintf(stderr, "Not a controller message, nlmsg_len=%d "
+			"nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type);
+		return -1;
+	}
+
+	if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
+		fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
+		return -1;
+	}
+
+	len -= NLMSG_LENGTH(GENL_HDRLEN);
+
+	if (len < 0) {
+		fprintf(stderr, "wrong controller message len %d\n", len);
+		return -1;
+	}
+
+	attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
+	parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
+
+	if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
+		fprintf(stderr, "Missing family id TLV\n");
+		return -1;
+	}
+
+	return rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
+}
+
+int genl_ctrl_resolve_family(const char *family)
+{
+	struct {
+		struct nlmsghdr         n;
+		struct genlmsghdr	g;
+		char                    buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = GENL_ID_CTRL;
+	req.g.cmd = CTRL_CMD_GETFAMILY;
+
+	addattr_l(&req.n, 1024, CTRL_ATTR_FAMILY_NAME,
+		  family, strlen(family) + 1);
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, &req.n) < 0) {
+		fprintf(stderr, "Error talking to the kernel\n");
+		return -2;
+	}
+
+	return genl_parse_getfamily(&req.n);
+}
+
+int do_ipl2tp(int argc, char **argv)
+{
+	if (genl_family < 0) {
+		if (rtnl_open_byproto(&genl_rth, 0, NETLINK_GENERIC) < 0) {
+			fprintf(stderr, "Cannot open generic netlink socket\n");
+			exit(1);
+		}
+
+		genl_family = genl_ctrl_resolve_family(L2TP_GENL_NAME);
+		if (genl_family < 0)
+			exit(1);
+	}
+
+	if (argc < 1)
+		usage();
+
+	if (matches(*argv, "add") == 0)
+		return do_add(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();
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ip/iplink.c b/ip/iplink.c
index cb2c4f5..679091e 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -34,7 +34,7 @@
 
 #define IPLINK_IOCTL_COMPAT	1
 #ifndef LIBDIR
-#define LIBDIR "/usr/lib/"
+#define LIBDIR "/usr/lib"
 #endif
 
 static void usage(void) __attribute__((noreturn));
@@ -43,7 +43,7 @@
 void iplink_usage(void)
 {
 	if (iplink_have_newlink()) {
-		fprintf(stderr, "Usage: ip link add link DEV [ name ] NAME\n");
+		fprintf(stderr, "Usage: ip link add [link DEV] [ name ] NAME\n");
 		fprintf(stderr, "                   [ txqueuelen PACKETS ]\n");
 		fprintf(stderr, "                   [ address LLADDR ]\n");
 		fprintf(stderr, "                   [ broadcast LLADDR ]\n");
@@ -51,7 +51,7 @@
 		fprintf(stderr, "                   type TYPE [ ARGS ]\n");
 		fprintf(stderr, "       ip link delete DEV type TYPE [ ARGS ]\n");
 		fprintf(stderr, "\n");
-		fprintf(stderr, "       ip link set DEVICE [ { up | down } ]\n");
+		fprintf(stderr, "       ip link set { dev DEVICE | group DEVGROUP } [ { up | down } ]\n");
 	} else
 		fprintf(stderr, "Usage: ip link set DEVICE [ { up | down } ]\n");
 
@@ -67,15 +67,21 @@
 	fprintf(stderr, "	                  [ broadcast LLADDR ]\n");
 	fprintf(stderr, "	                  [ mtu MTU ]\n");
 	fprintf(stderr, "	                  [ netns PID ]\n");
+	fprintf(stderr, "	                  [ netns NAME ]\n");
 	fprintf(stderr, "			  [ alias NAME ]\n");
 	fprintf(stderr, "	                  [ vf NUM [ mac LLADDR ]\n");
 	fprintf(stderr, "				   [ vlan VLANID [ qos VLAN-QOS ] ]\n");
+
 	fprintf(stderr, "				   [ rate TXRATE ] ] \n");
-	fprintf(stderr, "       ip link show [ DEVICE ]\n");
+
+	fprintf(stderr, "				   [ spoofchk { on | off} ] ] \n");
+	fprintf(stderr, "			  [ master DEVICE ]\n");
+	fprintf(stderr, "			  [ nomaster ]\n");
+	fprintf(stderr, "       ip link show [ DEVICE | group GROUP ]\n");
 
 	if (iplink_have_newlink()) {
 		fprintf(stderr, "\n");
-		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can }\n");
+		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can | bridge }\n");
 	}
 	exit(-1);
 }
@@ -126,6 +132,15 @@
 	return l;
 }
 
+int get_link_mode(const char *mode)
+{
+	if (strcasecmp(mode, "default") == 0)
+		return IF_LINK_MODE_DEFAULT;
+	if (strcasecmp(mode, "dormant") == 0)
+		return IF_LINK_MODE_DORMANT;
+	return -1;
+}
+
 #if IPLINK_IOCTL_COMPAT
 static int have_rtnl_newlink = -1;
 
@@ -158,7 +173,7 @@
 		req.n.nlmsg_type = RTM_NEWLINK;
 		req.i.ifi_family = AF_UNSPEC;
 
-		rtnl_send(&rth, (char *)&req.n, req.n.nlmsg_len);
+		rtnl_send(&rth, &req.n, req.n.nlmsg_len);
 		rtnl_listen(&rth, accept_msg, NULL);
 	}
 	return have_rtnl_newlink;
@@ -225,6 +240,18 @@
 			ivt.vf = vf;
 			addattr_l(&req->n, sizeof(*req), IFLA_VF_TX_RATE, &ivt, sizeof(ivt));
 		
+		} else if (matches(*argv, "spoofchk") == 0) {
+			struct ifla_vf_spoofchk ivs;
+			NEXT_ARG();
+			if (matches(*argv, "on") == 0)
+				ivs.setting = 1;
+			else if (matches(*argv, "off") == 0)
+				ivs.setting = 0;
+			else
+				invarg("Invalid \"spoofchk\" value\n", *argv);
+			ivs.vf = vf;
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs));
+
 		} else {
 			/* rewind arg */
 			PREV_ARG();
@@ -244,7 +271,7 @@
 
 
 int iplink_parse(int argc, char **argv, struct iplink_req *req,
-		char **name, char **type, char **link, char **dev)
+		char **name, char **type, char **link, char **dev, int *group)
 {
 	int ret, len;
 	char abuf[32];
@@ -253,6 +280,7 @@
 	int netns = -1;
 	int vf = -1;
 
+	*group = -1;
 	ret = argc;
 
 	while (argc > 0) {
@@ -301,9 +329,12 @@
                         NEXT_ARG();
                         if (netns != -1)
                                 duparg("netns", *argv);
-                        if (get_integer(&netns, *argv, 0))
+			if ((netns = get_netns_fd(*argv)) >= 0)
+				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
+			else if (get_integer(&netns, *argv, 0) == 0)
+				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
+			else
                                 invarg("Invalid \"netns\" value\n", *argv);
-                        addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
 		} else if (strcmp(*argv, "multicast") == 0) {
 			NEXT_ARG();
 			req->i.ifi_change |= IFF_MULTICAST;
@@ -361,7 +392,18 @@
 			if (len < 0)
 				return -1;
 			addattr_nest_end(&req->n, vflist);
-#ifdef IFF_DYNAMIC
+		} else if (matches(*argv, "master") == 0) {
+			int ifindex;
+			NEXT_ARG();
+			ifindex = ll_name_to_index(*argv);
+			if (!ifindex)
+				invarg("Device does not exist\n", *argv);
+			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
+				  &ifindex, 4);
+		} else if (matches(*argv, "nomaster") == 0) {
+			int ifindex = 0;
+			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
+				  &ifindex, 4);
 		} else if (matches(*argv, "dynamic") == 0) {
 			NEXT_ARG();
 			req->i.ifi_change |= IFF_DYNAMIC;
@@ -371,7 +413,6 @@
 				req->i.ifi_flags &= ~IFF_DYNAMIC;
 			} else
 				return on_off("dynamic");
-#endif
 		} else if (matches(*argv, "type") == 0) {
 			NEXT_ARG();
 			*type = *argv;
@@ -383,6 +424,27 @@
 				  *argv, strlen(*argv));
 			argc--; argv++;
 			break;
+		} else if (strcmp(*argv, "group") == 0) {
+			NEXT_ARG();
+			if (*group != -1)
+				duparg("group", *argv);
+			if (rtnl_group_a2n(group, *argv))
+				invarg("Invalid \"group\" value\n", *argv);
+		} else if (strcmp(*argv, "mode") == 0) {
+			int mode;
+			NEXT_ARG();
+			mode = get_link_mode(*argv);
+			if (mode < 0)
+				invarg("Invalid link mode\n", *argv);
+			addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode);
+		} else if (strcmp(*argv, "state") == 0) {
+			int state;
+			NEXT_ARG();
+			state = get_operstate(*argv);
+			if (state < 0)
+				invarg("Invalid operstate\n", *argv);
+
+			addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state);
 		} else {
 			if (strcmp(*argv, "dev") == 0) {
 				NEXT_ARG();
@@ -406,6 +468,7 @@
 	char *name = NULL;
 	char *link = NULL;
 	char *type = NULL;
+	int group;
 	struct link_util *lu = NULL;
 	struct iplink_req req;
 	int ret;
@@ -417,44 +480,40 @@
 	req.n.nlmsg_type = cmd;
 	req.i.ifi_family = preferred_family;
 
-	ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev);
+	ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev, &group);
 	if (ret < 0)
 		return ret;
 
 	argc -= ret;
 	argv += ret;
-	ll_init_map(&rth);
 
-	if (type) {
-		struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
-		addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
-		addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
-			 strlen(type));
-
-		lu = get_link_kind(type);
-		if (lu && argc) {
-			struct rtattr * data = NLMSG_TAIL(&req.n);
-			addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
-
-			if (lu->parse_opt &&
-			    lu->parse_opt(lu, argc, argv, &req.n))
+	if (group != -1) {
+		if (dev)
+			addattr_l(&req.n, sizeof(req), IFLA_GROUP,
+					&group, sizeof(group));
+		else {
+			if (argc) {
+				fprintf(stderr, "Garbage instead of arguments "
+						"\"%s ...\". Try \"ip link "
+						"help\".\n", *argv);
 				return -1;
+			}
+			if (flags & NLM_F_CREATE) {
+				fprintf(stderr, "group cannot be used when "
+						"creating devices.\n");
+				return -1;
+			}
 
-			data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
-		} else if (argc) {
-			if (matches(*argv, "help") == 0)
-				usage();
-			fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
-					"Try \"ip link help\".\n", *argv);
-			return -1;
+			req.i.ifi_index = 0;
+			addattr32(&req.n, sizeof(req), IFLA_GROUP, group);
+			if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+				exit(2);
+			return 0;
 		}
-		linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
-	} else if (flags & NLM_F_CREATE) {
-		fprintf(stderr, "Not enough information: \"type\" argument "
-				"is required\n");
-		return -1;
 	}
 
+	ll_init_map(&rth);
+
 	if (!(flags & NLM_F_CREATE)) {
 		if (!dev) {
 			fprintf(stderr, "Not enough information: \"dev\" "
@@ -494,7 +553,37 @@
 		addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
 	}
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (type) {
+		struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
+		addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
+		addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
+			 strlen(type));
+
+		lu = get_link_kind(type);
+		if (lu && argc) {
+			struct rtattr * data = NLMSG_TAIL(&req.n);
+			addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
+
+			if (lu->parse_opt &&
+			    lu->parse_opt(lu, argc, argv, &req.n))
+				return -1;
+
+			data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
+		} else if (argc) {
+			if (matches(*argv, "help") == 0)
+				usage();
+			fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
+					"Try \"ip link help\".\n", *argv);
+			return -1;
+		}
+		linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
+	} else if (flags & NLM_F_CREATE) {
+		fprintf(stderr, "Not enough information: \"type\" argument "
+				"is required\n");
+		return -1;
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	return 0;
@@ -779,7 +868,6 @@
 				flags |= IFF_NOARP;
 			} else
 				return on_off("noarp");
-#ifdef IFF_DYNAMIC
 		} else if (matches(*argv, "dynamic") == 0) {
 			NEXT_ARG();
 			mask |= IFF_DYNAMIC;
@@ -789,7 +877,6 @@
 				flags &= ~IFF_DYNAMIC;
 			} else
 				return on_off("dynamic");
-#endif
 		} else {
                         if (strcmp(*argv, "dev") == 0) {
 				NEXT_ARG();
diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c
index a3c78bd..ed0e34b 100644
--- a/ip/iplink_macvlan.c
+++ b/ip/iplink_macvlan.c
@@ -23,14 +23,14 @@
 static void explain(void)
 {
 	fprintf(stderr,
-		"Usage: ... macvlan mode { private | vepa | bridge }\n"
+		"Usage: ... macvlan mode { private | vepa | bridge | passthru }\n"
 	);
 }
 
 static int mode_arg(void)
 {
         fprintf(stderr, "Error: argument of \"mode\" must be \"private\", "
-		"\"vepa\" or \"bridge\"\n");
+		"\"vepa\", \"bridge\" or \"passthru\" \n");
         return -1;
 }
 
@@ -48,6 +48,8 @@
 				mode = MACVLAN_MODE_VEPA;
 			else if (strcmp(*argv, "bridge") == 0)
 				mode = MACVLAN_MODE_BRIDGE;
+			else if (strcmp(*argv, "passthru") == 0)
+				mode = MACVLAN_MODE_PASSTHRU;
 			else
 				return mode_arg();
 
@@ -77,11 +79,12 @@
 	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32))
 		return;
 
-	mode = *(__u32 *)RTA_DATA(tb[IFLA_VLAN_ID]);
+	mode = rta_getattr_u32(tb[IFLA_VLAN_ID]);
 	fprintf(f, " mode %s ",
 		  mode == MACVLAN_MODE_PRIVATE ? "private"
 		: mode == MACVLAN_MODE_VEPA    ? "vepa"
 		: mode == MACVLAN_MODE_BRIDGE  ? "bridge"
+		: mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
 		:				 "unknown");
 }
 
diff --git a/ip/iplink_macvtap.c b/ip/iplink_macvtap.c
new file mode 100644
index 0000000..6c7fe1f
--- /dev/null
+++ b/ip/iplink_macvtap.c
@@ -0,0 +1,93 @@
+/*
+ * iplink_macvtap.c	macvtap device support
+ *
+ *              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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/if_link.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void explain(void)
+{
+	fprintf(stderr,
+		"Usage: ... macvtap mode { private | vepa | bridge | passthru }\n"
+	);
+}
+
+static int mode_arg(void)
+{
+        fprintf(stderr, "Error: argument of \"mode\" must be \"private\", "
+		"\"vepa\", \"bridge\" or \"passthru\" \n");
+        return -1;
+}
+
+static int macvtap_parse_opt(struct link_util *lu, int argc, char **argv,
+			  struct nlmsghdr *n)
+{
+	while (argc > 0) {
+		if (matches(*argv, "mode") == 0) {
+			__u32 mode = 0;
+			NEXT_ARG();
+
+			if (strcmp(*argv, "private") == 0)
+				mode = MACVLAN_MODE_PRIVATE;
+			else if (strcmp(*argv, "vepa") == 0)
+				mode = MACVLAN_MODE_VEPA;
+			else if (strcmp(*argv, "bridge") == 0)
+				mode = MACVLAN_MODE_BRIDGE;
+			else if (strcmp(*argv, "passthru") == 0)
+				mode = MACVLAN_MODE_PASSTHRU;
+			else
+				return mode_arg();
+
+			addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
+		} else if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "macvtap: what is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	return 0;
+}
+
+static void macvtap_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	__u32 mode;
+
+	if (!tb)
+		return;
+
+	if (!tb[IFLA_MACVLAN_MODE] ||
+	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32))
+		return;
+
+	mode = rta_getattr_u32(tb[IFLA_VLAN_ID]);
+	fprintf(f, " mode %s ",
+		  mode == MACVLAN_MODE_PRIVATE ? "private"
+		: mode == MACVLAN_MODE_VEPA    ? "vepa"
+		: mode == MACVLAN_MODE_BRIDGE  ? "bridge"
+		: mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
+		:				 "unknown");
+}
+
+struct link_util macvtap_link_util = {
+	.id		= "macvtap",
+	.maxattr	= IFLA_MACVLAN_MAX,
+	.parse_opt	= macvtap_parse_opt,
+	.print_opt	= macvtap_print_opt,
+};
diff --git a/ip/iplink_vlan.c b/ip/iplink_vlan.c
index 223feb3..97af8d6 100644
--- a/ip/iplink_vlan.c
+++ b/ip/iplink_vlan.c
@@ -183,7 +183,7 @@
 	    RTA_PAYLOAD(tb[IFLA_VLAN_ID]) < sizeof(__u16))
 		return;
 
-	fprintf(f, "id %u ", *(__u16 *)RTA_DATA(tb[IFLA_VLAN_ID]));
+	fprintf(f, "id %u ", rta_getattr_u16(tb[IFLA_VLAN_ID]));
 
 	if (tb[IFLA_VLAN_FLAGS]) {
 		if (RTA_PAYLOAD(tb[IFLA_VLAN_FLAGS]) < sizeof(*flags))
diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c
index 44ffdfc..3ae9478 100644
--- a/ip/ipmaddr.c
+++ b/ip/ipmaddr.c
@@ -128,14 +128,17 @@
 	if (!fp)
 		return;
 	memset(&m, 0, sizeof(m));
-	fgets(buf, sizeof(buf), fp);
+	if (!fgets(buf, sizeof(buf), fp)) {
+		fclose(fp);
+		return;
+	}
 
 	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));
+		struct ma_info *ma;
 
 		if (buf[0] != '\t') {
 			sscanf(buf, "%d%s", &m.index, m.name);
diff --git a/ip/ipmroute.c b/ip/ipmroute.c
index 977143c..945727d 100644
--- a/ip/ipmroute.c
+++ b/ip/ipmroute.c
@@ -58,8 +58,10 @@
 	if (!fp)
 		return;
 
-	fgets(buf, sizeof(buf), fp);
-
+	if (!fgets(buf, sizeof(buf), fp)) {
+		fclose(fp);
+		return;
+	}
 	while (fgets(buf, sizeof(buf), fp)) {
 		int vifi;
 		char dev[256];
@@ -83,7 +85,10 @@
 	if (!fp)
 		return;
 
-	fgets(buf, sizeof(buf), fp);
+	if (!fgets(buf, sizeof(buf), fp)) {
+		fclose(fp);
+		return;
+	}
 
 	while (fgets(buf, sizeof(buf), fp)) {
 		inet_prefix maddr, msrc;
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index 2d96ab7..93cfba2 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -174,7 +174,7 @@
 		return -1;
 	}
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	return 0;
@@ -209,6 +209,7 @@
 	if (filter.index && filter.index != r->ndm_ifindex)
 		return 0;
 	if (!(filter.state&r->ndm_state) &&
+	    !(r->ndm_flags & NTF_PROXY) &&
 	    (r->ndm_state || !(filter.state&0x100)) &&
              (r->ndm_family != AF_DECnet))
 		return 0;
@@ -267,6 +268,9 @@
 	if (r->ndm_flags & NTF_ROUTER) {
 		fprintf(fp, " router");
 	}
+	if (r->ndm_flags & NTF_PROXY) {
+		fprintf(fp, " proxy");
+	}
 	if (tb[NDA_CACHEINFO] && show_stats) {
 		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
 		int hz = get_user_hz();
@@ -277,12 +281,10 @@
 		       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]);
+		__u32 p = rta_getattr_u32(tb[NDA_PROBES]);
 		fprintf(fp, " probes %u", p);
 	}
-#endif
 
 	if (r->ndm_state) {
 		int nud = r->ndm_state;
@@ -316,6 +318,7 @@
 {
 	char *filter_dev = NULL;
 	int state_given = 0;
+	struct ndmsg ndm = { 0 };
 
 	ipneigh_reset_filter();
 
@@ -356,7 +359,9 @@
 			if (state == 0)
 				state = 0x100;
 			filter.state |= state;
-		} else {
+		} else if (strcmp(*argv, "proxy") == 0)
+			ndm.ndm_flags = NTF_PROXY;
+		else {
 			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
 			}
@@ -393,7 +398,7 @@
 				exit(1);
 			}
 			filter.flushed = 0;
-			if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) {
+			if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
 				fprintf(stderr, "Flush terminated\n");
 				exit(1);
 			}
@@ -420,12 +425,14 @@
 		return 1;
 	}
 
-	if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
+	ndm.ndm_family = filter.family;
+
+	if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) {
 		perror("Cannot send dump request");
 		exit(1);
 	}
 
-	if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
diff --git a/ip/ipnetns.c b/ip/ipnetns.c
new file mode 100644
index 0000000..e41a598
--- /dev/null
+++ b/ip/ipnetns.c
@@ -0,0 +1,309 @@
+#define _ATFILE_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/syscall.h>
+#include <stdio.h>
+#include <string.h>
+#include <sched.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+#define NETNS_RUN_DIR "/var/run/netns"
+#define NETNS_ETC_DIR "/etc/netns"
+
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000	/* New network namespace (lo, device, names sockets, etc) */
+#endif
+
+#ifndef MNT_DETACH
+#define MNT_DETACH	0x00000002	/* Just detach from the tree */
+#endif /* MNT_DETACH */
+
+#ifndef HAVE_SETNS
+static int setns(int fd, int nstype)
+{
+#ifdef __NR_setns
+	return syscall(__NR_setns, fd, nstype);
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+#endif /* HAVE_SETNS */
+
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip netns list\n");
+	fprintf(stderr, "       ip netns add NAME\n");
+	fprintf(stderr, "       ip netns delete NAME\n");
+	fprintf(stderr, "       ip netns exec NAME cmd ...\n");
+	fprintf(stderr, "       ip netns monitor\n");
+	exit(-1);
+}
+
+int get_netns_fd(const char *name)
+{
+	char pathbuf[MAXPATHLEN];
+	const char *path, *ptr;
+
+	path = name;
+	ptr = strchr(name, '/');
+	if (!ptr) {
+		snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
+			NETNS_RUN_DIR, name );
+		path = pathbuf;
+	}
+	return open(path, O_RDONLY);
+}
+
+static int netns_list(int argc, char **argv)
+{
+	struct dirent *entry;
+	DIR *dir;
+
+	dir = opendir(NETNS_RUN_DIR);
+	if (!dir)
+		return 0;
+
+	while ((entry = readdir(dir)) != NULL) {
+		if (strcmp(entry->d_name, ".") == 0)
+			continue;
+		if (strcmp(entry->d_name, "..") == 0)
+			continue;
+		printf("%s\n", entry->d_name);
+	}
+	closedir(dir);
+	return 0;
+}
+
+static void bind_etc(const char *name)
+{
+	char etc_netns_path[MAXPATHLEN];
+	char netns_name[MAXPATHLEN];
+	char etc_name[MAXPATHLEN];
+	struct dirent *entry;
+	DIR *dir;
+
+	snprintf(etc_netns_path, sizeof(etc_netns_path), "%s/%s", NETNS_ETC_DIR, name);
+	dir = opendir(etc_netns_path);
+	if (!dir)
+		return;
+
+	while ((entry = readdir(dir)) != NULL) {
+		if (strcmp(entry->d_name, ".") == 0)
+			continue;
+		if (strcmp(entry->d_name, "..") == 0)
+			continue;
+		snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name);
+		snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name);
+		if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) {
+			fprintf(stderr, "Bind %s -> %s failed: %s\n",
+				netns_name, etc_name, strerror(errno));
+		}
+	}
+	closedir(dir);
+}
+
+static int netns_exec(int argc, char **argv)
+{
+	/* Setup the proper environment for apps that are not netns
+	 * aware, and execute a program in that environment.
+	 */
+	const char *name, *cmd;
+	char net_path[MAXPATHLEN];
+	int netns;
+
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return -1;
+	}
+	if (argc < 2) {
+		fprintf(stderr, "No cmd specified\n");
+		return -1;
+	}
+	name = argv[0];
+	cmd = argv[1];
+	snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
+	netns = open(net_path, O_RDONLY);
+	if (netns < 0) {
+		fprintf(stderr, "Cannot open network namespace: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	if (setns(netns, CLONE_NEWNET) < 0) {
+		fprintf(stderr, "seting the network namespace failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	if (unshare(CLONE_NEWNS) < 0) {
+		fprintf(stderr, "unshare failed: %s\n", strerror(errno));
+		return -1;
+	}
+	/* Mount a version of /sys that describes the network namespace */
+	if (umount2("/sys", MNT_DETACH) < 0) {
+		fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno));
+		return -1;
+	}
+	if (mount(name, "/sys", "sysfs", 0, NULL) < 0) {
+		fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
+		return -1;
+	}
+
+	/* Setup bind mounts for config files in /etc */
+	bind_etc(name);
+
+	if (execvp(cmd, argv + 1)  < 0)
+		fprintf(stderr, "exec of %s failed: %s\n",
+			cmd, strerror(errno));
+	exit(-1);
+}
+
+static int netns_delete(int argc, char **argv)
+{
+	const char *name;
+	char netns_path[MAXPATHLEN];
+
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return -1;
+	}
+
+	name = argv[0];
+	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
+	umount2(netns_path, MNT_DETACH);
+	if (unlink(netns_path) < 0) {
+		fprintf(stderr, "Cannot remove %s: %s\n",
+			netns_path, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int netns_add(int argc, char **argv)
+{
+	/* This function creates a new network namespace and
+	 * a new mount namespace and bind them into a well known
+	 * location in the filesystem based on the name provided.
+	 *
+	 * The mount namespace is created so that any necessary
+	 * userspace tweaks like remounting /sys, or bind mounting
+	 * a new /etc/resolv.conf can be shared between uers.
+	 */
+	char netns_path[MAXPATHLEN];
+	const char *name;
+	int fd;
+
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return -1;
+	}
+	name = argv[0];
+
+	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
+
+	/* Create the base netns directory if it doesn't exist */
+	mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
+
+	/* Create the filesystem state */
+	fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
+	if (fd < 0) {
+		fprintf(stderr, "Could not create %s: %s\n",
+			netns_path, strerror(errno));
+		return -1;
+	}
+	close(fd);
+	if (unshare(CLONE_NEWNET) < 0) {
+		fprintf(stderr, "Failed to create a new network namespace: %s\n",
+			strerror(errno));
+		goto out_delete;
+	}
+
+	/* Bind the netns last so I can watch for it */
+	if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0) {
+		fprintf(stderr, "Bind /proc/self/ns/net -> %s failed: %s\n",
+			netns_path, strerror(errno));
+		goto out_delete;
+	}
+	return 0;
+out_delete:
+	netns_delete(argc, argv);
+	exit(-1);
+	return -1;
+}
+
+
+static int netns_monitor(int argc, char **argv)
+{
+	char buf[4096];
+	struct inotify_event *event;
+	int fd;
+	fd = inotify_init();
+	if (fd < 0) {
+		fprintf(stderr, "inotify_init failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) {
+		fprintf(stderr, "inotify_add_watch failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	for(;;) {
+		ssize_t len = read(fd, buf, sizeof(buf));
+		if (len < 0) {
+			fprintf(stderr, "read failed: %s\n",
+				strerror(errno));
+			return -1;
+		}
+		for (event = (struct inotify_event *)buf;
+		     (char *)event < &buf[len];
+		     event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len)) {
+			if (event->mask & IN_CREATE)
+				printf("add %s\n", event->name);
+			if (event->mask & IN_DELETE)
+				printf("delete %s\n", event->name);
+		}
+	}
+	return 0;
+}
+
+int do_netns(int argc, char **argv)
+{
+	if (argc < 1)
+		return netns_list(0, NULL);
+
+	if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) ||
+	    (matches(*argv, "lst") == 0))
+		return netns_list(argc-1, argv+1);
+
+	if (matches(*argv, "help") == 0)
+		usage();
+
+	if (matches(*argv, "add") == 0)
+		return netns_add(argc-1, argv+1);
+
+	if (matches(*argv, "delete") == 0)
+		return netns_delete(argc-1, argv+1);
+
+	if (matches(*argv, "exec") == 0)
+		return netns_exec(argc-1, argv+1);
+
+	if (matches(*argv, "monitor") == 0)
+		return netns_monitor(argc-1, argv+1);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ip/ipntable.c b/ip/ipntable.c
index 141ad42..639f512 100644
--- a/ip/ipntable.c
+++ b/ip/ipntable.c
@@ -313,7 +313,7 @@
 			  RTA_PAYLOAD(parms_rta));
 	}
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	return 0;
@@ -376,7 +376,7 @@
 		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*ndtm)));
 
 	if (tb[NDTA_NAME]) {
-		char *name = RTA_DATA(tb[NDTA_NAME]);
+		const char *name = rta_getattr_str(tb[NDTA_NAME]);
 
 		if (strlen(filter.name) > 0 && strcmp(filter.name, name))
 			return 0;
@@ -386,7 +386,7 @@
 			     RTA_PAYLOAD(tb[NDTA_PARMS]));
 
 		if (tpb[NDTPA_IFINDEX]) {
-			__u32 ifindex = *(__u32 *)RTA_DATA(tpb[NDTPA_IFINDEX]);
+			__u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]);
 
 			if (filter.index && filter.index != ifindex)
 				return 0;
@@ -406,7 +406,7 @@
 		fprintf(fp, "(%d) ", ndtm->ndtm_family);
 
 	if (tb[NDTA_NAME]) {
-		char *name = RTA_DATA(tb[NDTA_NAME]);
+		const char *name = rta_getattr_str(tb[NDTA_NAME]);
 		fprintf(fp, "%s ", name);
 	}
 
@@ -418,19 +418,19 @@
 		fprintf(fp, "    ");
 
 	if (tb[NDTA_THRESH1]) {
-		__u32 thresh1 = *(__u32 *)RTA_DATA(tb[NDTA_THRESH1]);
+		__u32 thresh1 = rta_getattr_u32(tb[NDTA_THRESH1]);
 		fprintf(fp, "thresh1 %u ", thresh1);
 	}
 	if (tb[NDTA_THRESH2]) {
-		__u32 thresh2 = *(__u32 *)RTA_DATA(tb[NDTA_THRESH2]);
+		__u32 thresh2 = rta_getattr_u32(tb[NDTA_THRESH2]);
 		fprintf(fp, "thresh2 %u ", thresh2);
 	}
 	if (tb[NDTA_THRESH3]) {
-		__u32 thresh3 = *(__u32 *)RTA_DATA(tb[NDTA_THRESH3]);
+		__u32 thresh3 = rta_getattr_u32(tb[NDTA_THRESH3]);
 		fprintf(fp, "thresh3 %u ", thresh3);
 	}
 	if (tb[NDTA_GC_INTERVAL]) {
-		__u64 gc_int = *(__u64 *)RTA_DATA(tb[NDTA_GC_INTERVAL]);
+		__u64 gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]);
 		fprintf(fp, "gc_int %llu ", gc_int);
 	}
 
@@ -469,7 +469,7 @@
 
 	if (tb[NDTA_PARMS]) {
 		if (tpb[NDTPA_IFINDEX]) {
-			__u32 ifindex = *(__u32 *)RTA_DATA(tpb[NDTPA_IFINDEX]);
+			__u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]);
 
 			fprintf(fp, "    ");
 			fprintf(fp, "dev %s ", ll_index_to_name(ifindex));
@@ -479,19 +479,19 @@
 		fprintf(fp, "    ");
 
 		if (tpb[NDTPA_REFCNT]) {
-			__u32 refcnt = *(__u32 *)RTA_DATA(tpb[NDTPA_REFCNT]);
+			__u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]);
 			fprintf(fp, "refcnt %u ", refcnt);
 		}
 		if (tpb[NDTPA_REACHABLE_TIME]) {
-			__u64 reachable = *(__u64 *)RTA_DATA(tpb[NDTPA_REACHABLE_TIME]);
+			__u64 reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]);
 			fprintf(fp, "reachable %llu ", reachable);
 		}
 		if (tpb[NDTPA_BASE_REACHABLE_TIME]) {
-			__u64 breachable = *(__u64 *)RTA_DATA(tpb[NDTPA_BASE_REACHABLE_TIME]);
+			__u64 breachable = rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]);
 			fprintf(fp, "base_reachable %llu ", breachable);
 		}
 		if (tpb[NDTPA_RETRANS_TIME]) {
-			__u64 retrans = *(__u64 *)RTA_DATA(tpb[NDTPA_RETRANS_TIME]);
+			__u64 retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]);
 			fprintf(fp, "retrans %llu ", retrans);
 		}
 
@@ -500,15 +500,15 @@
 		fprintf(fp, "    ");
 
 		if (tpb[NDTPA_GC_STALETIME]) {
-			__u64 gc_stale = *(__u64 *)RTA_DATA(tpb[NDTPA_GC_STALETIME]);
+			__u64 gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]);
 			fprintf(fp, "gc_stale %llu ", gc_stale);
 		}
 		if (tpb[NDTPA_DELAY_PROBE_TIME]) {
-			__u64 delay_probe = *(__u64 *)RTA_DATA(tpb[NDTPA_DELAY_PROBE_TIME]);
+			__u64 delay_probe = rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]);
 			fprintf(fp, "delay_probe %llu ", delay_probe);
 		}
 		if (tpb[NDTPA_QUEUE_LEN]) {
-			__u32 queue = *(__u32 *)RTA_DATA(tpb[NDTPA_QUEUE_LEN]);
+			__u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]);
 			fprintf(fp, "queue %u ", queue);
 		}
 
@@ -517,15 +517,15 @@
 		fprintf(fp, "    ");
 
 		if (tpb[NDTPA_APP_PROBES]) {
-			__u32 aprobe = *(__u32 *)RTA_DATA(tpb[NDTPA_APP_PROBES]);
+			__u32 aprobe = rta_getattr_u32(tpb[NDTPA_APP_PROBES]);
 			fprintf(fp, "app_probes %u ", aprobe);
 		}
 		if (tpb[NDTPA_UCAST_PROBES]) {
-			__u32 uprobe = *(__u32 *)RTA_DATA(tpb[NDTPA_UCAST_PROBES]);
+			__u32 uprobe = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]);
 			fprintf(fp, "ucast_probes %u ", uprobe);
 		}
 		if (tpb[NDTPA_MCAST_PROBES]) {
-			__u32 mprobe = *(__u32 *)RTA_DATA(tpb[NDTPA_MCAST_PROBES]);
+			__u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]);
 			fprintf(fp, "mcast_probes %u ", mprobe);
 		}
 
@@ -534,19 +534,19 @@
 		fprintf(fp, "    ");
 
 		if (tpb[NDTPA_ANYCAST_DELAY]) {
-			__u64 anycast_delay = *(__u64 *)RTA_DATA(tpb[NDTPA_ANYCAST_DELAY]);
+			__u64 anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]);
 			fprintf(fp, "anycast_delay %llu ", anycast_delay);
 		}
 		if (tpb[NDTPA_PROXY_DELAY]) {
-			__u64 proxy_delay = *(__u64 *)RTA_DATA(tpb[NDTPA_PROXY_DELAY]);
+			__u64 proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]);
 			fprintf(fp, "proxy_delay %llu ", proxy_delay);
 		}
 		if (tpb[NDTPA_PROXY_QLEN]) {
-			__u32 pqueue = *(__u32 *)RTA_DATA(tpb[NDTPA_PROXY_QLEN]);
+			__u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]);
 			fprintf(fp, "proxy_queue %u ", pqueue);
 		}
 		if (tpb[NDTPA_LOCKTIME]) {
-			__u64 locktime = *(__u64 *)RTA_DATA(tpb[NDTPA_LOCKTIME]);
+			__u64 locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]);
 			fprintf(fp, "locktime %llu ", locktime);
 		}
 
@@ -625,7 +625,7 @@
 		exit(1);
 	}
 
-	if (rtnl_dump_filter(&rth, print_ntable, stdout, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, print_ntable, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
diff --git a/ip/ipprefix.c b/ip/ipprefix.c
index cb1f582..d8327be 100644
--- a/ip/ipprefix.c
+++ b/ip/ipprefix.c
@@ -92,7 +92,7 @@
 
 	if (tb[PREFIX_CACHEINFO]) {
 		struct prefix_cacheinfo *pc;
-		pc = (struct prefix_cacheinfo *)tb[PREFIX_CACHEINFO];
+		pc = (struct prefix_cacheinfo *)RTA_DATA(tb[PREFIX_CACHEINFO]);
 
 		fprintf(fp, "valid %u ", pc->valid_time);
 		fprintf(fp, "preferred %u ", pc->preferred_time);
diff --git a/ip/iproute.c b/ip/iproute.c
index 16dee80..5cd313e 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -23,6 +23,7 @@
 #include <netinet/ip.h>
 #include <arpa/inet.h>
 #include <linux/in_route.h>
+#include <errno.h>
 
 #include "rt_names.h"
 #include "utils.h"
@@ -32,7 +33,11 @@
 #define RTAX_RTTVAR RTAX_HOPS
 #endif
 
-
+enum list_action {
+	IPROUTE_LIST,
+	IPROUTE_FLUSH,
+	IPROUTE_SAVE,
+};
 static const char *mx_names[RTAX_MAX+1] = {
 	[RTAX_MTU]	= "mtu",
 	[RTAX_WINDOW]	= "window",
@@ -53,9 +58,12 @@
 static void usage(void)
 {
 	fprintf(stderr, "Usage: ip route { list | flush } SELECTOR\n");
+	fprintf(stderr, "       ip route save SELECTOR\n");
+	fprintf(stderr, "       ip route restore\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, "                            [ mark NUMBER ]\n");
+	fprintf(stderr, "       ip route { add | del | change | append | replace } 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");
@@ -77,7 +85,7 @@
 	fprintf(stderr, "MP_ALGO := { rr | drr | random | wrandom }\n");
 	fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n");
 	fprintf(stderr, "RTPROTO := [ kernel | boot | static | NUMBER ]\n");
-	fprintf(stderr, "TIME := NUMBER[s|ms|us|ns|j]\n");
+	fprintf(stderr, "TIME := NUMBER[s|ms]\n");
 	exit(-1);
 }
 
@@ -96,6 +104,7 @@
 	int tos, tosmask;
 	int iif, iifmask;
 	int oif, oifmask;
+	int mark, markmask;
 	int realm, realmmask;
 	inet_prefix rprefsrc;
 	inet_prefix rvia;
@@ -115,46 +124,16 @@
 	return 0;
 }
 
-int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
 {
-	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;
-	static int ip6_multiple_tables;
+	inet_prefix prefsrc;
 	__u32 table;
-	SPRINT_BUF(b1);
-	static int hz;
+	static int ip6_multiple_tables;
 
-	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;
-
-	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
 	table = rtm_get_table(r, tb);
 
 	if (r->rtm_family == AF_INET6 && table != RT_TABLE_MAIN)
@@ -248,7 +227,7 @@
 	if (filter.realmmask) {
 		__u32 realms = 0;
 		if (tb[RTA_FLOW])
-			realms = *(__u32*)RTA_DATA(tb[RTA_FLOW]);
+			realms = rta_getattr_u32(tb[RTA_FLOW]);
 		if ((realms^filter.realm)&filter.realmmask)
 			return 0;
 	}
@@ -266,6 +245,13 @@
 		if ((oif^filter.oif)&filter.oifmask)
 			return 0;
 	}
+	if (filter.markmask) {
+		int mark = 0;
+		if (tb[RTA_MARK])
+			mark = *(int *)RTA_DATA(tb[RTA_MARK]);
+		if ((mark ^ filter.mark) & filter.markmask)
+			return 0;
+	}
 	if (filter.flushb &&
 	    r->rtm_family == AF_INET6 &&
 	    r->rtm_dst_len == 0 &&
@@ -274,6 +260,56 @@
 	    *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
 		return 0;
 
+	return 1;
+}
+
+int calc_host_len(struct rtmsg *r)
+{
+	if (r->rtm_family == AF_INET6)
+		return 128;
+	else if (r->rtm_family == AF_INET)
+		return 32;
+	else if (r->rtm_family == AF_DECnet)
+		return 16;
+	else if (r->rtm_family == AF_IPX)
+		return 80;
+	else
+		return -1;
+}
+
+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];
+	int host_len = -1;
+	__u32 table;
+	SPRINT_BUF(b1);
+	static int hz;
+
+	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;
+	}
+
+	host_len = calc_host_len(r);
+
+	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+	table = rtm_get_table(r, tb);
+
+	if (!filter_nlmsg(n, tb, host_len))
+		return 0;
+
 	if (filter.flushb) {
 		struct nlmsghdr *fn;
 		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
@@ -368,7 +404,7 @@
 				    abuf, sizeof(abuf)));
 	}
 	if (tb[RTA_PRIORITY])
-		fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
+		fprintf(fp, " metric %u ", rta_getattr_u32(tb[RTA_PRIORITY]));
 	if (r->rtm_flags & RTNH_F_DEAD)
 		fprintf(fp, "dead ");
 	if (r->rtm_flags & RTNH_F_ONLINK)
@@ -377,9 +413,18 @@
 		fprintf(fp, "pervasive ");
 	if (r->rtm_flags & RTM_F_NOTIFY)
 		fprintf(fp, "notify ");
+	if (tb[RTA_MARK]) {
+		unsigned int mark = *(unsigned int*)RTA_DATA(tb[RTA_MARK]);
+		if (mark) {
+			if (mark >= 16)
+				fprintf(fp, " mark 0x%x", mark);
+			else
+				fprintf(fp, " mark %u", mark);
+		}
+	}
 
 	if (tb[RTA_FLOW] && filter.realmmask != ~0U) {
-		__u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]);
+		__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
 		__u32 from = to>>16;
 		to &= 0xFFFF;
 		fprintf(fp, "realm%s ", from ? "s" : "");
@@ -433,12 +478,11 @@
 				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
+				fprintf(fp, " ts 0x%x tsage %dsec",
+					ci->rta_ts, ci->rta_tsage);
 		}
 	} else if (r->rtm_family == AF_INET6) {
 		struct rta_cacheinfo *ci = NULL;
@@ -481,8 +525,6 @@
 
 			if (mxrta[i] == NULL)
 				continue;
-			if (!hz)
-				hz = get_user_hz();
 
 			if (i < sizeof(mx_names)/sizeof(char*) && mx_names[i])
 				fprintf(fp, " %s", mx_names[i]);
@@ -504,18 +546,15 @@
 			case RTAX_RTT:
 			case RTAX_RTTVAR:
 			case RTAX_RTO_MIN:
-				val *= 1000;
 				if (i == RTAX_RTT)
 					val /= 8;
 				else if (i == RTAX_RTTVAR)
 					val /= 4;
 
-				if (val >= hz)
-					fprintf(fp, " %llums",
-						(unsigned long long) val / hz);
+				if (val >= 1000)
+					fprintf(fp, " %gs", val/1e3);
 				else
-					fprintf(fp, " %.2fms", 
-						(double)val / hz);
+					fprintf(fp, " %ums", val);
 			}
 		}
 	}
@@ -550,7 +589,7 @@
 							    abuf, sizeof(abuf)));
 				}
 				if (tb[RTA_FLOW]) {
-					__u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]);
+					__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
 					__u32 from = to>>16;
 					to &= 0xFFFF;
 					fprintf(fp, " realm%s ", from ? "s" : "");
@@ -673,8 +712,6 @@
 	int nhs_ok = 0;
 	int scope_ok = 0;
 	int table_ok = 0;
-	int proto_ok = 0;
-	int type_ok = 0;
 	int raw = 0;
 
 	memset(&req, 0, sizeof(req));
@@ -752,7 +789,6 @@
 			if (get_unsigned(&mtu, *argv, 0))
 				invarg("\"mtu\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
-#ifdef RTAX_HOPLIMIT
 		} else if (strcmp(*argv, "hoplimit") == 0) {
 			unsigned hoplimit;
 			NEXT_ARG();
@@ -763,8 +799,6 @@
 			if (get_unsigned(&hoplimit, *argv, 0))
 				invarg("\"hoplimit\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
-#endif
-#ifdef RTAX_ADVMSS
 		} else if (strcmp(*argv, "advmss") == 0) {
 			unsigned mss;
 			NEXT_ARG();
@@ -775,8 +809,6 @@
 			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();
@@ -787,7 +819,6 @@
 			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();
@@ -795,7 +826,7 @@
 				mxlock |= (1<<RTAX_RTT);
 				NEXT_ARG();
 			}
-			if (get_jiffies(&rtt, *argv, 0, &raw))
+			if (get_time_rtt(&rtt, *argv, &raw))
 				invarg("\"rtt\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, 
 				(raw) ? rtt : rtt * 8);
@@ -803,7 +834,7 @@
 			unsigned rto_min;
 			NEXT_ARG();
 			mxlock |= (1<<RTAX_RTO_MIN);
-			if (get_jiffies(&rto_min, *argv, 0, &raw))
+			if (get_time_rtt(&rto_min, *argv, &raw))
 				invarg("\"rto_min\" value is invalid\n",
 				       *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
@@ -855,7 +886,7 @@
 				mxlock |= (1<<RTAX_RTTVAR);
 				NEXT_ARG();
 			}
-			if (get_jiffies(&win, *argv, 0, &raw))
+			if (get_time_rtt(&win, *argv, &raw))
 				invarg("\"rttvar\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
 				(raw) ? win : win * 4);
@@ -886,7 +917,6 @@
 			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) {
 			__u32 tid;
 			NEXT_ARG();
@@ -914,7 +944,6 @@
 			    rtnl_rtntype_a2n(&type, *argv) == 0) {
 				NEXT_ARG();
 				req.r.rtm_type = type;
-				type_ok = 1;
 			}
 
 			if (matches(*argv, "help") == 0)
@@ -982,7 +1011,7 @@
 	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)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	return 0;
@@ -1035,17 +1064,52 @@
 	return 0;
 }
 
+int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	int ret;
+	int len = n->nlmsg_len;
+	struct rtmsg *r = NLMSG_DATA(n);
+	struct rtattr *tb[RTA_MAX+1];
+	int host_len = -1;
 
-static int iproute_list_or_flush(int argc, char **argv, int flush)
+	if (isatty(STDOUT_FILENO)) {
+		fprintf(stderr, "Not sending binary stream to stdout\n");
+		return -1;
+	}
+
+	host_len = calc_host_len(r);
+	len -= NLMSG_LENGTH(sizeof(*r));
+	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+	if (!filter_nlmsg(n, tb, host_len))
+		return 0;
+
+	ret = write(STDOUT_FILENO, n, n->nlmsg_len);
+	if ((ret > 0) && (ret != n->nlmsg_len)) {
+		fprintf(stderr, "Short write while saving nlmsg\n");
+		ret = -EIO;
+	}
+
+	return ret == n->nlmsg_len ? 0 : ret;
+}
+
+static int iproute_list_flush_or_save(int argc, char **argv, int action)
 {
 	int do_ipv6 = preferred_family;
 	char *id = NULL;
 	char *od = NULL;
+	unsigned int mark = 0;
+	rtnl_filter_t filter_fn;
+
+	if (action == IPROUTE_SAVE)
+		filter_fn = save_route;
+	else
+		filter_fn = print_route;
 
 	iproute_reset_filter();
 	filter.tb = RT_TABLE_MAIN;
 
-	if (flush && argc <= 0) {
+	if ((action == IPROUTE_FLUSH) && argc <= 0) {
 		fprintf(stderr, "\"ip route flush\" requires arguments.\n");
 		return -1;
 	}
@@ -1113,6 +1177,10 @@
 		} else if (strcmp(*argv, "iif") == 0) {
 			NEXT_ARG();
 			id = *argv;
+		} else if (strcmp(*argv, "mark") == 0) {
+			NEXT_ARG();
+			get_unsigned(&mark, *argv, 0);
+			filter.markmask = -1;
 		} else if (strcmp(*argv, "via") == 0) {
 			NEXT_ARG();
 			get_prefix(&filter.rvia, *argv, do_ipv6);
@@ -1194,8 +1262,9 @@
 			filter.oifmask = -1;
 		}
 	}
+	filter.mark = mark;
 
-	if (flush) {
+	if (action == IPROUTE_FLUSH) {
 		int round = 0;
 		char flushb[4096-512];
 		time_t start = time(0);
@@ -1220,7 +1289,7 @@
 				exit(1);
 			}
 			filter.flushed = 0;
-			if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+			if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
 				fprintf(stderr, "Flush terminated\n");
 				exit(1);
 			}
@@ -1263,7 +1332,7 @@
 		}
 	}
 
-	if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
@@ -1283,10 +1352,12 @@
 	char  *odev = NULL;
 	int connected = 0;
 	int from_ok = 0;
+	unsigned int mark = 0;
 
 	memset(&req, 0, sizeof(req));
 
 	iproute_reset_filter();
+	filter.cloned = 2;
 
 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
 	req.n.nlmsg_flags = NLM_F_REQUEST;
@@ -1323,6 +1394,9 @@
 		} else if (matches(*argv, "iif") == 0) {
 			NEXT_ARG();
 			idev = *argv;
+		} else if (matches(*argv, "mark") == 0) {
+			NEXT_ARG();
+			get_unsigned(&mark, *argv, 0);
 		} else if (matches(*argv, "oif") == 0 ||
 			   strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
@@ -1373,11 +1447,13 @@
 			addattr32(&req.n, sizeof(req), RTA_OIF, idx);
 		}
 	}
+	if (mark)
+		addattr32(&req.n, sizeof(req), RTA_MARK, mark);
 
 	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)
+	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
 		exit(2);
 
 	if (connected && !from_ok) {
@@ -1418,7 +1494,7 @@
 		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)
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
 			exit(2);
 	}
 
@@ -1430,6 +1506,26 @@
 	exit(0);
 }
 
+int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+{
+	int ret;
+
+	n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+
+	ll_init_map(&rth);
+
+	ret = rtnl_talk(&rth, n, 0, 0, n);
+	if ((ret < 0) && (errno == EEXIST))
+		ret = 0;
+
+	return ret;
+}
+
+int iproute_restore(void)
+{
+	exit(rtnl_from_file(stdin, &restore_handler, NULL));
+}
+
 void iproute_reset_filter()
 {
 	memset(&filter, 0, sizeof(filter));
@@ -1440,7 +1536,7 @@
 int do_iproute(int argc, char **argv)
 {
 	if (argc < 1)
-		return iproute_list_or_flush(0, NULL, 0);
+		return iproute_list_flush_or_save(0, NULL, IPROUTE_LIST);
 
 	if (matches(*argv, "add") == 0)
 		return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL,
@@ -1465,11 +1561,15 @@
 				      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);
+		return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_LIST);
 	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);
+		return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_FLUSH);
+	if (matches(*argv, "save") == 0)
+		return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_SAVE);
+	if (matches(*argv, "restore") == 0)
+		return iproute_restore();
 	if (matches(*argv, "help") == 0)
 		usage();
 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv);
diff --git a/ip/iprule.c b/ip/iprule.c
index 9318d8c..a5fcd43 100644
--- a/ip/iprule.c
+++ b/ip/iprule.c
@@ -131,23 +131,23 @@
 		__u32 mark = 0, mask = 0;
 
 		if (tb[FRA_FWMARK])
-			mark = *(__u32*)RTA_DATA(tb[FRA_FWMARK]);
+			mark = rta_getattr_u32(tb[FRA_FWMARK]);
 
 		if (tb[FRA_FWMASK] &&
-		    (mask = *(__u32*)RTA_DATA(tb[FRA_FWMASK])) != 0xFFFFFFFF)
+		    (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF)
 			fprintf(fp, "fwmark 0x%x/0x%x ", mark, mask);
 		else
 			fprintf(fp, "fwmark 0x%x ", mark);
 	}
 
 	if (tb[FRA_IFNAME]) {
-		fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[FRA_IFNAME]));
+		fprintf(fp, "iif %s ", rta_getattr_str(tb[FRA_IFNAME]));
 		if (r->rtm_flags & FIB_RULE_IIF_DETACHED)
 			fprintf(fp, "[detached] ");
 	}
 
 	if (tb[FRA_OIFNAME]) {
-		fprintf(fp, "oif %s ", (char*)RTA_DATA(tb[FRA_OIFNAME]));
+		fprintf(fp, "oif %s ", rta_getattr_str(tb[FRA_OIFNAME]));
 		if (r->rtm_flags & FIB_RULE_OIF_DETACHED)
 			fprintf(fp, "[detached] ");
 	}
@@ -157,7 +157,7 @@
 		fprintf(fp, "lookup %s ", rtnl_rttable_n2a(table, b1, sizeof(b1)));
 
 	if (tb[FRA_FLOW]) {
-		__u32 to = *(__u32*)RTA_DATA(tb[FRA_FLOW]);
+		__u32 to = rta_getattr_u32(tb[FRA_FLOW]);
 		__u32 from = to>>16;
 		to &= 0xFFFF;
 		if (from) {
@@ -180,7 +180,7 @@
 	} else if (r->rtm_type == FR_ACT_GOTO) {
 		fprintf(fp, "goto ");
 		if (tb[FRA_GOTO])
-			fprintf(fp, "%u", *(__u32 *) RTA_DATA(tb[FRA_GOTO]));
+			fprintf(fp, "%u", rta_getattr_u32(tb[FRA_GOTO]));
 		else
 			fprintf(fp, "none");
 		if (r->rtm_flags & FIB_RULE_UNRESOLVED)
@@ -212,7 +212,7 @@
 		return 1;
 	}
 
-	if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, print_rule, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
@@ -355,7 +355,7 @@
 	if (!table_ok && cmd == RTM_NEWRULE)
 		req.r.rtm_table = RT_TABLE_MAIN;
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		return 2;
 
 	return 0;
@@ -382,7 +382,7 @@
 		if (rtnl_open(&rth2, 0) < 0)
 			return -1;
 
-		if (rtnl_talk(&rth2, n, 0, 0, NULL, NULL, NULL) < 0)
+		if (rtnl_talk(&rth2, n, 0, 0, NULL) < 0)
 			return -2;
 
 		rtnl_close(&rth2);
@@ -408,7 +408,7 @@
 		return 1;
 	}
 
-	if (rtnl_dump_filter(&rth, flush_rule, NULL, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, flush_rule, NULL) < 0) {
 		fprintf(stderr, "Flush terminated\n");
 		return 1;
 	}
diff --git a/ip/iptunnel.c b/ip/iptunnel.c
index 7c682a7..3d41a27 100644
--- a/ip/iptunnel.c
+++ b/ip/iptunnel.c
@@ -18,8 +18,8 @@
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <sys/ioctl.h>
-#include <linux/if.h>
-#include <linux/if_arp.h>
+#include <net/if.h>
+#include <net/if_arp.h>
 #include <linux/ip.h>
 #include <linux/if_tunnel.h>
 
@@ -231,7 +231,7 @@
 	}
 
 	if (medium[0]) {
-		p->link = tnl_ioctl_get_ifindex(medium);
+		p->link = if_nametoindex(medium);
 		if (p->link == 0)
 			return -1;
 	}
@@ -306,12 +306,8 @@
 	struct ip_tunnel_6rd ip6rd;
 	char s1[1024];
 	char s2[1024];
-	char s3[64];
-	char s4[64];
 
 	memset(&ip6rd, 0, sizeof(ip6rd));
-	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.
@@ -342,7 +338,7 @@
 	}
 
 	if (p->link) {
-		char *n = tnl_ioctl_get_ifname(p->link);
+		const char *n = ll_index_to_name(p->link);
 		if (n)
 			printf(" dev %s ", n);
 	}
@@ -377,12 +373,12 @@
 	}
 
 	if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
-		printf(" key %s", s3);
+		printf(" key %u", ntohl(p->i_key));
 	else if ((p->i_flags|p->o_flags)&GRE_KEY) {
 		if (p->i_flags&GRE_KEY)
-			printf(" ikey %s ", s3);
+			printf(" ikey %u ", ntohl(p->i_key));
 		if (p->o_flags&GRE_KEY)
-			printf(" okey %s ", s4);
+			printf(" okey %u ", ntohl(p->o_key));
 	}
 
 	if (p->i_flags&GRE_SEQ)
@@ -402,7 +398,6 @@
 	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];
@@ -412,10 +407,16 @@
 		return -1;
 	}
 
-	fgets(buf, sizeof(buf), fp);
-	fgets(buf, sizeof(buf), fp);
+	/* skip header lines */
+	if (!fgets(buf, sizeof(buf), fp) ||
+	    !fgets(buf, sizeof(buf), fp)) {
+		fprintf(stderr, "/proc/net/dev read error\n");
+		fclose(fp);
+		return -1;
+	}
 
 	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		int index, type;
 		char *ptr;
 		buf[sizeof(buf) - 1] = 0;
 		if ((ptr = strchr(buf, ':')) == NULL ||
@@ -432,7 +433,10 @@
 			continue;
 		if (p->name[0] && strcmp(p->name, name))
 			continue;
-		type = tnl_ioctl_get_iftype(name);
+		index = ll_name_to_index(name);
+		if (index == 0)
+			continue;
+		type = ll_index_to_type(index);
 		if (type == -1) {
 			fprintf(stderr, "Failed to get type of [%s]\n", name);
 			continue;
@@ -469,6 +473,7 @@
 	int err;
 	struct ip_tunnel_parm p;
 
+	ll_init_map(&rth);
 	if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
 		return -1;
 
diff --git a/ip/iptuntap.c b/ip/iptuntap.c
index 2a8aa7f..29f2777 100644
--- a/ip/iptuntap.c
+++ b/ip/iptuntap.c
@@ -47,7 +47,7 @@
 
 static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid)
 {
-	int fd = open(TUNDEV, O_RDWR);
+	int fd;
 	int ret = -1;
 
 #ifdef IFF_TUN_EXCL
@@ -298,6 +298,7 @@
 			printf(" group %ld", group);
 		printf("\n");
 	}
+	closedir(dir);
 	return 0;
 }
 
diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c
index 32e56b1..6be62e0 100644
--- a/ip/ipxfrm.c
+++ b/ip/ipxfrm.c
@@ -62,8 +62,8 @@
 static void usage(void)
 {
 	fprintf(stderr,
-		"Usage: ip xfrm XFRM_OBJECT { COMMAND | help }\n"
-		"where  XFRM_OBJECT := { state | policy | monitor }\n");
+		"Usage: ip xfrm XFRM-OBJECT { COMMAND | help }\n"
+		"where  XFRM-OBJECT := state | policy | monitor\n");
 	exit(-1);
 }
 
@@ -158,6 +158,7 @@
 static const struct typeent algo_types[]= {
 	{ "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH },
 	{ "comp", XFRMA_ALG_COMP }, { "aead", XFRMA_ALG_AEAD },
+	{ "auth-trunc", XFRMA_ALG_AUTH_TRUNC },
 	{ NULL, -1 }
 };
 
@@ -486,6 +487,12 @@
 		if (sel->dport_mask)
 			fprintf(fp, "code %u ", ntohs(sel->dport));
 		break;
+	case IPPROTO_GRE:
+		if (sel->sport_mask || sel->dport_mask)
+			fprintf(fp, "key %u ",
+				(((__u32)ntohs(sel->sport)) << 16) +
+				ntohs(sel->dport));
+		break;
 	case IPPROTO_MH:
 		if (sel->sport_mask)
 			fprintf(fp, "type %u ", ntohs(sel->sport));
@@ -567,6 +574,25 @@
 	fprintf(fp, "%s", _SL_);
 }
 
+static void xfrm_auth_trunc_print(struct xfrm_algo_auth *algo, int len,
+				  FILE *fp, const char *prefix)
+{
+	struct {
+		struct xfrm_algo algo;
+		char key[algo->alg_key_len / 8];
+	} base;
+
+	memcpy(base.algo.alg_name, algo->alg_name, sizeof(base.algo.alg_name));
+	base.algo.alg_key_len = algo->alg_key_len;
+	memcpy(base.algo.alg_key, algo->alg_key, algo->alg_key_len / 8);
+
+	__xfrm_algo_print(&base.algo, XFRMA_ALG_AUTH_TRUNC, len, fp, prefix, 0);
+
+	fprintf(fp, " %d", algo->alg_trunc_len);
+
+	fprintf(fp, "%s", _SL_);
+}
+
 static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int len,
 			    __u16 family, FILE *fp, const char *prefix)
 {
@@ -674,12 +700,18 @@
 		fprintf(fp, "\tmark %d/0x%x\n", m->v, m->m);
 	}
 
-	if (tb[XFRMA_ALG_AUTH]) {
+	if (tb[XFRMA_ALG_AUTH] && !tb[XFRMA_ALG_AUTH_TRUNC]) {
 		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_AUTH_TRUNC]) {
+		struct rtattr *rta = tb[XFRMA_ALG_AUTH_TRUNC];
+		xfrm_auth_trunc_print((struct xfrm_algo_auth *) RTA_DATA(rta),
+				      RTA_PAYLOAD(rta), fp, prefix);
+	}
+
 	if (tb[XFRMA_ALG_AEAD]) {
 		struct rtattr *rta = tb[XFRMA_ALG_AEAD];
 		xfrm_aead_print((struct xfrm_algo_aead *)RTA_DATA(rta),
@@ -777,7 +809,7 @@
 			return;
 		}
 
-		lastused = *(__u64 *)RTA_DATA(tb[XFRMA_LASTUSED]);
+		lastused = rta_getattr_u64(tb[XFRMA_LASTUSED]);
 
 		fprintf(fp, "%s", strxf_time(lastused));
 		fprintf(fp, "%s", _SL_);
@@ -825,6 +857,7 @@
 		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_WILDRECV, "wildrecv");
 		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ICMP, "icmp");
 		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_AF_UNSPEC, "af-unspec");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ALIGN4, "align4");
 		if (flags)
 			fprintf(fp, "%x", flags);
 	}
@@ -847,6 +880,20 @@
 		xfrm_lifetime_print(&xsinfo->lft, &xsinfo->curlft, fp, buf);
 		xfrm_stats_print(&xsinfo->stats, fp, buf);
 	}
+
+	if (tb[XFRMA_SEC_CTX]) {
+		struct xfrm_user_sec_ctx *sctx;
+
+		fprintf(fp, "\tsecurity context ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx))
+			fprintf(fp, "(ERROR truncated)");
+
+		sctx = (struct xfrm_user_sec_ctx *)RTA_DATA(tb[XFRMA_SEC_CTX]);
+
+		fprintf(fp, "%s %s", (char *)(sctx + 1), _SL_);
+	}
+
 }
 
 void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo,
@@ -859,12 +906,31 @@
 
 	xfrm_selector_print(&xpinfo->sel, preferred_family, fp, title);
 
+	if (tb[XFRMA_SEC_CTX]) {
+		struct xfrm_user_sec_ctx *sctx;
+
+		fprintf(fp, "\tsecurity context ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx))
+			fprintf(fp, "(ERROR truncated)");
+
+		sctx = (struct xfrm_user_sec_ctx *)RTA_DATA(tb[XFRMA_SEC_CTX]);
+
+		fprintf(fp, "%s ", (char *)(sctx + 1));
+		fprintf(fp, "%s", _SL_);
+	}
+
 	if (prefix)
 		STRBUF_CAT(buf, prefix);
 	STRBUF_CAT(buf, "\t");
 
 	fputs(buf, fp);
-	fprintf(fp, "dir ");
+	if (xpinfo->dir >= XFRM_POLICY_MAX) {
+		xpinfo->dir -= XFRM_POLICY_MAX;
+		fprintf(fp, "socket ");
+	} else
+		fprintf(fp, "dir ");
+
 	switch (xpinfo->dir) {
 	case XFRM_POLICY_IN:
 		fprintf(fp, "in");
@@ -918,6 +984,7 @@
 
 		fprintf(fp, "flag ");
 		XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_LOCALOK, "localok");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_ICMP, "icmp");
 		if (flags)
 			fprintf(fp, "%x", flags);
 	}
@@ -976,7 +1043,7 @@
 
 			ret = xfrm_xfrmproto_getbyname(*argv);
 			if (ret < 0)
-				invarg("\"XFRM_PROTO\" is invalid", *argv);
+				invarg("\"XFRM-PROTO\" is invalid", *argv);
 
 			id->proto = (__u8)ret;
 
@@ -1008,7 +1075,7 @@
 		invarg("the same address family is required between \"src\" and \"dst\"", *argv);
 
 	if (loose == 0 && id->proto == 0)
-		missarg("XFRM_PROTO");
+		missarg("XFRM-PROTO");
 	if (argc == *argcp)
 		missarg("ID");
 
@@ -1084,6 +1151,7 @@
 	char *dportp = NULL;
 	char *typep = NULL;
 	char *codep = NULL;
+	char *grekey = NULL;
 
 	while (1) {
 		if (strcmp(*argv, "proto") == 0) {
@@ -1160,6 +1228,29 @@
 
 			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;
 
+		} else if (strcmp(*argv, "key") == 0) {
+			unsigned uval;
+
+			grekey = *argv;
+
+			NEXT_ARG();
+
+			if (strchr(*argv, '.'))
+				uval = htonl(get_addr32(*argv));
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value of \"key\"\n");
+					exit(-1);
+				}
+			}
+
+			sel->sport = htons(uval >> 16);
+			sel->dport = htons(uval & 0xffff);
+			sel->sport_mask = ~((__u16)0);
+			sel->dport_mask = ~((__u16)0);
+
+			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;
+
 		} else {
 			PREV_ARG(); /* back track */
 			break;
@@ -1194,6 +1285,15 @@
 			exit(1);
 		}
 	}
+	if (grekey) {
+		switch (sel->proto) {
+		case IPPROTO_GRE:
+			break;
+		default:
+			fprintf(stderr, "\"key\" is invalid with proto=%s\n", strxf_proto(sel->proto));
+			exit(1);
+		}
+	}
 
 	*argcp = argc;
 	*argvp = argv;
diff --git a/ip/link_gre.c b/ip/link_gre.c
index 9f8bde6..839fb29 100644
--- a/ip/link_gre.c
+++ b/ip/link_gre.c
@@ -72,7 +72,7 @@
 		req.i.ifi_family = preferred_family;
 		req.i.ifi_index = ifi->ifi_index;
 
-		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
 get_failed:
 			fprintf(stderr,
 				"Failed to get existing tunnel info.\n");
@@ -98,35 +98,35 @@
 				    linkinfo[IFLA_INFO_DATA]);
 
 		if (greinfo[IFLA_GRE_IKEY])
-			ikey = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_IKEY]);
+			ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
 
 		if (greinfo[IFLA_GRE_OKEY])
-			okey = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_OKEY]);
+			okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
 
 		if (greinfo[IFLA_GRE_IFLAGS])
-			iflags = *(__u16 *)RTA_DATA(greinfo[IFLA_GRE_IFLAGS]);
+			iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
 
 		if (greinfo[IFLA_GRE_OFLAGS])
-			oflags = *(__u16 *)RTA_DATA(greinfo[IFLA_GRE_OFLAGS]);
+			oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
 
 		if (greinfo[IFLA_GRE_LOCAL])
-			saddr = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_LOCAL]);
+			saddr = rta_getattr_u32(greinfo[IFLA_GRE_LOCAL]);
 
 		if (greinfo[IFLA_GRE_REMOTE])
-			daddr = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_REMOTE]);
+			daddr = rta_getattr_u32(greinfo[IFLA_GRE_REMOTE]);
 
 		if (greinfo[IFLA_GRE_PMTUDISC])
-			pmtudisc = *(__u8 *)RTA_DATA(
+			pmtudisc = rta_getattr_u8(
 				greinfo[IFLA_GRE_PMTUDISC]);
 
 		if (greinfo[IFLA_GRE_TTL])
-			ttl = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_TTL]);
+			ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
 
 		if (greinfo[IFLA_GRE_TOS])
-			tos = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_TOS]);
+			tos = rta_getattr_u8(greinfo[IFLA_GRE_TOS]);
 
 		if (greinfo[IFLA_GRE_LINK])
-			link = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_LINK]);
+			link = rta_getattr_u8(greinfo[IFLA_GRE_LINK]);
 	}
 
 	while (argc > 0) {
@@ -206,7 +206,7 @@
 				saddr = get_addr32(*argv);
 		} else if (!matches(*argv, "dev")) {
 			NEXT_ARG();
-			link = tnl_ioctl_get_ifindex(*argv);
+			link = if_nametoindex(*argv);
 			if (link == 0)
 				exit(-1);
 		} else if (!matches(*argv, "ttl") ||
@@ -279,7 +279,7 @@
 		return;
 
 	if (tb[IFLA_GRE_REMOTE]) {
-		unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_GRE_REMOTE]);
+		unsigned addr = rta_getattr_u32(tb[IFLA_GRE_REMOTE]);
 
 		if (addr)
 			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
@@ -288,7 +288,7 @@
 	fprintf(f, "remote %s ", remote);
 
 	if (tb[IFLA_GRE_LOCAL]) {
-		unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_GRE_LOCAL]);
+		unsigned addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]);
 
 		if (addr)
 			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
@@ -296,9 +296,9 @@
 
 	fprintf(f, "local %s ", local);
 
-	if (tb[IFLA_GRE_LINK] && *(__u32 *)RTA_DATA(tb[IFLA_GRE_LINK])) {
-		unsigned link = *(__u32 *)RTA_DATA(tb[IFLA_GRE_LINK]);
-		char *n = tnl_ioctl_get_ifname(link);
+	if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
+		const char *n = if_indextoname(link, s2);
 
 		if (n)
 			fprintf(f, "dev %s ", n);
@@ -306,13 +306,13 @@
 			fprintf(f, "dev %u ", link);
 	}
 
-	if (tb[IFLA_GRE_TTL] && *(__u8 *)RTA_DATA(tb[IFLA_GRE_TTL]))
-		fprintf(f, "ttl %d ", *(__u8 *)RTA_DATA(tb[IFLA_GRE_TTL]));
+	if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
 	else
 		fprintf(f, "ttl inherit ");
 
-	if (tb[IFLA_GRE_TOS] && *(__u8 *)RTA_DATA(tb[IFLA_GRE_TOS])) {
-		int tos = *(__u8 *)RTA_DATA(tb[IFLA_GRE_TOS]);
+	if (tb[IFLA_GRE_TOS] && rta_getattr_u8(tb[IFLA_GRE_TOS])) {
+		int tos = rta_getattr_u8(tb[IFLA_GRE_TOS]);
 
 		fputs("tos ", f);
 		if (tos == 1)
@@ -322,25 +322,23 @@
 	}
 
 	if (tb[IFLA_GRE_PMTUDISC] &&
-	    !*(__u8 *)RTA_DATA(tb[IFLA_GRE_PMTUDISC]))
+	    !rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
 		fputs("nopmtudisc ", f);
 
 	if (tb[IFLA_GRE_IFLAGS])
-		iflags = *(__u16 *)RTA_DATA(tb[IFLA_GRE_IFLAGS]);
+		iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
 
 	if (tb[IFLA_GRE_OFLAGS])
-		oflags = *(__u16 *)RTA_DATA(tb[IFLA_GRE_OFLAGS]);
+		oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
 
-	if (iflags & GRE_KEY && tb[IFLA_GRE_IKEY] &&
-	    *(__u32 *)RTA_DATA(tb[IFLA_GRE_IKEY])) {
+	if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
 		fprintf(f, "ikey %s ", s2);
 	}
 
-	if (oflags & GRE_KEY && tb[IFLA_GRE_OKEY] &&
-	    *(__u32 *)RTA_DATA(tb[IFLA_GRE_OKEY])) {
+	if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
-		fprintf(f, "ikey %s ", s2);
+		fprintf(f, "okey %s ", s2);
 	}
 
 	if (iflags & GRE_SEQ)
diff --git a/ip/link_veth.c b/ip/link_veth.c
index 9f5e871..3d19b01 100644
--- a/ip/link_veth.c
+++ b/ip/link_veth.c
@@ -30,6 +30,7 @@
 	char *name, *type, *link, *dev;
 	int err, len;
 	struct rtattr * data;
+	int group;
 
 	if (strcmp(argv[0], "peer") != 0) {
 		usage();
@@ -42,7 +43,7 @@
 	hdr->nlmsg_len += sizeof(struct ifinfomsg);
 
 	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)hdr,
-			   &name, &type, &link, &dev);
+			   &name, &type, &link, &dev, &group);
 	if (err < 0)
 		return err;
 
diff --git a/ip/rtmon.c b/ip/rtmon.c
index 7869b32..c1416a0 100644
--- a/ip/rtmon.c
+++ b/ip/rtmon.c
@@ -163,7 +163,7 @@
 
 	write_stamp(fp);
 
-	if (rtnl_dump_filter(&rth, dump_msg, fp, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, dump_msg, fp) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
diff --git a/ip/tunnel.c b/ip/tunnel.c
index 6efbd2d..b176d3f 100644
--- a/ip/tunnel.c
+++ b/ip/tunnel.c
@@ -63,58 +63,6 @@
 	return buf;
 }
 
-int tnl_ioctl_get_ifindex(const char *dev)
-{
-	struct ifreq ifr;
-	int fd;
-	int err;
-
-	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
-	fd = socket(preferred_family, SOCK_DGRAM, 0);
-	err = ioctl(fd, SIOCGIFINDEX, &ifr);
-	if (err) {
-		perror("ioctl");
-		return 0;
-	}
-	close(fd);
-	return ifr.ifr_ifindex;
-}
-
-int tnl_ioctl_get_iftype(const char *dev)
-{
-	struct ifreq ifr;
-	int fd;
-	int err;
-
-	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
-	fd = socket(preferred_family, SOCK_DGRAM, 0);
-	err = ioctl(fd, SIOCGIFHWADDR, &ifr);
-	if (err) {
-		perror("ioctl");
-		return -1;
-	}
-	close(fd);
-	return ifr.ifr_addr.sa_family;
-}
-
-
-char * tnl_ioctl_get_ifname(int idx)
-{
-	static struct ifreq ifr;
-	int fd;
-	int err;
-
-	ifr.ifr_ifindex = idx;
-	fd = socket(preferred_family, SOCK_DGRAM, 0);
-	err = ioctl(fd, SIOCGIFNAME, &ifr);
-	if (err) {
-		perror("ioctl");
-		return NULL;
-	}
-	close(fd);
-	return ifr.ifr_name;
-}
-
 int tnl_get_ioctl(const char *basedev, void *p)
 {
 	struct ifreq ifr;
@@ -126,7 +74,9 @@
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	err = ioctl(fd, SIOCGETTUNNEL, &ifr);
 	if (err)
-		perror("ioctl");
+		fprintf(stderr, "get tunnel %s failed: %s\n", basedev, 
+			strerror(errno));
+
 	close(fd);
 	return err;
 }
@@ -145,7 +95,8 @@
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	err = ioctl(fd, cmd, &ifr);
 	if (err)
-		perror("ioctl");
+		fprintf(stderr, "add tunnel %s failed: %s\n", ifr.ifr_name,
+			strerror(errno));
 	close(fd);
 	return err;
 }
@@ -160,16 +111,19 @@
 		strncpy(ifr.ifr_name, name, IFNAMSIZ);
 	else
 		strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+
 	ifr.ifr_ifru.ifru_data = p;
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	err = ioctl(fd, SIOCDELTUNNEL, &ifr);
 	if (err)
-		perror("ioctl");
+		fprintf(stderr, "delete tunnel %s failed: %s\n",
+			ifr.ifr_name, strerror(errno));
 	close(fd);
 	return err;
 }
 
-static int tnl_gen_ioctl(int cmd, const char *name, void *p, int skiperr)
+static int tnl_gen_ioctl(int cmd, const char *name, 
+			 void *p, int skiperr)
 {
 	struct ifreq ifr;
 	int fd;
@@ -180,7 +134,8 @@
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	err = ioctl(fd, cmd, &ifr);
 	if (err && errno != skiperr)
-		perror("ioctl");
+		fprintf(stderr, "%s: ioctl %x failed: %s\n", name,
+			cmd, strerror(errno));
 	close(fd);
 	return err;
 }
diff --git a/ip/tunnel.h b/ip/tunnel.h
index ded226b..7e7fe13 100644
--- a/ip/tunnel.h
+++ b/ip/tunnel.h
@@ -25,9 +25,7 @@
 #include <linux/types.h>
 
 const char *tnl_strproto(__u8 proto);
-int tnl_ioctl_get_ifindex(const char *dev);
-int tnl_ioctl_get_iftype(const char *dev);
-char * tnl_ioctl_get_ifname(int idx);
+
 int tnl_get_ioctl(const char *basedev, void *p);
 int tnl_add_ioctl(int cmd, const char *basedev, const char *name, void *p);
 int tnl_del_ioctl(const char *basedev, const char *name, void *p);
diff --git a/ip/xfrm.h b/ip/xfrm.h
index d3ca5c5..784a201 100644
--- a/ip/xfrm.h
+++ b/ip/xfrm.h
@@ -154,5 +154,6 @@
 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);
-
+int xfrm_sctx_parse(char *ctxstr, char *context,
+		    struct xfrm_user_sec_ctx *sctx);
 #endif
diff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c
index 1bbc7f8..0c8fcb0 100644
--- a/ip/xfrm_monitor.c
+++ b/ip/xfrm_monitor.c
@@ -38,7 +38,7 @@
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: ip xfrm monitor [ all | LISTofOBJECTS ]\n");
+	fprintf(stderr, "Usage: ip xfrm monitor [ all | LISTofXFRM-OBJECTS ]\n");
 	exit(-1);
 }
 
@@ -222,6 +222,20 @@
 
 }
 
+static void xfrm_usersa_print(const struct xfrm_usersa_id *sa_id, __u32 reqid, FILE *fp)
+{
+	char buf[256];
+
+	buf[0] = 0;
+	fprintf(fp, "dst %s ", rt_addr_n2a(sa_id->family,
+		sizeof(sa_id->daddr), &sa_id->daddr, buf, sizeof(buf)));
+
+	fprintf(fp, " reqid 0x%x", reqid);
+
+	fprintf(fp, " protocol %s ", strxf_proto(sa_id->proto));
+	fprintf(fp, " SPI 0x%x", ntohl(sa_id->spi));
+}
+
 static int xfrm_ae_print(const struct sockaddr_nl *who,
 			     struct nlmsghdr *n, void *arg)
 {
@@ -236,13 +250,8 @@
 	fprintf(fp, "src %s ", rt_addr_n2a(id->sa_id.family,
 		sizeof(id->saddr), &id->saddr,
 		abuf, sizeof(abuf)));
-	memset(abuf, '\0', sizeof(abuf));
-	fprintf(fp, "dst %s ", rt_addr_n2a(id->sa_id.family,
-		sizeof(id->sa_id.daddr), &id->sa_id.daddr,
-		abuf, sizeof(abuf)));
-	fprintf(fp, " reqid 0x%x", id->reqid);
-	fprintf(fp, " protocol %s ", strxf_proto(id->sa_id.proto));
-	fprintf(fp, " SPI 0x%x", ntohl(id->sa_id.spi));
+
+	xfrm_usersa_print(&id->sa_id, id->reqid, fp);
 
 	fprintf(fp, "\n");
 	fflush(fp);
@@ -250,6 +259,36 @@
 	return 0;
 }
 
+static void xfrm_print_addr(FILE *fp, int family, xfrm_address_t *a, size_t s)
+{
+	char buf[256];
+
+	buf[0] = 0;
+	fprintf(fp, "%s", rt_addr_n2a(family, s, a, buf, sizeof(buf)));
+}
+
+static int xfrm_mapping_print(const struct sockaddr_nl *who,
+			     struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct xfrm_user_mapping *map = NLMSG_DATA(n);
+
+	fprintf(fp, "Mapping change ");
+	xfrm_print_addr(fp, map->id.family, &map->old_saddr,
+			sizeof(map->old_saddr));
+
+	fprintf(fp, ":%d -> ", ntohs(map->old_sport));
+	xfrm_print_addr(fp, map->id.family, &map->new_saddr,
+			sizeof(map->new_saddr));
+	fprintf(fp, ":%d\n\t", ntohs(map->new_sport));
+
+	xfrm_usersa_print(&map->id, map->reqid, fp);
+
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
 static int xfrm_accept_msg(const struct sockaddr_nl *who,
 			   struct nlmsghdr *n, void *arg)
 {
@@ -286,6 +325,9 @@
 	case XFRM_MSG_NEWAE:
 		xfrm_ae_print(who, n, arg);
 		return 0;
+	case XFRM_MSG_MAPPING:
+		xfrm_mapping_print(who, n, arg);
+		return 0;
 	default:
 		break;
 	}
diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c
index dba1dbd..437f61e 100644
--- a/ip/xfrm_policy.c
+++ b/ip/xfrm_policy.c
@@ -33,6 +33,7 @@
 #include <linux/xfrm.h>
 #include <linux/in.h>
 #include <linux/in6.h>
+
 #include "utils.h"
 #include "xfrm.h"
 #include "ip_common.h"
@@ -50,54 +51,56 @@
 #define NLMSG_BUF_SIZE 4096
 #define RTA_BUF_SIZE 2048
 #define XFRM_TMPLS_BUF_SIZE 1024
+#define CTX_BUF_SIZE 256
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: ip xfrm policy { add | update } dir DIR SELECTOR [ index INDEX ] [ ptype PTYPE ]\n");
-	fprintf(stderr, "        [ action ACTION ] [ priority PRIORITY ] [ flag FLAG-LIST ] [ LIMIT-LIST ] [ TMPL-LIST ] [mark MARK [mask MASK]]\n");
-	fprintf(stderr, "Usage: ip xfrm policy { delete | get } dir DIR [ SELECTOR | index INDEX ] [ ptype PTYPE ] [mark MARK [mask MASK]]\n");
-	fprintf(stderr, "Usage: ip xfrm policy { deleteall | list } [ dir DIR ] [ SELECTOR ]\n");
-	fprintf(stderr, "        [ index INDEX ] [ action ACTION ] [ priority PRIORITY ]  [ flag FLAG-LIST ]\n");
+	fprintf(stderr, "Usage: ip xfrm policy { add | update } SELECTOR dir DIR [ ctx CTX ]\n");
+	fprintf(stderr, "        [ mark MARK [ mask MASK ] ] [ index INDEX ] [ ptype PTYPE ]\n");
+	fprintf(stderr, "        [ action ACTION ] [ priority PRIORITY ] [ flag FLAG-LIST ]\n");
+	fprintf(stderr, "        [ LIMIT-LIST ] [ TMPL-LIST ]\n");
+	fprintf(stderr, "Usage: ip xfrm policy { delete | get } { SELECTOR | index INDEX } dir DIR\n");
+	fprintf(stderr, "        [ ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]\n");
+	fprintf(stderr, "Usage: ip xfrm policy { deleteall | list } [ SELECTOR ] [ dir DIR ]\n");
+	fprintf(stderr, "        [ index INDEX ] [ ptype PTYPE ] [ action ACTION ] [ priority PRIORITY ]\n");
+	fprintf(stderr, "        [ flag FLAG-LIST ]\n");
 	fprintf(stderr, "Usage: ip xfrm policy flush [ ptype PTYPE ]\n");
 	fprintf(stderr, "Usage: ip xfrm count\n");
-	fprintf(stderr, "PTYPE := [ main | sub ](default=main)\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, "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n");
+	fprintf(stderr, "UPSPEC := proto { { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_TCP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_UDP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_SCTP));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_DCCP));
+	fprintf(stderr, " } [ sport PORT ] [ dport PORT ] |\n");
+	fprintf(stderr, "                  { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMPV6));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_MH));
+	fprintf(stderr, " } [ type NUMBER ] [ code NUMBER ] |\n");
+	fprintf(stderr, "                  %s", strxf_proto(IPPROTO_GRE));
+	fprintf(stderr, " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n");
+	fprintf(stderr, "DIR := in | out | fwd\n");
+	fprintf(stderr, "PTYPE := main | sub\n");
+	fprintf(stderr, "ACTION := allow | block\n");
 	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
-	fprintf(stderr, "FLAG := [ localok ]\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, "FLAG := localok | icmp\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");
+	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 := [ ");
+	fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\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, "%s | ", strxf_xfrmproto(IPPROTO_ROUTING));
-	fprintf(stderr, "%s ", strxf_xfrmproto(IPPROTO_DSTOPTS));
-	fprintf(stderr, "]\n");
-
- 	fprintf(stderr, "MODE := [ transport | tunnel | beet ](default=transport)\n");
- 	//fprintf(stderr, "REQID - number(default=0)\n");
-	fprintf(stderr, "LEVEL := [ required | use ](default=required)\n");
+	fprintf(stderr, "%s\n", strxf_xfrmproto(IPPROTO_DSTOPTS));
+ 	fprintf(stderr, "MODE := transport | tunnel | ro | in_trigger | beet\n");
+	fprintf(stderr, "LEVEL := required | use\n");
 
 	exit(-1);
 }
@@ -156,6 +159,8 @@
 		while (1) {
 			if (strcmp(*argv, "localok") == 0)
 				*flags |= XFRM_POLICY_LOCALOK;
+			else if (strcmp(*argv, "icmp") == 0)
+				*flags |= XFRM_POLICY_ICMP;
 			else {
 				PREV_ARG(); /* back track */
 				break;
@@ -223,6 +228,23 @@
 	return 0;
 }
 
+int xfrm_sctx_parse(char *ctxstr, char *s,
+			   struct xfrm_user_sec_ctx *sctx)
+{
+	int slen;
+
+	slen = strlen(s) + 1;
+
+	sctx->exttype = XFRMA_SEC_CTX;
+	sctx->ctx_doi = 1;
+	sctx->ctx_alg = 1;
+	sctx->ctx_len = slen;
+	sctx->len = sizeof(struct xfrm_user_sec_ctx) + slen;
+	memcpy(ctxstr, s, slen);
+
+	return 0;
+}
+
 static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct rtnl_handle rth;
@@ -234,14 +256,20 @@
 	char *dirp = NULL;
 	char *selp = NULL;
 	char *ptypep = NULL;
+	char *sctxp = NULL;
 	struct xfrm_userpolicy_type upt;
 	char tmpls_buf[XFRM_TMPLS_BUF_SIZE];
 	int tmpls_len = 0;
 	struct xfrm_mark mark = {0, 0};
+	struct {
+		struct xfrm_user_sec_ctx sctx;
+		char	str[CTX_BUF_SIZE];
+	} ctx;
 
 	memset(&req, 0, sizeof(req));
 	memset(&upt, 0, sizeof(upt));
 	memset(&tmpls_buf, 0, sizeof(tmpls_buf));
+	memset(&ctx, 0, sizeof(ctx));
 
 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo));
 	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
@@ -261,6 +289,15 @@
 
 			NEXT_ARG();
 			xfrm_policy_dir_parse(&req.xpinfo.dir, &argc, &argv);
+		} else if (strcmp(*argv, "ctx") == 0) {
+			char *context;
+
+			if (sctxp)
+				duparg("ctx", *argv);
+			sctxp = *argv;
+			NEXT_ARG();
+			context = *argv;
+			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
 		} else if (strcmp(*argv, "mark") == 0) {
 			xfrm_parse_mark(&mark, &argc, &argv);
 		} else if (strcmp(*argv, "index") == 0) {
@@ -348,6 +385,10 @@
 		}
 	}
 
+	if (sctxp) {
+		addattr_l(&req.n, sizeof(req), XFRMA_SEC_CTX,
+			  (void *)&ctx, ctx.sctx.len);
+	}
 
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
@@ -355,7 +396,7 @@
 	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)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
@@ -529,11 +570,18 @@
 	char *selp = NULL;
 	char *indexp = NULL;
 	char *ptypep = NULL;
+	char *sctxp = NULL;
 	struct xfrm_userpolicy_type upt;
 	struct xfrm_mark mark = {0, 0};
+	struct {
+		struct xfrm_user_sec_ctx sctx;
+		char    str[CTX_BUF_SIZE];
+	} ctx;
+
 
 	memset(&req, 0, sizeof(req));
 	memset(&upt, 0, sizeof(upt));
+	memset(&ctx, 0, sizeof(ctx));
 
 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid));
 	req.n.nlmsg_flags = NLM_F_REQUEST;
@@ -548,6 +596,15 @@
 			NEXT_ARG();
 			xfrm_policy_dir_parse(&req.xpid.dir, &argc, &argv);
 
+		} else if (strcmp(*argv, "ctx") == 0) {
+			char *context;
+
+			if (sctxp)
+				duparg("ctx", *argv);
+			sctxp = *argv;
+			NEXT_ARG();
+			context = *argv;
+			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
 		} else if (strcmp(*argv, "mark") == 0) {
 			xfrm_parse_mark(&mark, &argc, &argv);
 		} else if (strcmp(*argv, "index") == 0) {
@@ -611,7 +668,12 @@
 		}
 	}
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, res_nlbuf, NULL, NULL) < 0)
+	if (sctxp) {
+		addattr_l(&req.n, sizeof(req), XFRMA_SEC_CTX,
+			  (void *)&ctx, ctx.sctx.len);
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, res_nlbuf) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
@@ -800,7 +862,7 @@
 				exit(1);
 			}
 
-			if (rtnl_dump_filter(&rth, xfrm_policy_keep, &xb, NULL, NULL) < 0) {
+			if (rtnl_dump_filter(&rth, xfrm_policy_keep, &xb) < 0) {
 				fprintf(stderr, "Delete-all terminated\n");
 				exit(1);
 			}
@@ -826,7 +888,7 @@
 			exit(1);
 		}
 
-		if (rtnl_dump_filter(&rth, xfrm_policy_print, stdout, NULL, NULL) < 0) {
+		if (rtnl_dump_filter(&rth, xfrm_policy_print, stdout) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
@@ -916,7 +978,7 @@
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
 		exit(2);
 
 	print_spdinfo(&req.n, (void*)stdout);
@@ -968,7 +1030,7 @@
 	if (show_stats > 1)
 		fprintf(stderr, "Flush policy\n");
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index 49f35ed..57f0333 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -33,6 +33,7 @@
 #include <linux/xfrm.h>
 #include <linux/in.h>
 #include <linux/in6.h>
+
 #include "utils.h"
 #include "xfrm.h"
 #include "ip_common.h"
@@ -53,67 +54,63 @@
 #define NLMSG_BUF_SIZE 4096
 #define RTA_BUF_SIZE 2048
 #define XFRM_ALGO_KEY_BUF_SIZE 512
+#define CTX_BUF_SIZE 256
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ XFRM_OPT ] [ mode MODE ]\n");
-	fprintf(stderr, "        [ reqid REQID ] [ seq SEQ ] [ replay-window SIZE ] [ flag FLAG-LIST ]\n");
-	fprintf(stderr, "        [ encap ENCAP ] [ sel SELECTOR ] [ replay-seq SEQ ]\n");
-	fprintf(stderr, "        [ replay-oseq SEQ ] [ LIMIT-LIST ]\n");
-	fprintf(stderr, "Usage: ip xfrm state allocspi ID [ mode MODE ] [ reqid REQID ] [ seq SEQ ]\n");
-	fprintf(stderr, "        [ min SPI max SPI ]\n");
-	fprintf(stderr, "Usage: ip xfrm state { delete | get } ID\n");
+	fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n");
+	fprintf(stderr, "        [ mark MARK [ mask MASK ] ] [ reqid REQID ] [ seq SEQ ]\n");
+	fprintf(stderr, "        [ replay-window SIZE ] [ replay-seq SEQ ] [ replay-oseq SEQ ]\n");
+	fprintf(stderr, "        [ flag FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ] [ encap ENCAP ]\n");
+	fprintf(stderr, "        [ coa ADDR[/PLEN] ] [ ctx CTX ]\n");
+	fprintf(stderr, "Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n");
+	fprintf(stderr, "        [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n");
+	fprintf(stderr, "Usage: ip xfrm state { delete | get } ID [ mark MARK [ mask MASK ] ]\n");
 	fprintf(stderr, "Usage: ip xfrm state { deleteall | list } [ ID ] [ mode MODE ] [ reqid REQID ]\n");
 	fprintf(stderr, "        [ flag FLAG-LIST ]\n");
-	fprintf(stderr, "Usage: ip xfrm state flush [ proto XFRM_PROTO ]\n");
-	fprintf(stderr, "Usage: ip xfrm state count \n");
-
-	fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ] [mark MARK [mask MASK]]\n");
-	//fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n");
-	fprintf(stderr, "XFRM_PROTO := [ ");
+	fprintf(stderr, "Usage: ip xfrm state flush [ proto XFRM-PROTO ]\n");
+	fprintf(stderr, "Usage: ip xfrm state count\n");
+	fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\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, "%s | ", strxf_xfrmproto(IPPROTO_ROUTING));
-	fprintf(stderr, "%s ", strxf_xfrmproto(IPPROTO_DSTOPTS));
-	fprintf(stderr, "]\n");
-
-	//fprintf(stderr, "SPI - security parameter index(default=0)\n");
-
- 	fprintf(stderr, "MODE := [ transport | tunnel | ro | beet ](default=transport)\n");
- 	//fprintf(stderr, "REQID - number(default=0)\n");
-
-	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
-	fprintf(stderr, "FLAG := [ noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec ]\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 "
-			"[ ALGO_ICV_LEN ]\n");
-	fprintf(stderr, "ALGO_TYPE := [ ");
-	fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_AEAD));
+	fprintf(stderr, "%s\n", strxf_xfrmproto(IPPROTO_DSTOPTS));
+	fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] ALGO\n");
+	fprintf(stderr, "ALGO := { ");
 	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, "%s", strxf_algotype(XFRMA_ALG_COMP));
+	fprintf(stderr, " } ALGO-NAME ALGO-KEY |\n");
+	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AEAD));
+	fprintf(stderr, " ALGO-NAME ALGO-KEY ALGO-ICV-LEN |\n");
+	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC));
+	fprintf(stderr, " ALGO-NAME ALGO-KEY ALGO-TRUNC-LEN\n");
+ 	fprintf(stderr, "MODE := transport | tunnel | ro | in_trigger | beet\n");
+	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
+	fprintf(stderr, "FLAG := noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4\n");
+	fprintf(stderr, "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n");
+	fprintf(stderr, "UPSPEC := proto { { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_TCP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_UDP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_SCTP));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_DCCP));
+	fprintf(stderr, " } [ sport PORT ] [ dport PORT ] |\n");
+	fprintf(stderr, "                  { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMPV6));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_MH));
+	fprintf(stderr, " } [ type NUMBER ] [ code NUMBER ] |\n");
+	fprintf(stderr, "                  %s", strxf_proto(IPPROTO_GRE));
+	fprintf(stderr, " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\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");
+        fprintf(stderr, "ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\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);
 }
 
@@ -125,7 +122,7 @@
 
 #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");
+	fprintf(stderr, "warning: ALGO-NAME/ALGO-KEY will send to kernel promiscuously! (verifying them isn't implemented yet)\n");
 #endif
 
 	strncpy(alg->alg_name, name, sizeof(alg->alg_name));
@@ -145,7 +142,7 @@
 		/* calculate length of the converted values(real key) */
 		len = (plen + 1) / 2;
 		if (len > max)
-			invarg("\"ALGOKEY\" makes buffer overflow\n", key);
+			invarg("\"ALGO-KEY\" makes buffer overflow\n", key);
 
 		for (i = - (plen % 2), j = 0; j < len; i += 2, j++) {
 			char vbuf[3];
@@ -156,7 +153,7 @@
 			vbuf[2] = '\0';
 
 			if (get_u8(&val, vbuf, 16))
-				invarg("\"ALGOKEY\" is invalid", key);
+				invarg("\"ALGO-KEY\" is invalid", key);
 
 			buf[j] = val;
 		}
@@ -164,7 +161,7 @@
 		len = slen;
 		if (len > 0) {
 			if (len > max)
-				invarg("\"ALGOKEY\" makes buffer overflow\n", key);
+				invarg("\"ALGO-KEY\" makes buffer overflow\n", key);
 
 			strncpy(buf, key, len);
 		}
@@ -217,6 +214,8 @@
 				*flags |= XFRM_STATE_ICMP;
 			else if (strcmp(*argv, "af-unspec") == 0)
 				*flags |= XFRM_STATE_AF_UNSPEC;
+			else if (strcmp(*argv, "align4") == 0)
+				*flags |= XFRM_STATE_ALIGN4;
 			else {
 				PREV_ARG(); /* back track */
 				break;
@@ -249,10 +248,16 @@
 	char *aalgop = NULL;
 	char *calgop = NULL;
 	char *coap = NULL;
+	char *sctxp = NULL;
 	struct xfrm_mark mark = {0, 0};
+	struct {
+		struct xfrm_user_sec_ctx sctx;
+		char    str[CTX_BUF_SIZE];
+	} ctx;
 
 	memset(&req, 0, sizeof(req));
 	memset(&replay, 0, sizeof(replay));
+	memset(&ctx, 0, sizeof(ctx));
 
 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
 	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
@@ -336,6 +341,19 @@
 
 			addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR,
 				  (void *)&xcoa, sizeof(xcoa));
+		} else if (strcmp(*argv, "ctx") == 0) {
+			char *context;
+
+			if (sctxp)
+				duparg("ctx", *argv);
+			sctxp = *argv;
+
+			NEXT_ARG();
+			context = *argv;
+
+			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
+			addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX,
+				  (void *)&ctx, ctx.sctx.len);
 		} else {
 			/* try to assume ALGO */
 			int type = xfrm_algotype_getbyname(*argv);
@@ -343,6 +361,7 @@
 			case XFRMA_ALG_AEAD:
 			case XFRMA_ALG_CRYPT:
 			case XFRMA_ALG_AUTH:
+			case XFRMA_ALG_AUTH_TRUNC:
 			case XFRMA_ALG_COMP:
 			{
 				/* ALGO */
@@ -350,11 +369,12 @@
 					union {
 						struct xfrm_algo alg;
 						struct xfrm_algo_aead aead;
+						struct xfrm_algo_auth auth;
 					} u;
 					char buf[XFRM_ALGO_KEY_BUF_SIZE];
 				} alg = {};
 				int len;
-				__u32 icvlen;
+				__u32 icvlen, trunclen;
 				char *name;
 				char *key;
 				char *buf;
@@ -362,57 +382,70 @@
 				switch (type) {
 				case XFRMA_ALG_AEAD:
 					if (aeadop)
-						duparg("ALGOTYPE", *argv);
+						duparg("ALGO-TYPE", *argv);
 					aeadop = *argv;
 					break;
 				case XFRMA_ALG_CRYPT:
 					if (ealgop)
-						duparg("ALGOTYPE", *argv);
+						duparg("ALGO-TYPE", *argv);
 					ealgop = *argv;
 					break;
 				case XFRMA_ALG_AUTH:
+				case XFRMA_ALG_AUTH_TRUNC:
 					if (aalgop)
-						duparg("ALGOTYPE", *argv);
+						duparg("ALGO-TYPE", *argv);
 					aalgop = *argv;
 					break;
 				case XFRMA_ALG_COMP:
 					if (calgop)
-						duparg("ALGOTYPE", *argv);
+						duparg("ALGO-TYPE", *argv);
 					calgop = *argv;
 					break;
 				default:
 					/* not reached */
-					invarg("\"ALGOTYPE\" is invalid\n", *argv);
+					invarg("\"ALGO-TYPE\" is invalid\n", *argv);
 				}
 
 				if (!NEXT_ARG_OK())
-					missarg("ALGONAME");
+					missarg("ALGO-NAME");
 				NEXT_ARG();
 				name = *argv;
 
 				if (!NEXT_ARG_OK())
-					missarg("ALGOKEY");
+					missarg("ALGO-KEY");
 				NEXT_ARG();
 				key = *argv;
 
 				buf = alg.u.alg.alg_key;
 				len = sizeof(alg.u.alg);
 
-				if (type != XFRMA_ALG_AEAD)
-					goto parse_algo;
+				switch (type) {
+				case XFRMA_ALG_AEAD:
+					if (!NEXT_ARG_OK())
+						missarg("ALGO-ICV-LEN");
+					NEXT_ARG();
+					if (get_u32(&icvlen, *argv, 0))
+						invarg("\"aead\" ICV length is invalid",
+						       *argv);
+					alg.u.aead.alg_icv_len = icvlen;
 
-				if (!NEXT_ARG_OK())
-					missarg("ALGOICVLEN");
-				NEXT_ARG();
-				if (get_u32(&icvlen, *argv, 0))
-					invarg("\"aead\" ICV length is invalid",
-					       *argv);
-				alg.u.aead.alg_icv_len = icvlen;
+					buf = alg.u.aead.alg_key;
+					len = sizeof(alg.u.aead);
+					break;
+				case XFRMA_ALG_AUTH_TRUNC:
+					if (!NEXT_ARG_OK())
+						missarg("ALGO-TRUNC-LEN");
+					NEXT_ARG();
+					if (get_u32(&trunclen, *argv, 0))
+						invarg("\"auth\" trunc length is invalid",
+						       *argv);
+					alg.u.auth.alg_trunc_len = trunclen;
 
-				buf = alg.u.aead.alg_key;
-				len = sizeof(alg.u.aead);
+					buf = alg.u.auth.alg_key;
+					len = sizeof(alg.u.auth);
+					break;
+				}
 
-parse_algo:
 				xfrm_algo_parse((void *)&alg, type, name, key,
 						buf, sizeof(alg.buf));
 				len += alg.u.alg.alg_key_len;
@@ -515,7 +548,7 @@
 	if (req.xsinfo.family == AF_UNSPEC)
 		req.xsinfo.family = AF_INET;
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
@@ -614,7 +647,7 @@
 			exit(1);
 		}
 		if (req.xspi.min > req.xspi.max) {
-			fprintf(stderr, "\"min\" valie is larger than \"max\" one\n");
+			fprintf(stderr, "\"min\" value is larger than \"max\" value\n");
 			exit(1);
 		}
 	} else {
@@ -652,7 +685,7 @@
 		req.xspi.info.family = AF_INET;
 
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
 		exit(2);
 
 	if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
@@ -842,7 +875,7 @@
 		req.xsid.family = AF_INET;
 
 	if (delete) {
-		if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+		if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 			exit(2);
 	} else {
 		char buf[NLMSG_BUF_SIZE];
@@ -850,7 +883,7 @@
 
 		memset(buf, 0, sizeof(buf));
 
-		if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0)
+		if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
 			exit(2);
 
 		if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
@@ -986,7 +1019,7 @@
 				exit(1);
 			}
 
-			if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb, NULL, NULL) < 0) {
+			if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb) < 0) {
 				fprintf(stderr, "Delete-all terminated\n");
 				exit(1);
 			}
@@ -1013,7 +1046,7 @@
 			exit(1);
 		}
 
-		if (rtnl_dump_filter(&rth, xfrm_state_print, stdout, NULL, NULL) < 0) {
+		if (rtnl_dump_filter(&rth, xfrm_state_print, stdout) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
@@ -1091,7 +1124,7 @@
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
 		exit(2);
 
 	print_sadinfo(&req.n, (void*)stdout);
@@ -1129,7 +1162,7 @@
 
 			ret = xfrm_xfrmproto_getbyname(*argv);
 			if (ret < 0)
-				invarg("\"XFRM_PROTO\" is invalid", *argv);
+				invarg("\"XFRM-PROTO\" is invalid", *argv);
 
 			req.xsf.proto = (__u8)ret;
 		} else
@@ -1145,7 +1178,7 @@
 		fprintf(stderr, "Flush state proto=%s\n",
 			strxf_xfrmproto(req.xsf.proto));
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index cfeb894..878911e 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -94,25 +94,32 @@
 	struct {
 		struct nlmsghdr nlh;
 		struct rtgenmsg g;
+		__u16 align_rta;	/* attribute has to be 32bit aligned */
+		struct rtattr ext_req;
+		__u32 ext_filter_mask;
 	} req;
 
 	memset(&req, 0, sizeof(req));
 	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_flags = NLM_F_DUMP|NLM_F_REQUEST;
 	req.nlh.nlmsg_pid = 0;
 	req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
 	req.g.rtgen_family = family;
 
+	req.ext_req.rta_type = IFLA_EXT_MASK;
+	req.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));
+	req.ext_filter_mask = RTEXT_FILTER_VF;
+
 	return send(rth->fd, (void*)&req, sizeof(req), 0);
 }
 
-int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+int rtnl_send(struct rtnl_handle *rth, const void *buf, int len)
 {
 	return send(rth->fd, buf, len, 0);
 }
 
-int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len)
+int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int len)
 {
 	struct nlmsghdr *h;
 	int status;
@@ -148,7 +155,7 @@
 int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
 {
 	struct nlmsghdr nlh;
-	struct sockaddr_nl nladdr;
+	struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
 	struct iovec iov[2] = {
 		{ .iov_base = &nlh, .iov_len = sizeof(nlh) },
 		{ .iov_base = req, .iov_len = len }
@@ -160,12 +167,9 @@
 		.msg_iovlen = 2,
 	};
 
-	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_flags = NLM_F_DUMP|NLM_F_REQUEST;
 	nlh.nlmsg_pid = 0;
 	nlh.nlmsg_seq = rth->dump = ++rth->seq;
 
@@ -189,6 +193,8 @@
 	while (1) {
 		int status;
 		const struct rtnl_dump_filter_arg *a;
+		int found_done = 0;
+		int msglen = 0;
 
 		iov.iov_len = sizeof(buf);
 		status = recvmsg(rth->fd, &msg, 0);
@@ -208,24 +214,20 @@
 
 		for (a = arg; a->filter; a++) {
 			struct nlmsghdr *h = (struct nlmsghdr*)buf;
+			msglen = status;
 
-			while (NLMSG_OK(h, status)) {
+			while (NLMSG_OK(h, msglen)) {
 				int err;
 
 				if (nladdr.nl_pid != 0 ||
 				    h->nlmsg_pid != rth->local.nl_pid ||
-				    h->nlmsg_seq != rth->dump) {
-					if (a->junk) {
-						err = a->junk(&nladdr, h,
-							      a->arg2);
-						if (err < 0)
-							return err;
-					}
+				    h->nlmsg_seq != rth->dump)
 					goto skip_it;
-				}
 
-				if (h->nlmsg_type == NLMSG_DONE)
-					return 0;
+				if (h->nlmsg_type == NLMSG_DONE) {
+					found_done = 1;
+					break; /* process next filter */
+				}
 				if (h->nlmsg_type == NLMSG_ERROR) {
 					struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
 					if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
@@ -242,15 +244,19 @@
 					return err;
 
 skip_it:
-				h = NLMSG_NEXT(h, status);
+				h = NLMSG_NEXT(h, msglen);
 			}
-		} while (0);
+		}
+
+		if (found_done)
+			return 0;
+
 		if (msg.msg_flags & MSG_TRUNC) {
 			fprintf(stderr, "Message truncated\n");
 			continue;
 		}
-		if (status) {
-			fprintf(stderr, "!!!Remnant of size %d\n", status);
+		if (msglen) {
+			fprintf(stderr, "!!!Remnant of size %d\n", msglen);
 			exit(1);
 		}
 	}
@@ -258,22 +264,18 @@
 
 int rtnl_dump_filter(struct rtnl_handle *rth,
 		     rtnl_filter_t filter,
-		     void *arg1,
-		     rtnl_filter_t junk,
-		     void *arg2)
+		     void *arg1)
 {
 	const struct rtnl_dump_filter_arg a[2] = {
-		{ .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
-		{ .filter = NULL,   .arg1 = NULL, .junk = NULL, .arg2 = NULL }
+		{ .filter = filter, .arg1 = arg1, },
+		{ .filter = NULL,   .arg1 = NULL, },
 	};
 
 	return rtnl_dump_filter_l(rth, a);
 }
 
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
-	      unsigned groups, struct nlmsghdr *answer,
-	      rtnl_filter_t junk,
-	      void *jarg)
+	      unsigned groups, struct nlmsghdr *answer)
 {
 	int status;
 	unsigned seq;
@@ -332,11 +334,10 @@
 			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 (l < 0 || len>status) {
 				if (msg.msg_flags & MSG_TRUNC) {
 					fprintf(stderr, "Truncated message\n");
 					return -1;
@@ -348,11 +349,6 @@
 			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;
-				}
 				/* Don't forget to skip that message. */
 				status -= NLMSG_ALIGN(len);
 				h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
@@ -484,7 +480,7 @@
 	nladdr.nl_groups = 0;
 
 	while (1) {
-		int err, len, type;
+		int err, len;
 		int l;
 
 		status = fread(&buf, 1, sizeof(*h), rtnl);
@@ -499,7 +495,6 @@
 			return 0;
 
 		len = h->nlmsg_len;
-		type= h->nlmsg_type;
 		l = len - sizeof(*h);
 
 		if (l<0 || len>sizeof(buf)) {
@@ -525,20 +520,34 @@
 	}
 }
 
+int addattr(struct nlmsghdr *n, int maxlen, int type)
+{
+	return addattr_l(n, maxlen, type, NULL, 0);
+}
+
+int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data)
+{
+	return addattr_l(n, maxlen, type, &data, sizeof(__u8));
+}
+
+int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data)
+{
+	return addattr_l(n, maxlen, type, &data, sizeof(__u16));
+}
+
 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;
+	return addattr_l(n, maxlen, type, &data, sizeof(__u32));
+}
+
+int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data)
+{
+	return addattr_l(n, maxlen, type, &data, sizeof(__u64));
+}
+
+int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *str)
+{
+	return addattr_l(n, maxlen, type, str, strlen(str)+1);
 }
 
 int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
diff --git a/lib/ll_map.c b/lib/ll_map.c
index b8b49aa..1ca781e 100644
--- a/lib/ll_map.c
+++ b/lib/ll_map.c
@@ -18,31 +18,38 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <string.h>
+#include <linux/if.h>
 
 #include "libnetlink.h"
 #include "ll_map.h"
 
 extern unsigned int if_nametoindex (const char *);
 
-struct idxmap
+struct ll_cache
 {
-	struct idxmap * next;
-	unsigned	index;
-	int		type;
-	int		alen;
+	struct ll_cache   *idx_next;
 	unsigned	flags;
+	int		index;
+	unsigned short	type;
+	unsigned short	alen;
+	char		name[IFNAMSIZ];
 	unsigned char	addr[20];
-	char		name[16];
 };
 
-static struct idxmap *idxmap[16];
+#define IDXMAP_SIZE	1024
+static struct ll_cache *idx_head[IDXMAP_SIZE];
+
+static inline struct ll_cache *idxhead(int idx)
+{
+	return idx_head[idx & (IDXMAP_SIZE - 1)];
+}
 
 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 ll_cache *im, **imp;
 	struct rtattr *tb[IFLA_MAX+1];
 
 	if (n->nlmsg_type != RTM_NEWLINK)
@@ -51,15 +58,13 @@
 	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)
+	h = ifi->ifi_index & (IDXMAP_SIZE - 1);
+	for (imp = &idx_head[h]; (im=*imp)!=NULL; imp = &im->idx_next)
 		if (im->index == ifi->ifi_index)
 			break;
 
@@ -67,7 +72,7 @@
 		im = malloc(sizeof(*im));
 		if (im == NULL)
 			return 0;
-		im->next = *imp;
+		im->idx_next = *imp;
 		im->index = ifi->ifi_index;
 		*imp = im;
 	}
@@ -90,32 +95,34 @@
 
 const char *ll_idx_n2a(unsigned idx, char *buf)
 {
-	struct idxmap *im;
+	const struct ll_cache *im;
 
 	if (idx == 0)
 		return "*";
-	for (im = idxmap[idx&0xF]; im; im = im->next)
+
+	for (im = idxhead(idx); im; im = im->idx_next)
 		if (im->index == idx)
 			return im->name;
-	snprintf(buf, 16, "if%d", idx);
+
+	snprintf(buf, IFNAMSIZ, "if%d", idx);
 	return buf;
 }
 
 
 const char *ll_index_to_name(unsigned idx)
 {
-	static char nbuf[16];
+	static char nbuf[IFNAMSIZ];
 
 	return ll_idx_n2a(idx, nbuf);
 }
 
 int ll_index_to_type(unsigned idx)
 {
-	struct idxmap *im;
+	const struct ll_cache *im;
 
 	if (idx == 0)
 		return -1;
-	for (im = idxmap[idx&0xF]; im; im = im->next)
+	for (im = idxhead(idx); im; im = im->idx_next)
 		if (im->index == idx)
 			return im->type;
 	return -1;
@@ -123,12 +130,12 @@
 
 unsigned ll_index_to_flags(unsigned idx)
 {
-	struct idxmap *im;
+	const struct ll_cache *im;
 
 	if (idx == 0)
 		return 0;
 
-	for (im = idxmap[idx&0xF]; im; im = im->next)
+	for (im = idxhead(idx); im; im = im->idx_next)
 		if (im->index == idx)
 			return im->flags;
 	return 0;
@@ -137,12 +144,12 @@
 unsigned ll_index_to_addr(unsigned idx, unsigned char *addr,
 			  unsigned alen)
 {
-	struct idxmap *im;
+	const struct ll_cache *im;
 
 	if (idx == 0)
 		return 0;
 
-	for (im = idxmap[idx&0xF]; im; im = im->next) {
+	for (im = idxhead(idx); im; im = im->idx_next) {
 		if (im->index == idx) {
 			if (alen > sizeof(im->addr))
 				alen = sizeof(im->addr);
@@ -157,18 +164,20 @@
 
 unsigned ll_name_to_index(const char *name)
 {
-	static char ncache[16];
+	static char ncache[IFNAMSIZ];
 	static int icache;
-	struct idxmap *im;
+	struct ll_cache *im;
 	int i;
 	unsigned idx;
 
 	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) {
+
+	for (i=0; i<IDXMAP_SIZE; i++) {
+		for (im = idx_head[i]; im; im = im->idx_next) {
 			if (strcmp(im->name, name) == 0) {
 				icache = im->index;
 				strcpy(ncache, name);
@@ -185,14 +194,22 @@
 
 int ll_init_map(struct rtnl_handle *rth)
 {
+	static int initialized;
+
+	if (initialized)
+		return 0;
+
 	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) {
+	if (rtnl_dump_filter(rth, ll_remember_index, NULL) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
+
+	initialized = 1;
+
 	return 0;
 }
diff --git a/lib/ll_proto.c b/lib/ll_proto.c
index cdd1602..3337b14 100644
--- a/lib/ll_proto.c
+++ b/lib/ll_proto.c
@@ -30,25 +30,19 @@
 
 
 #define __PF(f,n) { ETH_P_##f, #n },
-static struct {
+static const 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)
@@ -62,19 +56,10 @@
 __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)
@@ -90,13 +75,12 @@
 __PF(MOBITEX,mobitex)
 __PF(CONTROL,control)
 __PF(IRDA,irda)
-#ifdef ETH_P_ECONET
 __PF(ECONET,econet)
-#endif
 __PF(TIPC,tipc)
 __PF(AOE,aoe)
 
 { 0x8100, "802.1Q" },
+{ 0x88cc, "LLDP" },
 { ETH_P_IP, "ipv4" },
 };
 #undef __PF
diff --git a/lib/rt_names.c b/lib/rt_names.c
index 89b1cc3..52ecdb2 100644
--- a/lib/rt_names.c
+++ b/lib/rt_names.c
@@ -23,6 +23,10 @@
 
 #include "rt_names.h"
 
+#ifndef CONFDIR
+#define CONFDIR "/etc/iproute2"
+#endif
+
 struct rtnl_hash_entry {
 	struct rtnl_hash_entry *next;
 	char *			name;
@@ -129,7 +133,7 @@
 static void rtnl_rtprot_initialize(void)
 {
 	rtnl_rtprot_init = 1;
-	rtnl_tab_initialize("/etc/iproute2/rt_protos",
+	rtnl_tab_initialize(CONFDIR "/rt_protos",
 			    rtnl_rtprot_tab, 256);
 }
 
@@ -196,7 +200,7 @@
 	rtnl_rtscope_tab[254] = "host";
 	rtnl_rtscope_tab[253] = "link";
 	rtnl_rtscope_tab[200] = "site";
-	rtnl_tab_initialize("/etc/iproute2/rt_scopes",
+	rtnl_tab_initialize(CONFDIR "/rt_scopes",
 			    rtnl_rtscope_tab, 256);
 }
 
@@ -259,7 +263,7 @@
 static void rtnl_rtrealm_initialize(void)
 {
 	rtnl_rtrealm_init = 1;
-	rtnl_tab_initialize("/etc/iproute2/rt_realms",
+	rtnl_tab_initialize(CONFDIR "/rt_realms",
 			    rtnl_rtrealm_tab, 256);
 }
 
@@ -328,7 +332,7 @@
 static void rtnl_rttable_initialize(void)
 {
 	rtnl_rttable_init = 1;
-	rtnl_hash_initialize("/etc/iproute2/rt_tables",
+	rtnl_hash_initialize(CONFDIR "/rt_tables",
 			     rtnl_rttable_hash, 256);
 }
 
@@ -396,7 +400,7 @@
 static void rtnl_rtdsfield_initialize(void)
 {
 	rtnl_rtdsfield_init = 1;
-	rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+	rtnl_tab_initialize(CONFDIR "/rt_dsfield",
 			    rtnl_rtdsfield_tab, 256);
 }
 
@@ -449,3 +453,53 @@
 	return 0;
 }
 
+
+static struct rtnl_hash_entry dflt_group_entry  = { .id = 0, .name = "default" };
+
+static struct rtnl_hash_entry * rtnl_group_hash[256] = {
+	[0] = &dflt_group_entry,
+};
+
+static int rtnl_group_init;
+
+static void rtnl_group_initialize(void)
+{
+	rtnl_group_init = 1;
+	rtnl_hash_initialize("/etc/iproute2/group",
+			     rtnl_group_hash, 256);
+}
+
+int rtnl_group_a2n(int *id, char *arg)
+{
+	static char *cache = NULL;
+	static unsigned long res;
+	struct rtnl_hash_entry *entry;
+	char *end;
+	int i;
+
+	if (cache && strcmp(cache, arg) == 0) {
+		*id = res;
+		return 0;
+	}
+
+	if (!rtnl_group_init)
+		rtnl_group_initialize();
+
+	for (i=0; i<256; i++) {
+		entry = rtnl_group_hash[i];
+		while (entry && strcmp(entry->name, arg))
+			entry = entry->next;
+		if (entry) {
+			cache = entry->name;
+			res = entry->id;
+			*id = res;
+			return 0;
+		}
+	}
+
+	i = strtol(arg, &end, 0);
+	if (!end || end == arg || *end || i < 0)
+		return -1;
+	*id = i;
+	return 0;
+}
diff --git a/lib/utils.c b/lib/utils.c
index 8971751..1ad7616 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -25,6 +25,7 @@
 #include <linux/pkt_sched.h>
 #include <time.h>
 #include <sys/time.h>
+#include <errno.h>
 
 
 #include "utils.h"
@@ -93,14 +94,13 @@
 }
 
 /*
- * get_jiffies is "translated" from a similar routine "get_time" in
- * tc_util.c.  we don't use the exact same routine because tc passes
- * microseconds to the kernel and the callers of get_jiffies want 
- * to pass jiffies, and have a different assumption for the units of
- * a "raw" number.
+ * get_time_rtt is "translated" from a similar routine "get_time" in
+ * tc_util.c.  We don't use the exact same routine because tc passes
+ * microseconds to the kernel and the callers of get_time_rtt want to
+ * pass milliseconds (standard unit for rtt values since 2.6.27), and
+ * have a different assumption for the units of a "raw" number.
  */
-
-int get_jiffies(unsigned *jiffies, const char *arg, int base, int *raw)
+int get_time_rtt(unsigned *val, const char *arg, int *raw)
 {
 	double t;
 	unsigned long res;
@@ -112,35 +112,22 @@
 			return -1;
 	}
 	else {
-		res = strtoul(arg,&p,base);
+		res = strtoul(arg, &p, 0);
 		if (res > UINT_MAX)
 			return -1;
 		t = (double)res;
 	}
 	if (p == arg)
 		return -1;
-
-	if (__iproute2_hz_internal == 0)
-                __iproute2_hz_internal = __get_hz();
-	
 	*raw = 1;
 
 	if (*p) {
 		*raw = 0;
                 if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 ||
                     strcasecmp(p, "secs")==0)
-                        t *= __iproute2_hz_internal;
+                        t *= 1000;
                 else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 ||
                          strcasecmp(p, "msecs") == 0)
-                        t *= __iproute2_hz_internal/1000.0;
-                else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 ||
-                         strcasecmp(p, "usecs") == 0)
-                        t *= __iproute2_hz_internal/1000000.0;
-                else if (strcasecmp(p, "ns") == 0 || strcasecmp(p, "nsec")==0 ||
-                         strcasecmp(p, "nsecs") == 0)
-                        t *= __iproute2_hz_internal/1000000000.0;
-		else if (strcasecmp(p, "j") == 0 || strcasecmp(p, "hz") == 0 ||
-			 strcasecmp(p,"jiffies") == 0)
 			t *= 1.0; /* allow suffix, do nothing */
                 else
                         return -1;
@@ -148,9 +135,9 @@
 
 	/* emulate ceil() without having to bring-in -lm and always be >= 1 */
 
-	*jiffies = t;
-	if (*jiffies < t)
-		*jiffies += 1;
+	*val = t;
+	if (*val < t)
+		*val += 1;
 	
         return 0;
 
@@ -212,6 +199,24 @@
 	return 0;
 }
 
+int get_s32(__s32 *val, const char *arg, int base)
+{
+	long res;
+	char *ptr;
+
+	errno = 0;
+
+	if (!arg || !*arg)
+		return -1;
+	res = strtol(arg, &ptr, base);
+	if (ptr == arg || *ptr ||
+	    ((res ==  LONG_MIN || res == LONG_MAX) && errno == ERANGE) ||
+	    res > INT32_MAX || res < INT32_MIN)
+		return -1;
+	*val = res;
+	return 0;
+}
+
 int get_s16(__s16 *val, const char *arg, int base)
 {
 	long res;
@@ -719,7 +724,7 @@
 	while ((cp = strstr(*linep, "\\\n")) != NULL) {
 		char *line1 = NULL;
 		size_t len1 = 0;
-		size_t cc1;
+		ssize_t cc1;
 
 		if ((cc1 = getline(&line1, &len1, in)) < 0) {
 			fprintf(stderr, "Missing continuation line\n");
diff --git a/man/Makefile b/man/Makefile
new file mode 100644
index 0000000..67fea05
--- /dev/null
+++ b/man/Makefile
@@ -0,0 +1,20 @@
+INSTALL=install
+INSTALLDIR=install -m 0755 -d
+INSTALLMAN=install -m 0644
+
+SUBDIRS = man3 man8
+
+all:
+	@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir; done
+
+distclean: clean
+
+clean:
+	@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir clean; done
+
+install:
+	@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir install; done
+
+.PHONY: install clean distclean
+
+.EXPORT_ALL_VARIABLES:
diff --git a/man/man3/Makefile b/man/man3/Makefile
new file mode 100644
index 0000000..bf55658
--- /dev/null
+++ b/man/man3/Makefile
@@ -0,0 +1,13 @@
+MAN3PAGES=libnetlink.3
+
+all:
+
+distclean: clean
+
+clean:
+
+install:
+	$(INSTALLDIR) $(DESTDIR)$(MANDIR)/man3
+	$(INSTALLMAN) $(MAN3PAGES) $(DESTDIR)$(MANDIR)/man3
+
+.PHONY: install clean distclean
diff --git a/man/man7/tc-hfsc.7 b/man/man7/tc-hfsc.7
new file mode 100644
index 0000000..eff8a0f
--- /dev/null
+++ b/man/man7/tc-hfsc.7
@@ -0,0 +1,561 @@
+.TH "TC\-HFSC" 7 "31 October 2011" iproute2 Linux
+.SH "NAME"
+tc-hfcs \- Hierarchical Fair Service Curve
+.
+.SH "HISTORY & INTRODUCTION"
+.
+HFSC \- \fBHierarchical Fair Service Curve\fR was first presented at
+SIGCOMM'97. Developed as a part of ALTQ (ALTernative Queuing) on NetBSD, found
+its way quickly to other BSD systems, and then a few years ago became part of
+the linux kernel. Still, it's not the most popular scheduling algorithm \-
+especially if compared to HTB \- and it's not well documented from enduser's
+perspective. This introduction aims to explain how HFSC works without
+going to deep into math side of things (although some if it will be
+inevitable).
+
+In short HFSC aims to:
+.
+.RS 4
+.IP \fB1)\fR 4
+guarantee precise bandwidth and delay allocation for all leaf classes (realtime
+criterion)
+.IP \fB2)\fR
+allocate excess bandwidth fairly as specified by class hierarchy (linkshare &
+upperlimit criterion)
+.IP \fB3)\fR
+minimize any discrepancy between the service curve and the actual amount of
+service provided during linksharing
+.RE
+.PP
+.
+The main "selling" point of HFSC is feature \fB(1)\fR, which is achieved by
+using nonlinear service curves (more about what it actually is later). This is
+particularly useful in VoIP or games, where not only guarantee of consistent
+bandwidth is important, but initial delay of a data stream as well. Note that
+it matters only for leaf classes (where the actual queues are) \- thus class
+hierarchy is ignored in realtime case.
+
+Feature \fB(2)\fR is well, obvious \- any algorithm featuring class hierarchy
+(such as HTB or CBQ) strives to achieve that. HFSC does that well, although
+you might end with unusual situations, if you define service curves carelessly
+\- see section CORNER CASES for examples.
+
+Feature \fB(3)\fR is mentioned due to the nature of the problem. There may be
+situations where it's either not possible to guarantee service of all curves at
+the same time, and/or it's impossible to do so fairly. Both will be explained
+later. Note that this is mainly related to interior (aka aggregate) classes, as
+the leafs are already handled by \fB(1)\fR. Still \- it's perfectly possible to
+create a leaf class w/o realtime service, and in such case \- the caveats will
+naturally extend to leaf classes as well.
+
+.SH ABBREVIATIONS
+For the remaining part of the document, we'll use following shortcuts:
+.nf
+.RS 4
+
+RT \- realtime
+LS \- linkshare
+UL \- upperlimit
+SC \- service curve
+.fi
+.
+.SH "BASICS OF HFSC"
+.
+To understand how HFSC works, we must first introduce a service curve.
+Overall, it's a nondecreasing function of some time unit, returning amount of
+service (allowed or allocated amount of bandwidth) by some specific point in
+time. The purpose of it should be subconsciously obvious \- if a class was
+allowed to transfer not less than the amount specified by its service curve \-
+then service curve is not violated.
+
+Still \- we need more elaborate criterion than just the above (although in
+most generic case it can be reduced to it). The criterion has to take two
+things into account:
+.
+.RS 4
+.IP \(bu 4
+idling periods
+.IP \(bu
+ability to "look back", so if during current active period service curve is violated, maybe it
+isn't if we count excess bandwidth received during earlier active period(s)
+.RE
+.PP
+Let's define the criterion as follows:
+.RS 4
+.nf
+.IP "\fB(1)\fR" 4
+For each t1, there must exist t0 in set B, so S(t1\-t0)\~<=\~w(t0,t1)
+.fi
+.RE
+.
+.PP
+Here 'w' denotes the amount of service received during some time period between t0
+and t1. B is a set of all times, where a session becomes active after idling
+period (further denoted as 'becoming backlogged'). For a clearer picture,
+imagine two situations:
+.
+.RS 4
+.IP \fBa)\fR 4
+our session was active during two periods, with a small time gap between them
+.IP \fBb)\fR
+as in (a), but with a larger gap
+.RE
+.
+.PP
+Consider \fB(a)\fR \- if the service received during both periods meets
+\fB(1)\fR, then all is good. But what if it doesn't do so during the 2nd
+period ? If the amount of service received during the 1st period is bigger
+than the service curve, then it might compensate for smaller service during
+the 2nd period \fIand\fR the gap \- if the gap is small enough.
+
+If the gap is larger \fB(b)\fR \- then it's less likely to happen (unless the
+excess bandwidth allocated during the 1st part was really large). Still, the
+larger the gap \- the less interesting is what happened in the past (e.g. 10
+minutes ago) \- what matters is the current traffic that just started.
+
+From HFSC's perspective, more interesting is answering the following question:
+when should we start transferring packets, so a service curve of a class is not
+violated. Or rephrasing it: How much X() amount of service should a session
+receive by time t, so the service curve is not violated. Function X() defined
+as below is the basic building block of HFSC, used in: eligible, deadline,
+virtual\-time and fit\-time curves. Of course, X() is based on equation
+\fB(1)\fR and is defined recursively:
+
+.RS 4
+.IP \(bu 4
+At the 1st backlogged period beginning function X is initialized to generic
+service curve assigned to a class
+.IP \(bu
+At any subsequent backlogged period, X() is:
+.nf
+\fBmin(X() from previous period ; w(t0)+S(t\-t0) for t>=t0),\fR
+.fi
+\&... where t0 denotes the beginning of the current backlogged period.
+.RE
+.
+.PP
+HFSC uses either linear, or two\-piece linear service curves. In case of
+linear or two\-piece linear convex functions (first slope < second slope),
+min() in X's definition reduces to the 2nd argument. But in case of two\-piece
+concave functions, the 1st argument might quickly become lesser for some
+t>=t0. Note, that for some backlogged period, X() is defined only from that
+period's beginning. We also define X^(\-1)(w) as smallest t>=t0, for which
+X(t)\~=\~w. We have to define it this way, as X() is usually not an injection.
+
+The above generic X() can be one of the following:
+.
+.RS 4
+.IP "E()" 4
+In realtime criterion, selects packets eligible for sending. If none are
+eligible, HFSC will use linkshare criterion. Eligible time \&'et' is calculated
+with reference to packets' heads ( et\~=\~E^(\-1)(w) ). It's based on RT
+service curve, \fIbut in case of a convex curve, uses its 2nd slope only.\fR
+.IP "D()"
+In realtime criterion, selects the most suitable packet from the ones chosen
+by E(). Deadline time \&'dt' corresponds to packets' tails
+(dt\~=\~D^(\-1)(w+l), where \&'l' is packet's length). Based on RT service
+curve.
+.IP "V()"
+In linkshare criterion, arbitrates which packet to send next. Note that V() is
+function of a virtual time \- see \fBLINKSHARE CRITERION\fR section for
+details. Virtual time \&'vt' corresponds to packets' heads
+(vt\~=\~V^(\-1)(w)). Based on LS service curve.
+.IP "F()"
+An extension to linkshare criterion, used to limit at which speed linkshare
+criterion is allowed to dequeue. Fit\-time 'ft' corresponds to packets' heads
+as well (ft\~=\~F^(\-1)(w)). Based on UL service curve.
+.RE
+
+Be sure to make clean distinction between session's RT, LS and UL service
+curves and the above "utility" functions.
+.
+.SH "REALTIME CRITERION"
+.
+RT criterion \fIignores class hierarchy\fR and guarantees precise bandwidth and
+delay allocation. We say that packet is eligible for sending, when current real
+time is bigger than eligible time. From all packets eligible, the one most
+suited for sending, is the one with the smallest deadline time. Sounds simply,
+but consider following example:
+
+Interface 10mbit, two classes, both with two\-piece linear service curves:
+.RS 4
+.IP \(bu 4
+1st class \- 2mbit for 100ms, then 7mbit (convex \- 1st slope < 2nd slope)
+.IP \(bu
+2nd class \- 7mbit for 100ms, then 2mbit (concave \- 1st slope > 2nd slope)
+.RE
+.PP
+Assume for a moment, that we only use D() for both finding eligible packets,
+and choosing the most fitting one, thus eligible time would be computed as
+D^(\-1)(w) and deadline time would be computed as D^(\-1)(w+l). If the 2nd
+class starts sending packets 1 second after the 1st class, it's of course
+impossible to guarantee 14mbit, as the interface capability is only 10mbit.
+The only workaround in this scenario is to allow the 1st class to send the
+packets earlier that would normally be allowed. That's where separate E() comes
+to help. Putting all the math aside (see HFSC paper for details), E() for RT
+concave service curve is just like D(), but for the RT convex service curve \-
+it's constructed using \fIonly\fR RT service curve's 2nd slope (in our example
+\- 7mbit).
+
+The effect of such E() \- packets will be sent earlier, and at the same time
+D() \fIwill\fR be updated \- so current deadline time calculated from it will
+be bigger. Thus, when the 2nd class starts sending packets later, both the 1st
+and the 2nd class will be eligible, but the 2nd session's deadline time will be
+smaller and its packets will be sent first. When the 1st class becomes idle at
+some later point, the 2nd class will be able to "buffer" up again for later
+active period of the 1st class.
+
+A short remark \- in a situation, where the total amount of bandwidth
+available on the interface is bigger than the allocated total realtime parts
+(imagine interface 10 mbit, but 1mbit/2mbit and 2mbit/1mbit classes), the sole
+speed of the interface could suffice to guarantee the times.
+
+Important part of RT criterion is that apart from updating its D() and E(),
+also V() used by LS criterion is updated. Generally the RT criterion is
+secondary to LS one, and used \fIonly\fR if there's a risk of violating precise
+realtime requirements. Still, the "participation" in bandwidth distributed by
+LS criterion is there, so V() has to be updated along the way. LS criterion can
+than properly compensate for non\-ideal fair sharing situation, caused by RT
+scheduling. If you use UL service curve its F() will be updated as well (UL
+service curve is an extension to LS one \- see \fBUPPERLIMIT CRITERION\fR
+section).
+
+Anyway \- careless specification of LS and RT service curves can lead to
+potentially undesired situations (see CORNER CASES for examples). This wasn't
+the case in HFSC paper where LS and RT service curves couldn't be specified
+separately.
+
+.SH "LINKSHARING CRITERION"
+.
+LS criterion's task is to distribute bandwidth according to specified class
+hierarchy. Contrary to RT criterion, there're no comparisons between current
+real time and virtual time \- the decision is based solely on direct comparison
+of virtual times of all active subclasses \- the one with the smallest vt wins
+and gets scheduled. One immediate conclusion from this fact is that absolute
+values don't matter \- only ratios between them (so for example, two children
+classes with simple linear 1mbit service curves will get the same treatment
+from LS criterion's perspective, as if they were 5mbit). The other conclusion
+is, that in perfectly fluid system with linear curves, all virtual times across
+whole class hierarchy would be equal.
+
+Why is VC defined in term of virtual time (and what is it) ?
+
+Imagine an example: class A with two children \- A1 and A2, both with let's say
+10mbit SCs. If A2 is idle, A1 receives all the bandwidth of A (and update its
+V() in the process). When A2 becomes active, A1's virtual time is already
+\fIfar\fR bigger than A2's one. Considering the type of decision made by LS
+criterion, A1 would become idle for a lot of time. We can workaround this
+situation by adjusting virtual time of the class becoming active \- we do that
+by getting such time "up to date". HFSC uses a mean of the smallest and the
+biggest virtual time of currently active children fit for sending. As it's not
+real time anymore (excluding trivial case of situation where all classes become
+active at the same time, and never become idle), it's called virtual time.
+
+Such approach has its price though. The problem is analogous to what was
+presented in previous section and is caused by non\-linearity of service
+curves:
+.IP 1) 4
+either it's impossible to guarantee service curves and satisfy fairness
+during certain time periods:
+
+.RS 4
+Recall the example from RT section, slightly modified (with 3mbit slopes
+instead of 2mbit ones):
+
+.IP \(bu 4
+1st class \- 3mbit for 100ms, then 7mbit (convex \- 1st slope < 2nd slope)
+.IP \(bu
+2nd class \- 7mbit for 100ms, then 3mbit (concave \- 1st slope > 2nd slope)
+
+.PP
+They sum up nicely to 10mbit \- interface's capacity. But if we wanted to only
+use LS for guarantees and fairness \- it simply won't work. In LS context,
+only V() is used for making decision which class to schedule. If the 2nd class
+becomes active when the 1st one is in its second slope, the fairness will be
+preserved \- ratio will be 1:1 (7mbit:7mbit), but LS itself is of course
+unable to guarantee the absolute values themselves \- as it would have to go
+beyond of what the interface is capable of.
+.RE
+
+.IP 2) 4
+and/or it's impossible to guarantee service curves of all classes at the same
+time [fairly or not]:
+
+.RS 4
+
+This is similar to the above case, but a bit more subtle. We will consider two
+subtrees, arbitrated by their common (root here) parent:
+
+.nf
+R (root) -\ 10mbit
+
+A  \- 7mbit, then 3mbit
+A1 \- 5mbit, then 2mbit
+A2 \- 2mbit, then 1mbit
+
+B  \- 3mbit, then 7mbit
+.fi
+
+R arbitrates between left subtree (A) and right (B). Assume that A2 and B are
+constantly backlogged, and at some later point A1 becomes backlogged (when all
+other classes are in their 2nd linear part).
+
+What happens now ? B (choice made by R) will \fIalways\fR get 7 mbit as R is
+only (obviously) concerned with the ratio between its direct children. Thus A
+subtree gets 3mbit, but its children would want (at the point when A1 became
+backlogged) 5mbit + 1mbit. That's of course impossible, as they can only get
+3mbit due to interface limitation.
+
+In the left subtree \- we have the same situation as previously (fair split
+between A1 and A2, but violated guarantees), but in the whole tree \- there's
+no fairness (B got 7mbit, but A1 and A2 have to fit together in 3mbit) and
+there's no guarantees for all classes (only B got what it wanted). Even if we
+violated fairness in the A subtree and set A2's service curve to 0, A1 would
+still not get the required bandwidth.
+.RE
+.
+.SH "UPPERLIMIT CRITERION"
+.
+UL criterion is an extensions to LS one, that permits sending packets only
+if current real time is bigger than fit\-time ('ft'). So the modified LS
+criterion becomes: choose the smallest virtual time from all active children,
+such that fit\-time < current real time also holds. Fit\-time is calculated
+from F(), which is based on UL service curve. As you can see, it's role is
+kinda similar to E() used in RT criterion. Also, for obvious reasons \- you
+can't specify UL service curve without LS one.
+
+Main purpose of UL service curve is to limit HFSC to bandwidth available on the
+upstream router (think adsl home modem/router, and linux server as
+nat/firewall/etc. with 100mbit+ connection to mentioned modem/router).
+Typically, it's used to create a single class directly under root, setting
+linear UL service curve to available bandwidth \- and then creating your class
+structure from that class downwards. Of course, you're free to add UL service
+(linear or not) curve to any class with LS criterion.
+
+Important part about UL service curve is, that whenever at some point in time
+a class doesn't qualify for linksharing due to its fit\-time, the next time it
+does qualify, it will update its virtual time to the smallest virtual time of
+all active children fit for linksharing. This way, one of the main things LS
+criterion tries to achieve \- equality of all virtual times across whole
+hierarchy \- is preserved (in perfectly fluid system with only linear curves,
+all virtual times would be equal).
+
+Without that, 'vt' would lag behind other virtual times, and could cause
+problems. Consider interface with capacity 10mbit, and following leaf classes
+(just in case you're skipping this text quickly \- this example shows behavior
+that \f(BIdoesn't happen\fR):
+
+.nf
+A \- ls 5.0mbit
+B \- ls 2.5mbit
+C \- ls 2.5mbit, ul 2.5mbit
+.fi
+
+If B was idle, while A and C were constantly backlogged, they would normally
+(as far as LS criterion is concerned) divide bandwidth in 2:1 ratio. But due
+to UL service curve in place, C would get at most 2.5mbit, and A would get the
+remaining 7.5mbit. The longer the backlogged period, the more virtual times of
+A and C would drift apart. If B became backlogged at some later point in time,
+its virtual time would be set to (A's\~vt\~+\~C's\~vt)/2, thus blocking A from
+sending any traffic, until B's virtual time catches up with A.
+.
+.SH "SEPARATE LS / RT SCs"
+.
+Another difference from original HFSC paper, is that RT and LS SCs can be
+specified separately. Moreover \- leaf classes are allowed to have only either
+RT SC or LS SC. For interior classes, only LS SCs make sense \- Any RT SC will
+be ignored.
+.
+.SH "CORNER CASES"
+.
+Separate service curves for LS and RT criteria can lead to certain traps,
+that come from "fighting" between ideal linksharing and enforced realtime
+guarantees. Those situations didn't exist in original HFSC paper, where
+specifying separate LS / RT service curves was not discussed.
+
+Consider interface with capacity 10mbit, with following leaf classes:
+
+.nf
+A \- ls 5.0mbit, rt 8mbit
+B \- ls 2.5mbit
+C \- ls 2.5mbit
+.fi
+
+Imagine A and C are constantly backlogged. As B is idle, A and C would divide
+bandwidth in 2:1 ratio, considering LS service curve (so in theory \- 6.66 and
+3.33). Alas RT criterion takes priority, so A will get 8mbit and LS will be
+able to compensate class C for only 2 mbit \- this will cause discrepancy
+between virtual times of A and C.
+
+Assume this situation lasts for a lot of time with no idle periods, and
+suddenly B becomes active. B's virtual time will be updated to
+(A's\~vt\~+\~C's\~vt)/2, effectively landing in the middle between A's and C's
+virtual time. The effect \- B, having no RT guarantees, will be punished and
+will not be allowed to transfer until C's virtual time catches up.
+
+If the interface had higher capacity \- for example 100mbit, this example
+would behave perfectly fine though.
+
+Let's look a bit closer at the above example \- it "cleverly" invalidates one
+of the basic things LS criterion tries to achieve \- equality of all virtual
+times across class hierarchy. Leaf classes without RT service curves are
+literally left to their own fate (governed by messed up virtual times).
+
+Also - it doesn't make much sense. Class A will always be guaranteed up to
+8mbit, and this is more than any absolute bandwidth that could happen from its
+LS criterion (excluding trivial case of only A being active). If the bandwidth
+taken by A is smaller than absolute value from LS criterion, the unused part
+will be automatically assigned to other active classes (as A has idling periods
+in such case). The only "advantage" is, that even in case of low bandwidth on
+average, bursts would be handled at the speed defined by RT criterion. Still,
+if extra speed is needed (e.g. due to latency), non linear service curves
+should be used in such case.
+
+In the other words - LS criterion is meaningless in the above example.
+
+You can quickly "workaround" it by making sure each leaf class has RT service
+curve assigned (thus guaranteeing all of them will get some bandwidth), but it
+doesn't make it any more valid.
+
+Keep in mind - if you use nonlinear curves and irregularities explained above
+happen \fIonly\fR in the first segment, then there's little wrong with
+"overusing" RT curve a bit:
+
+.nf
+A \- ls 5.0mbit, rt 9mbit/30ms, then 1mbit
+B \- ls 2.5mbit
+C \- ls 2.5mbit
+.fi
+
+Here, the vt of A will "spike" in the initial period, but then A will never get more
+than 1mbit, until B & C catch up. Then everything will be back to normal.
+.
+.SH "LINUX AND TIMER RESOLUTION"
+.
+In certain situations, the scheduler can throttle itself and setup so
+called watchdog to wakeup dequeue function at some time later. In case of HFSC
+it happens when for example no packet is eligible for scheduling, and UL
+service curve is used to limit the speed at which LS criterion is allowed to
+dequeue packets. It's called throttling, and accuracy of it is dependent on
+how the kernel is compiled.
+
+There're 3 important options in modern kernels, as far as timers' resolution
+goes: \&'tickless system', \&'high resolution timer support' and \&'timer
+frequency'.
+
+If you have \&'tickless system' enabled, then the timer interrupt will trigger
+as slowly as possible, but each time a scheduler throttles itself (or any
+other part of the kernel needs better accuracy), the rate will be increased as
+needed / possible. The ceiling is either \&'timer frequency' if \&'high
+resolution timer support' is not available or not compiled in, or it's
+hardware dependent and can go \fIfar\fR beyond the highest \&'timer frequency'
+setting available.
+
+If \&'tickless system' is not enabled, the timer will trigger at a fixed rate
+specified by \&'timer frequency' \- regardless if high resolution timers are
+or aren't available.
+
+This is important to keep those settings in mind, as in scenario like: no
+tickless, no HR timers, frequency set to 100hz \- throttling accuracy would be
+at 10ms. It doesn't automatically mean you would be limited to ~0.8mbit/s
+(assuming packets at ~1KB) \- as long as your queues are prepared to cover for
+timer inaccuracy. Of course, in case of e.g. locally generated udp traffic \-
+appropriate socket size is needed as well. Short example to make it more
+understandable (assume hardcore anti\-schedule settings \- HZ=100, no HR
+timers, no tickless):
+
+.nf
+tc qdisc add dev eth0 root handle 1:0 hfsc default 1
+tc class add dev eth0 parent 1:0 classid 1:1 hfsc rt m2 10mbit
+.fi
+
+Assuming packet of ~1KB size and HZ=100, that averages to ~0.8mbit \- anything
+beyond it (e.g. the above example with specified rate over 10x bigger) will
+require appropriate queuing and cause bursts every ~10 ms. As you can
+imagine, any HFSC's RT guarantees will be seriously invalidated by that.
+Aforementioned example is mainly important if you deal with old hardware \- as
+it's particularly popular for home server chores. Even then, you can easily
+set HZ=1000 and have very accurate scheduling for typical adsl speeds.
+
+Anything modern (apic or even hpet msi based timers + \&'tickless system')
+will provide enough accuracy for superb 1gbit scheduling. For example, on one
+of basically cheap dual core AMD boards I have with following settings:
+
+.nf
+tc qdisc add dev eth0 parent root handle 1:0 hfsc default 1
+tc class add dev eth0 paretn 1:0 classid 1:1 hfsc rt m2 300mbit
+.fi
+
+And simple:
+
+.nf
+nc \-u dst.host.com 54321 </dev/zero
+nc \-l \-p 54321 >/dev/null
+.fi
+
+\&...will yield following effects over period of ~10 seconds (taken from
+/proc/interrupts):
+
+.nf
+319: 42124229   0  HPET_MSI\-edge  hpet2 (before)
+319: 42436214   0  HPET_MSI\-edge  hpet2 (after 10s.)
+.fi
+
+That's roughly 31000/s. Now compare it with HZ=1000 setting. The obvious
+drawback of it is that cpu load can be rather extensive with servicing that
+many timer interrupts. Example with 300mbit RT service curve on 1gbit link is
+particularly ugly, as it requires a lot of throttling with minuscule delays.
+
+Also note that it's just an example showing capability of current hardware.
+The above example (essentially 300mbit TBF emulator) is pointless on internal
+interface to begin with \- you will pretty much always want regular LS service
+curve there, and in such scenario HFSC simply doesn't throttle at all.
+
+300mbit RT service curve (selected columns from mpstat \-P ALL 1):
+
+.nf
+10:56:43 PM  CPU  %sys     %irq   %soft   %idle
+10:56:44 PM  all  20.10    6.53   34.67   37.19
+10:56:44 PM    0  35.00    0.00   63.00    0.00
+10:56:44 PM    1   4.95   12.87    6.93   73.27
+.fi
+
+So, in rare case you need those speeds with only RT service curve, or with UL
+service curve \- remember about drawbacks.
+.
+.SH "CAVEAT: RANDOM ONLINE EXAMPLES"
+.
+For reasons unknown (though well guessed), many examples you can google love to
+overuse UL criterion and stuff it in every node possible. This makes no sense
+and works against what HFSC tries to do (and does pretty damn well). Use UL
+where it makes sense - on the uppermost node to match upstream router's uplink
+capacity. Or - in special cases, such as testing (limit certain subtree to some
+speed) or customers that must never get more than certain speed. In the last
+case you can usually achieve the same by just using RT criterion without LS+UL
+on leaf nodes.
+
+As for router case - remember it's good to differentiate between "traffic to
+router" (remote console, web config, etc.) and "outgoing traffic", so for
+example:
+
+.nf
+tc qdisc add dev eth0 root handle 1:0 hfsc default 0x8002
+tc class add dev eth0 parent 1:0 classid 1:999 hfsc rt m2 50mbit
+tc class add dev eth0 parent 1:0 classid 1:1 hfsc ls m2 2mbit ul m2 2mbit
+.fi
+
+\&... so "internet" tree under 1:1 and "router itself" as 1:999
+.
+.SH "LAYER2 ADAPTATION"
+.
+Please refer to \fBtc\-stab\fR(8)
+.
+.SH "SEE ALSO"
+.
+\fBtc\fR(8), \fBtc\-hfsc\fR(8), \fBtc\-stab\fR(8)
+
+Please direct bugreports and patches to: <net...@vger.kernel.org>
+.
+.SH "AUTHOR"
+.
+Manpage created by Michal Soltys (sol...@ziu.info)
diff --git a/man/man8/Makefile b/man/man8/Makefile
new file mode 100644
index 0000000..6873a4b
--- /dev/null
+++ b/man/man8/Makefile
@@ -0,0 +1,32 @@
+TARGETS = ip-address.8 ip-link.8 ip-route.8
+
+MAN8PAGES = $(TARGETS) ip.8 arpd.8 lnstat.8 routel.8 rtacct.8 rtmon.8 ss.8 \
+	tc-bfifo.8 tc-cbq-details.8 tc-cbq.8 tc-drr.8 tc-htb.8 \
+	tc-pfifo.8 tc-pfifo_fast.8 tc-prio.8 tc-red.8 tc-sfq.8 \
+	tc-tbf.8 tc.8 rtstat.8 ctstat.8 nstat.8 routef.8 \
+	tc-sfb.8 tc-netem.8 tc-choke.8 ip-tunnel.8 ip-rule.8 ip-ntable.8 \
+	ip-monitor.8 tc-stab.8 tc-hfsc.8 ip-xfrm.8 ip-netns.8 \
+	ip-neighbour.8 ip-mroute.8 ip-maddress.8 ip-addrlabel.8
+
+
+all: $(TARGETS)
+
+ip-address.8: ip-address.8.in
+	sed "s|@SYSCONFDIR@|$(CONFDIR)|g" $< > $@
+
+ip-link.8: ip-link.8.in
+	sed "s|@SYSCONFDIR@|$(CONFDIR)|g" $< > $@
+
+ip-route.8: ip-route.8.in
+	sed "s|@SYSCONFDIR@|$(CONFDIR)|g" $< > $@
+
+distclean: clean
+
+clean:
+	@rm -f $(TARGETS)
+
+install:
+	$(INSTALLDIR) $(DESTDIR)$(MANDIR)/man8
+	$(INSTALLMAN) $(MAN8PAGES) $(DESTDIR)$(MANDIR)/man8
+
+.PHONY: install clean distclean
diff --git a/man/man8/arpd.8 b/man/man8/arpd.8
index d172600..a14044b 100644
--- a/man/man8/arpd.8
+++ b/man/man8/arpd.8
@@ -4,7 +4,7 @@
 arpd \- userspace arp daemon.
 
 .SH SYNOPSIS
-Usage: arpd [ -lk ] [ -a N ] [ -b dbase ] [ -f file ] [ interfaces ]
+Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ] [ -f file ] [-p interval ] [ -n time ] [ -R rate ] [ interfaces ]
 
 .SH DESCRIPTION
 The
@@ -34,7 +34,10 @@
 -n <TIME>
 Timeout of negative cache. When resolution fails arpd suppresses further attempts to resolve for this period. It makes sense only together with option -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.
 .TP
--r <RATE>
+-p <TIME>
+Time to wait in seconds between polling attempts to the kernel ARP table. TIME may be a floating point number.  The default value is 30.
+.TP
+-R <RATE>
 Maximal steady rate of broadcasts sent by arpd in packets per second. Default value is 1.
 .TP
 -B <NUMBER>
diff --git a/man/man8/ctstat.8 b/man/man8/ctstat.8
new file mode 100644
index 0000000..080e2b2
--- /dev/null
+++ b/man/man8/ctstat.8
@@ -0,0 +1 @@
+.so man8/lnstat.8
diff --git a/man/man8/ip-address.8 b/man/man8/ip-address.8
new file mode 100644
index 0000000..2eb0709
--- /dev/null
+++ b/man/man8/ip-address.8
@@ -0,0 +1,280 @@
+.TH "IP\-ADDRESS" 8 "04 March 2012" "iproute2" "Linux"
+.SH "NAME"
+ip-address \- protocol address management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B address
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.BR "ip address" " { " add " | " change " | " replace " } "
+.IB IFADDR " dev " STRING
+.RI "[ " LIFETIME " ] [ " CONFFLAG-LIST " ]"
+
+.ti -8
+.BR "ip address del"
+.IB IFADDR " dev " STRING
+
+.ti -8
+.BR "ip address" " { " 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 " | " dadfailed " | " temporary " | " CONFFLAG-LIST " ]"
+
+.ti -8
+.IR CONFFLAG-LIST " := [ "  CONFFLAG-LIST " ] " CONFFLAG
+
+.ti -8
+.IR CONFFLAG " := "
+.RB "[ " home " | " nodad " ]"
+
+.ti -8
+.IR LIFETIME " := [ "
+.BI valid_lft " LFT"
+.RB "| " preferred_lft
+.IR  LFT " ]"
+
+.ti -8
+.IR LFT " := [ "
+.BR forever " |"
+.IR SECONDS " ]"
+
+.SH "DESCRIPTION"
+The
+.B address
+is a protocol (IPv4 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 address
+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 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
+
+.TP
+.BI valid_lft " LFT"
+(IPv6 only) the valid lifetime of this address; see section 5.5.4 of
+RFC 4862. Defaults to
+.BR "forever" .
+
+.TP
+.BI preferred_lft " LFT"
+(IPv6 only) the preferred lifetime of this address; see section 5.5.4
+of RFC 4862. Defaults to
+.BR "forever" .
+
+.TP
+.B home
+(IPv6 only) designates this address the "home address" as defined in
+RFC 6275.
+
+.TP
+.B nodad
+(IPv6 only) do not perform Duplicate Address Detection (RFC 4862) when
+adding this address.
+
+.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 have not yet passed duplicate
+address detection.
+
+.TP
+.B deprecated
+(IPv6 only) only list deprecated addresses.
+
+.TP
+.B dadfailed
+(IPv6 only) only list addresses which have failed duplicate
+address detection.
+
+.TP
+.B temporary
+(IPv6 only) only list temporary 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 are unforgiving. They 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 address flush
+also dumps all the deleted addresses in the format described in the
+previous subsection.
+
+.SH "EXAMPLES"
+.PP
+ip address show dev eth0
+.RS 4
+Shows the addresses assigned to network interface eth0
+.RE
+.PP
+ip addr add 2001:0db8:85a3::0370:7334/64 dev eth1
+.RS 4
+Adds an IPv6 address to network interface eth1
+.RE
+.PP
+ip addr flush dev eth4
+.RS 4
+Removes all addresses from device eth4
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-address.8.in b/man/man8/ip-address.8.in
new file mode 100644
index 0000000..63bf9cb
--- /dev/null
+++ b/man/man8/ip-address.8.in
@@ -0,0 +1,240 @@
+.TH "IP\-ADDRESS" 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-address \- protocol address management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B address
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.BR "ip address" " { " add " | " del " } "
+.IB IFADDR " dev " STRING
+
+.ti -8
+.BR "ip address" " { " 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 " | " dadfailed " | " temporary " ]"
+
+.SH "DESCRIPTION"
+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 address
+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 "@SYSCONFDIR@/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 have not yet passed duplicate
+address detection.
+
+.TP
+.B deprecated
+(IPv6 only) only list deprecated addresses.
+
+.TP
+.B dadfailed
+(IPv6 only) only list addresses which have failed duplicate
+address detection.
+
+.TP
+.B temporary
+(IPv6 only) only list temporary 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 address flush
+also dumps all the deleted addresses in the format described in the
+previous subsection.
+
+.SH "EXAMPLES"
+.PP
+ip address show dev eth0
+.RS 4
+Shows the addresses assigned to network interface eth0
+.RE
+.PP
+ip addr add 2001:0db8:85a3::0370:7334/64 dev eth1
+.RS 4
+Adds an IPv6 address to network interface eth1
+.RE
+.PP
+ip addr flush dev eth4
+.RS 4
+Removes all addresses from device eth4
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-addrlabel.8 b/man/man8/ip-addrlabel.8
new file mode 100644
index 0000000..3252bd2
--- /dev/null
+++ b/man/man8/ip-addrlabel.8
@@ -0,0 +1,69 @@
+.TH IP\-ADDRLABEL 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-addrlabel \- protocol address label management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]" 
+.B addrlabel 
+.RI " { " COMMAND " | "
+.BR help " }"
+.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
+.BR "ip addrlabel" " { " add " | " del " } " prefix
+.BR PREFIX " [ "
+.B dev
+.IR DEV " ] [ "
+.B label
+.IR NUMBER " ]"
+
+.ti -8
+.BR "ip addrlabel" " { " list " | " flush " }"
+
+.SH "DESCRIPTION"
+IPv6 address label is used for address selection
+described in RFC 3484.  Precedence is managed by userspace,
+and only label is stored in kernel.
+
+.SS ip addrlabel add - add an address label
+the command adds an address label entry to the kernel.
+.TP
+.BI prefix " PREFIX"
+.TP
+.BI dev " DEV"
+the outgoing interface.
+.TP
+.BI label " NUMBER"
+the label for the prefix.
+0xffffffff is reserved.
+.SS ip addrlabel del - delete an address label
+the command deletes an address label entry in the kernel.
+.B Arguments:
+coincide with the arguments of
+.B ip addrlabel add
+but label is not required.
+.SS ip addrlabel list - list address labels
+the command show contents of address labels.
+.SS ip addrlabel flush - flush address labels
+the command flushes the contents of address labels and it does not restore default settings.
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Manpage by Yoshifuji Hideaki / 吉藤英明
+
diff --git a/man/man8/ip-l2tp.8 b/man/man8/ip-l2tp.8
new file mode 100644
index 0000000..18a83d4
--- /dev/null
+++ b/man/man8/ip-l2tp.8
@@ -0,0 +1,376 @@
+.TH IP\-L2TP 8 "19 Apr 2012" "iproute2" "Linux"
+.SH "NAME"
+ip-l2tp - L2TPv3 static unmanaged tunnel configuration
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B l2tp
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+.ti -8
+.BR "ip l2tp add tunnel"
+.br
+.B remote
+.RI "[ " ADDR " ]"
+.B local
+.RI "[ " ADDR " ]"
+.br
+.B tunnel_id
+.IR ID
+.B peer_tunnel_id
+.IR ID
+.br
+.RB "[ " encap " { " ip " | " udp " } ]"
+.br
+.RB "[ " udp_sport
+.IR PORT
+.RB " ] [ " udp_dport
+.IR PORT
+.RB " ]"
+.br
+.ti -8
+.BR "ip l2tp add session"
+.RB "[ " name
+.IR NAME
+.RB " ]"
+.br
+.B tunnel_id
+.IR ID
+.B session_id
+.IR ID
+.B peer_session_id
+.IR ID
+.br
+.RB "[ " cookie
+.IR HEXSTR
+.RB " ] [ " peer_cookie
+.IR HEXSTR
+.RB " ]"
+.br
+.RB "[ " offset
+.IR OFFSET
+.RB " ] [ " peer_offset
+.IR OFFSET
+.RB " ]"
+.br
+.ti -8
+.BR "ip l2tp del tunnel"
+.B tunnel_id
+.IR ID
+.br
+.ti -8
+.BR "ip l2tp del session"
+.B tunnel_id
+.IR ID
+.B session_id
+.IR ID
+.br
+.ti -8
+.BR "ip l2tp show tunnel"
+.B "[" tunnel_id
+.IR ID
+.B "]"
+.br
+.ti -8
+.BR "ip l2tp show session"
+.B "[" tunnel_id
+.IR ID
+.B "] [" session_id
+.IR ID
+.B "]"
+.br
+.ti -8
+.IR NAME " := "
+.IR STRING
+.ti -8
+.IR ADDR " := { " IP_ADDRESS " }"
+.ti -8
+.IR PORT " := { " NUMBER " }"
+.ti -8
+.IR ID " := { " NUMBER " }"
+.ti -8
+.ti -8
+.IR HEXSTR " := { 8 or 16 hex digits (4 / 8 bytes) }"
+.SH DESCRIPTION
+The
+.B ip l2tp
+commands are used to establish static, or so-called
+.I unmanaged
+L2TPv3 ethernet tunnels. For unmanaged tunnels, there is no L2TP
+control protocol so no userspace daemon is required - tunnels are
+manually created by issuing commands at a local system and at a remote
+peer.
+.PP
+L2TPv3 is suitable for Layer-2 tunnelling. Static tunnels are useful
+to establish network links across IP networks when the tunnels are
+fixed. L2TPv3 tunnels can carry data of more than one session. Each
+session is identified by a session_id and its parent tunnel's
+tunnel_id. A tunnel must be created before a session can be created in
+the tunnel.
+.PP
+When creating an L2TP tunnel, the IP address of the remote peer is
+specified, which can be either an IPv4 or IPv6 address. The local IP
+address to be used to reach the peer must also be specified. This is
+the address on which the local system will listen for and accept
+received L2TP data packets from the peer.
+.PP
+L2TPv3 defines two packet encapsulation formats: UDP or IP. UDP
+encapsulation is most common. IP encapsulation uses a dedicated IP
+protocol value to carry L2TP data without the overhead of UDP. Use IP
+encapsulation only when there are no NAT devices or firewalls in the
+network path.
+.PP
+When an L2TPv3 ethernet session is created, a virtual network
+interface is created for the session, which must then be configured
+and brought up, just like any other network interface. When data is
+passed through the interface, it is carried over the L2TP tunnel to
+the peer. By configuring the system's routing tables or adding the
+interface to a bridge, the L2TP interface is like a virtual wire
+(pseudowire) connected to the peer.
+.PP
+Establishing an unmanaged L2TPv3 ethernet pseudowire involves manually
+creating L2TP contexts on the local system and at the peer. Parameters
+used at each site must correspond or no data will be passed. No
+consistency checks are possible since there is no control protocol
+used to establish unmanaged L2TP tunnels. Once the virtual network
+interface of a given L2TP session is configured and enabled, data can
+be transmitted, even if the peer isn't yet configured. If the peer
+isn't configured, the L2TP data packets will be discarded by
+the peer.
+.PP
+To establish an unmanaged L2TP tunnel, use
+.B l2tp add tunnel
+and
+.B l2tp add session
+commands described in this document. Then configure and enable the
+tunnel's virtual network interface, as required.
+.PP
+Note that unmanaged tunnels carry only ethernet frames. If you need to
+carry PPP traffic (L2TPv2) or your peer doesn't support unmanaged
+L2TPv3 tunnels, you will need an L2TP server which implements the L2TP
+control protocol. The L2TP control protocol allows dynamic L2TP
+tunnels and sessions to be established and provides for detecting and
+acting upon network failures.
+.SS ip l2tp add tunnel - add a new tunnel
+.TP
+.BI name " NAME "
+sets the session network interface name. Default is l2tpethN.
+.TP
+.BI tunnel_id " ID"
+set the tunnel id, which is a 32-bit integer value. Uniquely
+identifies the tunnel. The value used must match the peer_tunnel_id
+value being used at the peer.
+.TP
+.BI peer_tunnel_id " ID"
+set the peer tunnel id, which is a 32-bit integer value assigned to
+the tunnel by the peer. The value used must match the tunnel_id value
+being used at the peer.
+.TP
+.BI remote " ADDR"
+set the IP address of the remote peer. May be specified as an IPv4
+address or an IPv6 address.
+.TP
+.BI local " ADDR"
+set the IP address of the local interface to be used for the
+tunnel. This address must be the address of a local interface. May be
+specified as an IPv4 address or an IPv6 address.
+.TP
+.BI encap " ENCAP"
+set the encapsulation type of the tunnel.
+.br
+Valid values for encapsulation are:
+.BR udp ", " ip "."
+.TP
+.BI udp_sport " PORT"
+set the UDP source port to be used for the tunnel. Must be present
+when udp encapsulation is selected. Ignored when ip encapsulation is
+selected.
+.TP
+.BI udp_dport " PORT"
+set the UDP destination port to be used for the tunnel. Must be
+present when udp encapsulation is selected. Ignored when ip
+encapsulation is selected.
+.SS ip l2tp del tunnel - destroy a tunnel
+.TP
+.BI tunnel_id " ID"
+set the tunnel id of the tunnel to be deleted. All sessions within the
+tunnel must be deleted first.
+.SS ip l2tp show tunnel - show information about tunnels
+.TP
+.BI tunnel_id " ID"
+set the tunnel id of the tunnel to be shown. If not specified,
+information about all tunnels is printed.
+.SS ip l2tp add session - add a new session to a tunnel
+.TP
+.BI name " NAME "
+sets the session network interface name. Default is l2tpethN.
+.TP
+.BI tunnel_id " ID"
+set the tunnel id, which is a 32-bit integer value. Uniquely
+identifies the tunnel into which the session will be created. The
+tunnel must already exist.
+.TP
+.BI session_id " ID"
+set the session id, which is a 32-bit integer value. Uniquely
+identifies the session being created. The value used must match the
+peer_session_id value being used at the peer.
+.TP
+.BI peer_session_id " ID"
+set the peer session id, which is a 32-bit integer value assigned to
+the session by the peer. The value used must match the session_id
+value being used at the peer.
+.TP
+.BI cookie " HEXSTR"
+sets an optional cookie value to be assigned to the session. This is a
+4 or 8 byte value, specified as 8 or 16 hex digits,
+e.g. 014d3636deadbeef. The value must match the peer_cookie value set
+at the peer. The cookie value is carried in L2TP data packets and is
+checked for expected value at the peer. Default is to use no cookie.
+.TP
+.BI peer_cookie " HEXSTR"
+sets an optional peer cookie value to be assigned to the session. This
+is a 4 or 8 byte value, specified as 8 or 16 hex digits,
+e.g. 014d3636deadbeef. The value must match the cookie value set at
+the peer. It tells the local system what cookie value to expect to
+find in received L2TP packets. Default is to use no cookie.
+.TP
+.BI offset " OFFSET"
+sets the byte offset from the L2TP header where user data starts in
+transmitted L2TP data packets. This is hardly ever used. If set, the
+value must match the peer_offset value used at the peer. Default is 0.
+.TP
+.BI peer_offset " OFFSET"
+sets the byte offset from the L2TP header where user data starts in
+received L2TP data packets. This is hardly ever used. If set, the
+value must match the offset value used at the peer. Default is 0.
+.SS ip l2tp del session - destroy a session
+.TP
+.BI tunnel_id " ID"
+set the tunnel id in which the session to be deleted is located.
+.TP
+.BI session_id " ID"
+set the session id of the session to be deleted.
+.SS ip l2tp show session - show information about sessions
+.TP
+.BI tunnel_id " ID"
+set the tunnel id of the session(s) to be shown. If not specified,
+information about sessions in all tunnels is printed.
+.TP
+.BI session_id " ID"
+set the session id of the session to be shown. If not specified,
+information about all sessions is printed.
+.SH EXAMPLES
+.PP
+.SS Setup L2TP tunnels and sessions
+.nf
+site-A:# ip l2tp add tunnel tunnel_id 3000 peer_tunnel_id 4000 \\
+           encap udp local 1.2.3.4 remote 5.6.7.8 \\
+           udp_sport 5000 udp_dport 6000
+site-A:# ip l2tp add session tunnel_id 3000 session_id 1000 \\
+           peer_session_id 2000
+
+site-B:# ip l2tp add tunnel tunnel_id 4000 peer_tunnel_id 3000 \\
+           encap udp local 5.6.7.8 remote 1.2.3.4 \\
+           udp_sport 6000 udp_dport 5000
+site-B:# ip l2tp add session tunnel_id 4000 session_id 2000 \\
+           peer_session_id 1000
+
+site-A:# ip link set l2tpeth0 up mtu 1488
+
+site-B:# ip link set l2tpeth0 up mtu 1488
+.fi
+.PP
+Notice that the IP addresses, UDP ports and tunnel / session ids are
+matched and reversed at each site.
+.SS Configure as IP interfaces
+The two interfaces can be configured with IP addresses if only IP data
+is to be carried. This is perhaps the simplest configuration.
+.PP
+.nf
+site-A:# ip addr add 10.42.1.1 peer 10.42.1.2 dev l2tpeth0
+
+site-B:# ip addr add 10.42.1.2 peer 10.42.1.1 dev l2tpeth0
+
+site-A:# ping 10.42.1.2
+.fi
+.PP
+Now the link should be usable. Add static routes as needed to have
+data sent over the new link.
+.PP
+.SS Configure as bridged interfaces
+To carry non-IP data, the L2TP network interface is added to a bridge
+instead of being assigned its own IP address, using standard Linux
+utilities. Since raw ethernet frames are then carried inside the
+tunnel, the MTU of the L2TP interfaces must be set to allow space for
+those headers.
+.PP
+.nf
+site-A:# ip link set l2tpeth0 up mtu 1446
+site-A:# brctl addbr br0
+site-A:# brctl addif br0 l2tpeth0
+site-A:# brctl addif br0 eth0
+site-A:# ip link set br0 up
+.fi
+.PP
+If you are using VLANs, setup a bridge per VLAN and bridge each VLAN
+over a separate L2TP session. For example, to bridge VLAN ID 5 on eth1
+over an L2TP pseudowire:
+.PP
+.nf
+site-A:# ip link set l2tpeth0 up mtu 1446
+site-A:# brctl addbr brvlan5
+site-A:# brctl addif brvlan5 l2tpeth0.5
+site-A:# brctl addif brvlan5 eth1.5
+site-A:# ip link set brvlan5 up
+.fi
+.PP
+Adding the L2TP interface to a bridge causes the bridge to forward
+traffic over the L2TP pseudowire just like it forwards over any other
+interface. The bridge learns MAC addresses of hosts attached to each
+interface and intelligently forwards frames from one bridge port to
+another. IP addresses are not assigned to the l2tpethN interfaces. If
+the bridge is correctly configured at both sides of the L2TP
+pseudowire, it should be possible to reach hosts in the peer's bridged
+network.
+.PP
+When raw ethernet frames are bridged across an L2TP tunnel, large
+frames may be fragmented and forwarded as individual IP fragments to
+the recipient, depending on the MTU of the physical interface used by
+the tunnel. When the ethernet frames carry protocols which are
+reassembled by the recipient, like IP, this isn't a problem. However,
+such fragmentation can cause problems for protocols like PPPoE where
+the recipient expects to receive ethernet frames exactly as
+transmitted. In such cases, it is important that frames leaving the
+tunnel are reassembled back into a single frame before being
+forwarded on. To do so, enable netfilter connection tracking
+(conntrack) or manually load the Linux netfilter degrag modules at
+each tunnel endpoint.
+.PP
+.nf
+site-A:# modprobe nf_degrag_ipv4
+
+site-B:# modprobe nf_degrag_ipv4
+.fi
+.PP
+If L2TP is being used over IPv6, use the IPv6 degrag module.
+.SH INTEROPABILITY
+.PP
+Unmanaged (static) L2TPv3 tunnels are supported by some network
+equipment equipment vendors such as Cisco.
+.PP
+In Linux, L2TP Hello messages are not supported in unmanaged
+tunnels. Hello messages are used by L2TP clients and servers to detect
+link failures in order to automate tearing down and reestablishing
+dynamic tunnels. If a non-Linux peer supports Hello messages in
+unmanaged tunnels, it must be turned off to interoperate with Linux.
+.SH SEE ALSO
+.br
+.BR brctl (8)
+.BR ip (8)
+.SH AUTHOR
+James Chapman <jchapman@katalix.com>
diff --git a/man/man8/ip-link.8 b/man/man8/ip-link.8
new file mode 100644
index 0000000..24d2ec7
--- /dev/null
+++ b/man/man8/ip-link.8
@@ -0,0 +1,394 @@
+.TH IP\-LINK 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-link \- network device configuration
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B link
+.RI  " { " COMMAND " | "
+.BR help " }"
+.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 add"
+.RB "[ " link
+.IR DEVICE " ]"
+.RB "[ " name " ]"
+.I NAME
+.br
+.RB "[ " txqueuelen 
+.IR PACKETS " ]"
+.br
+.RB "[ " address
+.IR LLADDR " ]"
+.RB "[ " broadcast
+.IR LLADDR " ]"
+.br
+.RB "[ " mtu
+.IR MTU " ]"
+.br
+.BR type " TYPE"
+.RI "[ " ARGS " ]"
+
+.ti -8
+.IR TYPE " := [ "
+.BR vlan " | " veth " | " vcan " | " dummy " | " ifb " | " macvlan " | " can " | " bridge " ]"
+
+.ti -8
+.BI "ip link delete " DEVICE
+.BI type " TYPE"
+.RI "[ " ARGS " ]"
+
+.ti -8
+.BR "ip link set " {
+.IR DEVICE " | "
+.BI "group " GROUP
+.RB "} { " up " | " down " | " arp " { " on " | " off " } |"
+.br
+.BR promisc " { " on " | " off " } |"
+.br
+.BR allmulticast " { " 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 " |"
+.br
+.B  netns
+.IR PID " |"
+.br
+.B  netns
+.IR NETNSNAME " |"
+.br
+.B alias
+.IR NAME  " |"
+.br
+.B vf
+.IR NUM " ["
+.B  mac
+.IR LLADDR " ] ["
+.B vlan
+.IR VLANID " [ "
+.B qos
+.IR VLAN-QOS " ] ] ["
+.B rate
+.IR TXRATE " ] ["
+.B spoofchk { on | off }
+] |
+.br
+.B mode
+.IR LINKMODE " |"
+.br
+.B state
+.IR LINKSTATE " |"
+.br
+.B master
+.IR DEVICE
+.br
+.B nomaster
+.BR " }"
+
+
+.ti -8
+.B ip link show
+.RI "[ " DEVICE " | "
+.B group
+.IR GROUP " ]"
+
+.SH "DESCRIPTION"
+.SS ip link add - add virtual link
+
+.TP
+.BI link " DEVICE "
+specifies the physical device to act operate on.
+
+.I NAME
+specifies the name of the new virtual device.
+
+.I TYPE
+specifies the type of the new device.
+.sp
+Link types:
+
+.in +8
+.B vlan
+- 802.1q tagged virtual LAN interface
+.sp
+.B veth
+- Virtual ethernet interface
+.sp
+.B vcan
+- Virtual Local CAN interface
+.sp
+.B dummy
+- Dummy network interface
+.sp
+.B ifb
+- Intermediate Functional Block device
+.sp
+.B macvlan
+- virtual interface base on link layer address (MAC)
+.sp
+.B can
+- Controller Area Network interface
+.sp
+.B bridge
+- Ethernet Bridge device
+.in -8
+
+.SS ip link delete - delete virtual link
+.I DEVICE
+specifies the virtual  device to act operate on.
+.I TYPE
+specifies the type of the device.
+
+
+.TP
+.BI dev " DEVICE "
+specifies the physical device to act operate on.
+
+.SS ip link set - change device attributes
+
+.TP
+.BI dev " DEVICE "
+.I DEVICE
+specifies network device to operate on. When configuring SR-IOV Virtual Fuction
+(VF) devices, this keyword should specify the associated Physical Function (PF)
+device.
+
+.TP
+.BI group " GROUP "
+.I GROUP
+has a dual role: If both group and dev are present, then move the device to the
+specified group.  If only a group is specified, then the command operates on
+all devices in that group.
+
+.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" .
+
+.TP
+.BI netns " PID"
+move the device to the network namespace associated with the process
+.IR "PID".
+
+.TP
+.BI netns " NETNSNAME"
+move the device to the network namespace associated with name
+.IR "NETNSNAME".
+
+.TP
+.BI mode " LINKMODE"
+allows setting link mode which determines which RFC2863 operational state
+the device will transistion to when it is brought up. Setting
+.I dormant
+mode changes the behaviour so that device goes into DORMANT state instead
+of UP when driver is ready.
+
+.TP
+.BI state " LINKSTATE"
+allows setting the operational link state. The values (defined in RFC2863)
+are: UP, DOWN, TESTING, UNKNOWN, DORMANT, NOTPRESENT, LOWERLAYERDOWN.
+.TP
+.BI alias " NAME"
+give the device a symbolic name for easy reference.
+
+.TP
+.BI group " GROUP"
+specify the group the device belongs to.
+The available groups are listed in file
+.BR "/etc/iproute2/group" .
+
+.TP
+.BI vf " NUM"
+specify a Virtual Function device to be configured. The associated PF device
+must be specified using the
+.B dev
+parameter.
+
+.in +8
+.BI mac " LLADDRESS"
+- change the station address for the specified VF. The
+.B vf
+parameter must be specified.
+
+.sp
+.BI vlan " VLANID"
+- change the assigned VLAN for the specified VF. When specified, all traffic
+sent from the VF will be tagged with the specified VLAN ID. Incoming traffic
+will be filtered for the specified VLAN ID, and will have all VLAN tags
+stripped before being passed to the VF. Setting this parameter to 0 disables
+VLAN tagging and filtering. The
+.B vf
+parameter must be specified.
+
+.sp
+.BI qos " VLAN-QOS"
+- assign VLAN QOS (priority) bits for the VLAN tag. When specified, all VLAN
+tags transmitted by the VF will include the specified priority bits in the
+VLAN tag. If not specified, the value is assumed to be 0. Both the
+.B vf
+and
+.B vlan
+parameters must be specified. Setting both
+.B vlan
+and
+.B qos
+as 0 disables VLAN tagging and filtering for the VF.
+
+.sp
+.BI rate " TXRATE"
+- change the allowed transmit bandwidth, in Mbps, for the specified VF.
+Setting this parameter to 0 disables rate limiting. The
+.B vf
+parameter must be specified.
+
+.sp
+.BI spoofchk " on|off"
+- turn packet spoof checking on or off for the specified VF.
+.in -8
+
+.TP
+.BI master " DEVICE"
+set master device of the device (enslave device).
+
+.TP
+.BI nomaster
+unset master device of the device (release device).
+
+.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 in the default group are listed.
+
+.TP
+.BI group " GROUP "
+.I GROUP
+specifies what group of devices to show.
+
+.TP
+.B up
+only display running interfaces.
+
+.SH "EXAMPLES"
+.PP
+ip link show
+.RS 4
+Shows the state of all network interfaces on the system.
+.RE
+.PP
+ip link set dev ppp0 mtu 1400
+.RS 4
+Change the MTU the ppp0 device.
+.RE
+.PP
+ip link add link eth0 name eth0.10 type vlan id 10
+.RS 4
+Creates a new vlan device eth0.10 on device eth0.
+.RE
+.PP
+ip link delete dev eth0.10
+.RS 4
+Removes vlan device.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
new file mode 100644
index 0000000..9386cc6
--- /dev/null
+++ b/man/man8/ip-link.8.in
@@ -0,0 +1,376 @@
+.TH IP\-LINK 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-link \- network device configuration
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B link
+.RI  " { " COMMAND " | "
+.BR help " }"
+.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 add"
+.RB "[ " link
+.IR DEVICE " ]"
+.RB "[ " name " ]"
+.I NAME
+.br
+.RB "[ " txqueuelen
+.IR PACKETS " ]"
+.br
+.RB "[ " address
+.IR LLADDR " ]"
+.RB "[ " broadcast
+.IR LLADDR " ]"
+.br
+.RB "[ " mtu
+.IR MTU " ]"
+.br
+.BR type " TYPE"
+.RI "[ " ARGS " ]"
+
+.ti -8
+.IR TYPE " := [ "
+.BR vlan " | " veth " | " vcan " | " dummy " | " ifb " | " macvlan " | " can " | " bridge " ]"
+
+.ti -8
+.BI "ip link delete " DEVICE
+.BI type " TYPE"
+.RI "[ " ARGS " ]"
+
+.ti -8
+.BR "ip link set " {
+.IR DEVICE " | "
+.BI "group " GROUP
+.RB "} { " up " | " down " | " arp " { " on " | " off " } |"
+.br
+.BR promisc " { " on " | " off " } |"
+.br
+.BR allmulticast " { " 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 " |"
+.br
+.B  netns
+.IR PID " |"
+.br
+.B  netns
+.IR NETNSNAME " |"
+.br
+.B alias
+.IR NAME  " |"
+.br
+.B vf
+.IR NUM " ["
+.B  mac
+.IR LLADDR " ] ["
+.B vlan
+.IR VLANID " [ "
+.B qos
+.IR VLAN-QOS " ] ] ["
+.B rate
+.IR TXRATE " ] ["
+.B spoofchk { on | off }
+] |
+.br
+.B master
+.IR DEVICE
+.br
+.B nomaster
+.BR " }"
+
+
+.ti -8
+.B ip link show
+.RI "[ " DEVICE " | "
+.B group
+.IR GROUP " ]"
+
+.SH "DESCRIPTION"
+.SS ip link add - add virtual link
+
+.TP
+.BI link " DEVICE "
+specifies the physical device to act operate on.
+
+.I NAME
+specifies the name of the new virtual device.
+
+.I TYPE
+specifies the type of the new device.
+.sp
+Link types:
+
+.in +8
+.B vlan
+- 802.1q tagged virtual LAN interface
+.sp
+.B veth
+- Virtual ethernet interface
+.sp
+.B vcan
+- Virtual Local CAN interface
+.sp
+.B dummy
+- Dummy network interface
+.sp
+.B ifb
+- Intermediate Functional Block device
+.sp
+.B macvlan
+- virtual interface base on link layer address (MAC)
+.sp
+.B can
+- Controller Area Network interface
+.sp
+.B bridge
+- Ethernet Bridge device
+.in -8
+
+.SS ip link delete - delete virtual link
+.I DEVICE
+specifies the virtual  device to act operate on.
+.I TYPE
+specifies the type of the device.
+
+
+.TP
+.BI dev " DEVICE "
+specifies the physical device to act operate on.
+
+.SS ip link set - change device attributes
+
+.TP
+.BI dev " DEVICE "
+.I DEVICE
+specifies network device to operate on. When configuring SR-IOV Virtual Fuction
+(VF) devices, this keyword should specify the associated Physical Function (PF)
+device.
+
+.TP
+.BI group " GROUP "
+.I GROUP
+has a dual role: If both group and dev are present, then move the device to the
+specified group.  If only a group is specified, then the command operates on
+all devices in that group.
+
+.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" .
+
+.TP
+.BI netns " PID"
+move the device to the network namespace associated with the process
+.IR "PID".
+
+.TP
+.BI netns " NETNSNAME"
+move the device to the network namespace associated with name
+.IR "NETNSNAME".
+
+.TP
+.BI alias " NAME"
+give the device a symbolic name for easy reference.
+
+.TP
+.BI group " GROUP"
+specify the group the device belongs to.
+The available groups are listed in file
+.BR "@SYSCONFDIR@/group" .
+
+.TP
+.BI vf " NUM"
+specify a Virtual Function device to be configured. The associated PF device
+must be specified using the
+.B dev
+parameter.
+
+.in +8
+.BI mac " LLADDRESS"
+- change the station address for the specified VF. The
+.B vf
+parameter must be specified.
+
+.sp
+.BI vlan " VLANID"
+- change the assigned VLAN for the specified VF. When specified, all traffic
+sent from the VF will be tagged with the specified VLAN ID. Incoming traffic
+will be filtered for the specified VLAN ID, and will have all VLAN tags
+stripped before being passed to the VF. Setting this parameter to 0 disables
+VLAN tagging and filtering. The
+.B vf
+parameter must be specified.
+
+.sp
+.BI qos " VLAN-QOS"
+- assign VLAN QOS (priority) bits for the VLAN tag. When specified, all VLAN
+tags transmitted by the VF will include the specified priority bits in the
+VLAN tag. If not specified, the value is assumed to be 0. Both the
+.B vf
+and
+.B vlan
+parameters must be specified. Setting both
+.B vlan
+and
+.B qos
+as 0 disables VLAN tagging and filtering for the VF.
+
+.sp
+.BI rate " TXRATE"
+- change the allowed transmit bandwidth, in Mbps, for the specified VF.
+Setting this parameter to 0 disables rate limiting. The
+.B vf
+parameter must be specified.
+
+.sp
+.BI spoofchk " on|off"
+- turn packet spoof checking on or off for the specified VF.
+.in -8
+
+.TP
+.BI master " DEVICE"
+set master device of the device (enslave device).
+
+.TP
+.BI nomaster
+unset master device of the device (release device).
+
+.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 in the default group are listed.
+
+.TP
+.BI group " GROUP "
+.I GROUP
+specifies what group of devices to show.
+
+.TP
+.B up
+only display running interfaces.
+
+.SH "EXAMPLES"
+.PP
+ip link show
+.RS 4
+Shows the state of all network interfaces on the system.
+.RE
+.PP
+ip link set dev ppp0 mtu 1400
+.RS 4
+Change the MTU the ppp0 device.
+.RE
+.PP
+ip link add link eth0 name eth0.10 type vlan id 10
+.RS 4
+Creates a new vlan device eth0.10 on device eth0.
+.RE
+.PP
+ip link delete dev eth0.10
+.RS 4
+Removes vlan device.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-maddress.8 b/man/man8/ip-maddress.8
new file mode 100644
index 0000000..afae551
--- /dev/null
+++ b/man/man8/ip-maddress.8
@@ -0,0 +1,54 @@
+.TH IP\-MADDRESS 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-maddress \- multicast addresses management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B  maddress
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+.ti -8
+
+.BR "ip maddress" " [ " add " | " del " ]"
+.IB MULTIADDR " dev " STRING
+
+.ti -8
+.BR "ip maddress show" " [ " dev
+.IR STRING " ]"
+
+.SH DESCRIPTION
+.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 SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-monitor.8 b/man/man8/ip-monitor.8
new file mode 100644
index 0000000..351a744
--- /dev/null
+++ b/man/man8/ip-monitor.8
@@ -0,0 +1,67 @@
+.TH IP\-MONITOR 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-monitor, rtmon \- state monitoring
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.BR "ip monitor" " [ " all " |"
+.IR LISTofOBJECTS " ]"
+.sp
+
+.SH DESCRIPTION
+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 SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-mroute.8 b/man/man8/ip-mroute.8
new file mode 100644
index 0000000..98aab88
--- /dev/null
+++ b/man/man8/ip-mroute.8
@@ -0,0 +1,50 @@
+.TH IP\-MROUTE 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-mroute \- multicast routing cache management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.BR "ip mroute show" " ["
+.IR PREFIX " ] [ "
+.B  from
+.IR PREFIX " ] [ "
+.B  iif
+.IR DEVICE " ]"
+
+.SH DESCRIPTION
+.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 SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-neighbour.8 b/man/man8/ip-neighbour.8
new file mode 100644
index 0000000..34980c5
--- /dev/null
+++ b/man/man8/ip-neighbour.8
@@ -0,0 +1,197 @@
+.TH IP\-NEIGHBOUR 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-neighbour \- neighbour/arp tables management.
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B neigh
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.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 " } [ " proxy " ] [ " to
+.IR PREFIX " ] [ "
+.B  dev
+.IR DEV " ] [ "
+.B  nud
+.IR STATE " ]"
+
+
+.SH DESCRIPTION
+The 
+.B ip neigh
+command manipulates 
+.I neighbour
+objects that 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 'Neighbour 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
+.BI proxy
+list neighbour proxies.
+
+.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 EXAMPLES
+.PP
+ip neighbour
+.RS
+Shows the current neighbour table in kernel.
+.RE
+.PP
+ip neigh flush dev eth0
+.RS
+Removes entries in the neighbour table on device eth0.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-netns.8 b/man/man8/ip-netns.8
new file mode 100644
index 0000000..7fe9eb9
--- /dev/null
+++ b/man/man8/ip-netns.8
@@ -0,0 +1,68 @@
+.TH IP\-NETNS 8 "20 Dec 2011" "iproute2" "Linux"
+.SH NAME
+ip-netns \- process network namespace management
+.SH SYNOPSIS
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B netns
+.RI  " { " COMMAND " | "
+.BR help " }"
+.sp
+.ti -8
+.BR "ip netns" " { " list " } "
+
+.ti -8
+.BR "ip netns" " { " add " | " delete " } "
+.I NETNSNAME
+
+.ti -8
+.BR "ip netns exec "
+.I NETNSNAME command ...
+
+.SH DESCRIPTION
+A network namespace is logically another copy of the network stack,
+with it's own routes, firewall rules, and network devices.
+
+By convention a named network namespace is an object at
+.BR "/var/run/netns/" NAME
+that can be opened.  The file descriptor resulting from opening
+.BR "/var/run/netns/" NAME
+refers to the specified network namespace.  Holding that file
+descriptor open keeps the network namespace alive.  The file
+descriptor can be used with the
+.B setns(2)
+system call to change the network namespace associated with a task.
+
+The convention for network namespace aware applications is to look
+for global network configuration files first in
+.BR "/etc/netns/" NAME "/"
+then in
+.BR "/etc/".
+For example, if you want a different version of
+.BR /etc/resolv.conf
+for a network namespace used to isolate your vpn you would name it
+.BR /etc/netns/myvpn/resolv.conf.
+
+.B ip netns exec
+automates handling of this configuration, file convention for network
+namespace unaware applications, by creating a mount namespace and
+bind mounting all of the per network namespace configure files into
+their traditional location in /etc.
+
+.SS ip netns list - show all of the named network namespaces
+.SS ip netns add NAME - create a new named network namespace
+.SS ip netns delete NAME - delete the name of a network namespace
+.SS ip netns exec NAME cmd ... - Run cmd in the named network namespace
+
+.SH EXAMPLES
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Eric W. Biederman
diff --git a/man/man8/ip-ntable.8 b/man/man8/ip-ntable.8
new file mode 100644
index 0000000..d903a17
--- /dev/null
+++ b/man/man8/ip-ntable.8
@@ -0,0 +1,101 @@
+.TH IP\-NTABLE 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-ntable - neighbour table configuration
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B address
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.BR "ip ntable change name"
+.IR NAME " [ "
+.B dev
+.IR DEV " ] " PARMS
+
+.ti -8
+.IR PARMS " := { "
+.B thresh1
+.IR VAL " | "
+.B thresh2
+.IR VAL " | "
+.B thresh3
+.IR VAL " | "
+.B gc_int
+.IR MSEC " | "
+.B base_reachable
+.IR MSEC " | "
+.B retrans
+.IR MSEC " | " "gc_stale MSEC " " | "
+.B delay_probe
+.IR MSEC " | " "queue LEN " " | "
+.B app_probs
+.IR VAL " | "
+.B ucast_probes
+.IR VAL " | " "mcast_probes VAL " " | "
+.B anycast_delay
+.IR MSEC " | "
+.B proxy_delay
+.IR MSEC " | " "proxy_queue LEN " " | "
+.B locktime
+.IR MSEC " }"
+
+.ti -8
+.BR "ip ntable show" " [ "
+.B dev
+.IR DEV " ] [ "
+.B name
+.IR NAME " ]"
+
+.SH DESCRIPTION
+.I ip ntable
+controls the parameters for the neighbour tables. 
+
+.SS ip ntable show - list the ip neighbour tables
+
+This commands displays neighbour table parameters and statistics.
+
+.TP
+.BI dev " DEV"
+only list the table attached to this device.
+
+.TP
+.BI name " NAME"
+only lists the table with the given name.
+
+.SS ip ntable change - modify table parameter
+
+This command allows modifying table parameters such as timers and queue lengths.
+.TP
+.BI name " NAME"
+the name of the table to modify.
+
+.TP
+.BI dev " DEV"
+the name of the device to modify the table values.
+
+.SH EXAMPLES
+.PP
+ip ntable show dev eth0
+.RS 4
+Shows the neighbour table (IPv4 ARP and IPv6 ndisc) parameters on device eth0.
+.RE
+.PP
+ip ntable change name arp_cache queue 8 dev eth0
+.RS 4
+Changes the number of packets queued while address is being resolved from the
+default value (3) to 8 packets.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Manpage by Stephen Hemminger 
diff --git a/man/man8/ip-route.8 b/man/man8/ip-route.8
new file mode 100644
index 0000000..91c1a0b
--- /dev/null
+++ b/man/man8/ip-route.8
@@ -0,0 +1,744 @@
+.TH IP\-ROUTE 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-route \- routing table management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]" 
+.B route 
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+.ti -8
+
+.ti -8
+.BR "ip route" " { "
+.BR list " | " flush " } "
+.I  SELECTOR
+
+.ti -8
+.BR "ip route save"
+.I SELECTOR
+
+.ti -8
+.BR "ip route restore"
+
+.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 " } "
+.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 TIME " ] [ "
+.B  rttvar
+.IR TIME " ] [ "
+.B  window
+.IR NUMBER " ] [ "
+.B  cwnd
+.IR NUMBER " ] [ "
+.B  ssthresh
+.IR REALM " ] [ "
+.B  realms
+.IR REALM " ] [ "
+.B  rto_min
+.IR TIME " ] [ "
+.B  initcwnd
+.IR NUMBER " ] [ "
+.B  initrwnd
+.IR NUMBER " ]"
+
+.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 NHFLAGS " := [ "
+.BR onlink " | " pervasive " ]"
+
+.ti -8
+.IR RTPROTO " := [ "
+.BR kernel " | " boot " | " static " |"
+.IR NUMBER " ]"
+
+
+.SH DESCRIPTION
+.B ip route
+is used to manipulate entries in the kernel routing tables.
+.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 2^31 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.
+Values (0, 253, 254, and 255) are reserved for built-in use.
+
+.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 " TIME"
+the initial RTT ('Round Trip Time') estimate. If no suffix is
+specified the units are raw values passed directly to the
+routing code to maintain compatibility with previous releases.
+Otherwise if a suffix of s, sec or secs is used to specify
+seconds and ms, msec or msecs to specify milliseconds.
+
+
+.TP
+.BI rttvar " TIME " "(2.3.15+ only)"
+the initial RTT variance estimate. Values are specified as with
+.BI rtt
+above.
+
+.TP
+.BI rto_min " TIME " "(2.6.23+ only)"
+the minimum TCP Retransmission TimeOut to use when communicating with this
+destination.  Values are specified as with
+.BI rtt
+above.
+
+.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 initcwnd " NUMBER " "(2.5.70+ only)"
+the initial congestion window size for connections to this destination.
+Actual window size is this value multiplied by the MSS
+(``Maximal Segment Size'') for same connection. The default is
+zero, meaning to use the values specified in RFC2414.
+
+.TP
+.BI initrwnd " NUMBER " "(2.6.33+ only)"
+the initial receive window size for connections to this destination.
+Actual window size is this value multiplied by the MSS of the connection.
+The default value is zero, meaning to use Slow Start value.
+
+.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.
+
+.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.
+
+.SS ip route save - save routing table information to stdout
+this command behaves like
+.BR "ip route show"
+except that the output is raw data suitable for passing to
+.BR "ip route restore" .
+
+.SS ip route restore - restore routing table information from stdin
+this command expects to read a data stream as returned from
+.BR "ip route save" .
+It will attempt to restore the routing table information exactly as
+it was at the time of the save, so any translation of information
+in the stream (such as device indexes) must be done first.  Any existing
+routes are left unchanged.  Any routes specified in the data stream that
+already exist in the table will be ignored.
+
+.SH EXAMPLES
+.PP
+ip ro
+.RS 4
+Show all route entries in the kernel.
+.RE
+.PP
+ip route add default via 192.168.1.1 dev eth0
+.RS 4
+Adds a default route (for all addresses) via the local gateway 192.168.1.1 that can
+be reached on device eth0.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
new file mode 100644
index 0000000..0ca6107
--- /dev/null
+++ b/man/man8/ip-route.8.in
@@ -0,0 +1,744 @@
+.TH IP\-ROUTE 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-route \- routing table management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B route
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+.ti -8
+
+.ti -8
+.BR "ip route" " { "
+.BR list " | " flush " } "
+.I  SELECTOR
+
+.ti -8
+.BR "ip route save"
+.I SELECTOR
+
+.ti -8
+.BR "ip route restore"
+
+.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 " } "
+.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 TIME " ] [ "
+.B  rttvar
+.IR TIME " ] [ "
+.B  window
+.IR NUMBER " ] [ "
+.B  cwnd
+.IR NUMBER " ] [ "
+.B  ssthresh
+.IR REALM " ] [ "
+.B  realms
+.IR REALM " ] [ "
+.B  rto_min
+.IR TIME " ] [ "
+.B  initcwnd
+.IR NUMBER " ] [ "
+.B  initrwnd
+.IR NUMBER " ]"
+
+.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 NHFLAGS " := [ "
+.BR onlink " | " pervasive " ]"
+
+.ti -8
+.IR RTPROTO " := [ "
+.BR kernel " | " boot " | " static " |"
+.IR NUMBER " ]"
+
+
+.SH DESCRIPTION
+.B ip route
+is used to manipulate entries in the kernel routing tables.
+.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 2^31 or by name from the file
+.B @SYSCONFDIR@/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.
+Values (0, 253, 254, and 255) are reserved for built-in use.
+
+.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 "@SYSCONFDIR@/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 "@SYSCONFDIR@/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 "@SYSCONFDIR@/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 " TIME"
+the initial RTT ('Round Trip Time') estimate. If no suffix is
+specified the units are raw values passed directly to the
+routing code to maintain compatibility with previous releases.
+Otherwise if a suffix of s, sec or secs is used to specify
+seconds and ms, msec or msecs to specify milliseconds.
+
+
+.TP
+.BI rttvar " TIME " "(2.3.15+ only)"
+the initial RTT variance estimate. Values are specified as with
+.BI rtt
+above.
+
+.TP
+.BI rto_min " TIME " "(2.6.23+ only)"
+the minimum TCP Retransmission TimeOut to use when communicating with this
+destination.  Values are specified as with
+.BI rtt
+above.
+
+.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 initcwnd " NUMBER " "(2.5.70+ only)"
+the initial congestion window size for connections to this destination.
+Actual window size is this value multiplied by the MSS
+(``Maximal Segment Size'') for same connection. The default is
+zero, meaning to use the values specified in RFC2414.
+
+.TP
+.BI initrwnd " NUMBER " "(2.6.33+ only)"
+the initial receive window size for connections to this destination.
+Actual window size is this value multiplied by the MSS of the connection.
+The default value is zero, meaning to use Slow Start value.
+
+.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 "@SYSCONFDIR@/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 "@SYSCONFDIR@/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.
+
+.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.
+
+.SS ip route save - save routing table information to stdout
+this command behaves like
+.BR "ip route show"
+except that the output is raw data suitable for passing to
+.BR "ip route restore" .
+
+.SS ip route restore - restore routing table information from stdin
+this command expects to read a data stream as returned from
+.BR "ip route save" .
+It will attempt to restore the routing table information exactly as
+it was at the time of the save, so any translation of information
+in the stream (such as device indexes) must be done first.  Any existing
+routes are left unchanged.  Any routes specified in the data stream that
+already exist in the table will be ignored.
+
+.SH EXAMPLES
+.PP
+ip ro
+.RS 4
+Show all route entries in the kernel.
+.RE
+.PP
+ip route add default via 192.168.1.1 dev eth0
+.RS 4
+Adds a default route (for all addresses) via the local gateway 192.168.1.1 that can
+be reached on device eth0.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-rule.8 b/man/man8/ip-rule.8
new file mode 100644
index 0000000..0f62a53
--- /dev/null
+++ b/man/man8/ip-rule.8
@@ -0,0 +1,253 @@
+.TH IP\-RULE 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-rule \- routing policy database management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B rule
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.B  ip rule
+.RB " [ " list " | " add " | " del " | " flush " ]"
+.I  SELECTOR ACTION
+
+.ti -8
+.IR SELECTOR " := [ "
+.B  from
+.IR PREFIX " ] [ "
+.B  to
+.IR PREFIX " ] [ "
+.B  tos
+.IR TOS " ] [ "
+.B  fwmark
+.IR FWMARK[/MASK] " ] [ "
+.B  iif
+.IR STRING " ] [ "
+.B  oif
+.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 " ]"
+
+.SH DESCRIPTION
+.I ip rule
+manipulates rules 
+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 oif " NAME"
+select the outgoing device to match.  The outgoing interface is only
+available for packets originating from local sockets that are bound to
+a device.
+
+.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.
+The options preference and order are synonyms with priority.
+
+.TP
+.BI table " TABLEID"
+the routing table identifier to lookup if the rule selector matches.
+It is also possible to use lookup instead of table.
+
+.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.
+Using map-to instead of nat means the same thing.
+
+.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 flush - also dumps all the deleted rules.
+This command has no arguments.
+
+.SS ip rule show - list rules
+This command has no arguments.
+The options list or lst are synonyms with show.
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-tunnel.8 b/man/man8/ip-tunnel.8
new file mode 100644
index 0000000..37ba542
--- /dev/null
+++ b/man/man8/ip-tunnel.8
@@ -0,0 +1,242 @@
+.TH IP\-TUNNEL 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-tunnel - tunnel configuration
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B tunnel
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+.ti -8
+.BR "ip tunnel" " { " add " | " change " | " del " | " show " | " prl " }"
+.RI "[ " NAME " ]"
+.br
+.RB "[ " mode
+.IR MODE " ] [ "
+.B remote
+.IR ADDR " ] [ "
+.B  local
+.IR ADDR " ]"
+.br
+.RB "[ [" i "|" o "]" seq " ] [ [" i "|" o "]" key
+.IR KEY " ] [ "
+.RB "[" i "|" o "]" csum " ] ]"
+.br
+.RB "[ " encaplimit
+.IR ELIM " ]"
+.RB "[ " ttl
+.IR TTL " ]"
+.br
+.RB "[ " tos
+.IR TOS " ] [ "
+.B flowlabel
+.IR FLOWLABEL " ]"
+.br
+.RB "[ " prl-default
+.IR ADDR " ] [ "
+.B prl-nodefault
+.IR ADDR " ] [ "
+.B prl-delete
+.IR ADDR " ]"
+.br
+.RB "[ [" no "]" pmtudisc " ]"
+.RB "[ " dev
+.IR PHYS_DEV " ]"
+.RB "[ " "dscp inherit" " ]"
+
+.ti -8
+.IR MODE " := "
+.RB " { " ipip " | " gre " | " sit " | " isatap " | " ip6ip6 " | " ipip6 " | " any " }"
+
+.ti -8
+.IR ADDR " := { " IP_ADDRESS " |"
+.BR any " }"
+
+.ti -8
+.IR TOS " := { " NUMBER " |"
+.BR inherit " }"
+
+.ti -8
+.IR ELIM " := {"
+.BR none " | "
+.IR 0 ".." 255 " }"
+
+.ti -8
+.ti -8
+.IR TTL " := { " 1 ".." 255 " | "
+.BR inherit " }"
+
+.ti -8
+.IR KEY " := { " DOTTED_QUAD " | " NUMBER " }"
+
+.ti -8
+.IR TIME " := " NUMBER "[s|ms]"
+
+.SH DESCRIPTION
+.B tunnel
+objects are tunnels, encapsulating packets in IP packets and then
+sending them over the IP infrastructure.
+The encapulating (or outer) address family is specified by the
+.B -f
+option.  The default is IPv4.
+
+.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. Available modes depend on the encapsulating address family.
+.br
+Modes for IPv4 encapsulation available:
+.BR ipip ", " sit ", " isatap " and " gre "."
+.br
+Modes for IPv6 encapsulation available:
+.BR ip6ip6 ", " ipip6 " and " any "."
+
+.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 for IPv4 tunnels is:
+.BR "inherit" .
+The default value for IPv6 tunnels is:
+.BR "64" .
+
+
+.TP
+.BI tos " T"
+.TP
+.BI dsfield " T"
+.TP
+.BI tclass " T"
+set a fixed TOS (or traffic class in IPv6)
+.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.
+
+.TP
+.BR "dscp inherit"
+.RB ( " only IPv6 tunnels " )
+Inherit DS field between inner and outer header.
+
+.TP
+.BI encaplim " ELIM"
+.RB ( " only IPv6 tunnels " )
+set a fixed encapsulation limit.  Default is 4.
+
+.TP
+.BI flowlabel " FLOWLABEL"
+.RB ( " only IPv6 tunnels " )
+set a fixed flowlabel.
+
+.SS ip tunnel prl - potential router list (ISATAP only)
+
+.TP
+.BI dev " NAME"
+mandatory device name.
+
+.TP
+.BI prl-default " ADDR"
+.TP
+.BI prl-nodefault " ADDR"
+.TP
+.BI prl-delete " ADDR"
+.RB "Add or delete " ADDR
+as a potential router or default router.
+
+.SS ip tunnel show - list tunnels
+This command has no arguments.
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/ip-xfrm.8 b/man/man8/ip-xfrm.8
new file mode 100644
index 0000000..f359773
--- /dev/null
+++ b/man/man8/ip-xfrm.8
@@ -0,0 +1,581 @@
+.TH IP\-XFRM 8 "20 Dec 2011" "iproute2" "Linux"
+.SH "NAME"
+ip-xfrm \- transform configuration
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B xfrm
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.B "ip xfrm"
+.IR XFRM-OBJECT " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.IR XFRM-OBJECT " :="
+.BR state " | " policy " | " monitor
+.sp
+
+.ti -8
+.BR "ip xfrm state" " { " add " | " update " } "
+.IR ID " [ " ALGO-LIST " ]"
+.RB "[ " mode
+.IR MODE " ]"
+.RB "[ " mark
+.I MARK
+.RB "[ " mask
+.IR MASK " ] ]"
+.RB "[ " reqid
+.IR REQID " ]"
+.RB "[ " seq
+.IR SEQ " ]"
+.RB "[ " replay-window
+.IR SIZE " ]"
+.RB "[ " replay-seq
+.IR SEQ " ]"
+.RB "[ " replay-oseq
+.IR SEQ " ]"
+.RB "[ " flag
+.IR FLAG-LIST " ]"
+.RB "[ " sel
+.IR SELECTOR " ] [ " LIMIT-LIST " ]"
+.RB "[ " encap
+.IR ENCAP " ]"
+.RB "[ " coa
+.IR ADDR "[/" PLEN "] ]"
+.RB "[ " ctx
+.IR CTX " ]"
+
+.ti -8
+.B "ip xfrm state allocspi"
+.I ID
+.RB "[ " mode
+.IR MODE " ]"
+.RB "[ " mark
+.I MARK
+.RB "[ " mask
+.IR MASK " ] ]"
+.RB "[ " reqid
+.IR REQID " ]"
+.RB "[ " seq
+.IR SEQ " ]"
+.RB "[ " min
+.I SPI
+.B max
+.IR SPI " ]"
+
+.ti -8
+.BR "ip xfrm state" " { " delete " | " get " } "
+.I ID
+.RB "[ " mark
+.I MARK
+.RB "[ " mask
+.IR MASK " ] ]"
+
+.ti -8
+.BR "ip xfrm state" " { " deleteall " | " list " } ["
+.IR ID " ]"
+.RB "[ " mode
+.IR MODE " ]"
+.RB "[ " reqid
+.IR REQID " ]"
+.RB "[ " flag
+.IR FLAG-LIST " ]"
+
+.ti -8
+.BR "ip xfrm state flush" " [ " proto
+.IR XFRM-PROTO " ]"
+
+.ti -8
+.BR "ip xfrm state count"
+
+.ti -8
+.IR ID " :="
+.RB "[ " src
+.IR ADDR " ]"
+.RB "[ " dst
+.IR ADDR " ]"
+.RB "[ " proto
+.IR XFRM-PROTO " ]"
+.RB "[ " spi
+.IR SPI " ]"
+
+.ti -8
+.IR XFRM-PROTO " :="
+.BR esp " | " ah " | " comp " | " route2 " | " hao
+
+.ti -8
+.IR ALGO-LIST " := [ " ALGO-LIST " ] " ALGO
+
+.ti -8
+.IR ALGO " :="
+.RB "{ " enc " | " auth " | " comp " } " 
+.IR ALGO-NAME " " ALGO-KEY " |"
+.br
+.B  aead
+.IR ALGO-NAME " " ALGO-KEY " " ALGO-ICV-LEN  " |"
+.br
+.B auth-trunc
+.IR ALGO-NAME " " ALGO-KEY " " ALGO-TRUNC-LEN
+
+.ti -8
+.IR MODE " := "
+.BR transport " | " tunnel " | " ro " | " in_trigger " | " beet
+
+.ti -8
+.IR FLAG-LIST " := [ " FLAG-LIST " ] " FLAG
+
+.ti -8
+.IR FLAG " :="
+.BR noecn " | " decap-dscp " | " nopmtudisc " | " wildrecv " | " icmp " | " af-unspec " | " align4
+
+.ti -8
+.IR SELECTOR " :="
+.RB "[ " src
+.IR ADDR "[/" PLEN "] ]"
+.RB "[ " dst
+.IR ADDR "[/" PLEN "] ]"
+.RB "[ " dev
+.IR DEV " ]"
+.br
+.RI "[ " UPSPEC " ]"
+
+.ti -8
+.IR UPSPEC " := "
+.BR proto " {"
+.IR PROTO " |"
+.br
+.RB "{ " tcp " | " udp " | " sctp " | " dccp " } [ " sport
+.IR PORT " ]"
+.RB "[ " dport
+.IR PORT " ] |"
+.br
+.RB "{ " icmp " | " ipv6-icmp " | " mobility-header " } [ " type
+.IR NUMBER " ]"
+.RB "[ " code
+.IR NUMBER " ] |"
+.br
+.BR gre " [ " key
+.RI "{ " DOTTED-QUAD " | " NUMBER " } ] }"
+
+.ti -8
+.IR LIMIT-LIST " := [ " LIMIT-LIST " ]"
+.B limit
+.I LIMIT
+
+.ti -8
+.IR LIMIT " :="
+.RB "{ " time-soft " | " time-hard " | " time-use-soft " | " time-use-hard " }"
+.IR "SECONDS" " |"
+.br
+.RB "{ " byte-soft " | " byte-hard " }"
+.IR SIZE " |"
+.br
+.RB "{ " packet-soft " | " packet-hard " }"
+.I COUNT
+
+.ti -8
+.IR ENCAP " :="
+.RB "{ " espinudp " | " espinudp-nonike " }"
+.IR SPORT " " DPORT " " OADDR
+
+.ti -8
+.BR "ip xfrm policy" " { " add " | " update " }"
+.I SELECTOR
+.B dir
+.I DIR
+.RB "[ " ctx
+.IR CTX " ]"
+.RB "[ " mark
+.I MARK
+.RB "[ " mask
+.IR MASK " ] ]"
+.RB "[ " index
+.IR INDEX " ]"
+.RB "[ " ptype
+.IR PTYPE " ]"
+.RB "[ " action
+.IR ACTION " ]"
+.RB "[ " priority
+.IR PRIORITY " ]"
+.RB "[ " flag
+.IR FLAG-LIST " ]"
+.RI "[ " LIMIT-LIST " ] [ " TMPL-LIST " ]"
+
+.ti -8
+.BR "ip xfrm policy" " { " delete " | " get " }"
+.RI "{ " SELECTOR " | "
+.B index
+.IR INDEX " }"
+.B dir
+.I DIR
+.RB "[ " ctx
+.IR CTX " ]"
+.RB "[ " mark
+.I MARK
+.RB "[ " mask
+.IR MASK " ] ]"
+.RB "[ " ptype
+.IR PTYPE " ]"
+
+.ti -8
+.BR "ip xfrm policy" " { " deleteall " | " list " }"
+.RI "[ " SELECTOR " ]"
+.RB "[ " dir
+.IR DIR " ]"
+.RB "[ " index
+.IR INDEX " ]"
+.RB "[ " ptype
+.IR PTYPE " ]"
+.RB "[ " action
+.IR ACTION " ]"
+.RB "[ " priority
+.IR PRIORITY " ]"
+
+.ti -8
+.B "ip xfrm policy flush"
+.RB "[ " ptype
+.IR PTYPE " ]"
+
+.ti -8
+.B "ip xfrm policy count"
+
+.ti -8
+.IR SELECTOR " :="
+.RB "[ " src
+.IR ADDR "[/" PLEN "] ]"
+.RB "[ " dst
+.IR ADDR "[/" PLEN "] ]"
+.RB "[ " dev
+.IR DEV " ]"
+.RI "[ " UPSPEC " ]"
+
+.ti -8
+.IR UPSPEC " := "
+.BR proto " {"
+.IR PROTO " |"
+.br
+.RB "{ " tcp " | " udp " | " sctp " | " dccp " } [ " sport
+.IR PORT " ]"
+.RB "[ " dport
+.IR PORT " ] |"
+.br
+.RB "{ " icmp " | " ipv6-icmp " | " mobility-header " } [ " type
+.IR NUMBER " ]"
+.RB "[ " code
+.IR NUMBER " ] |"
+.br
+.BR gre " [ " key
+.RI "{ " DOTTED-QUAD " | " NUMBER " } ] }"
+
+.ti -8
+.IR DIR " := "
+.BR in " | " out " | " fwd
+
+.ti -8
+.IR PTYPE " := "
+.BR main " | " sub
+
+.ti -8
+.IR ACTION " := "
+.BR allow " | " block
+
+.ti -8
+.IR FLAG-LIST " := [ " FLAG-LIST " ] " FLAG
+
+.ti -8
+.IR FLAG " :="
+.BR localok " | " icmp
+
+.ti -8
+.IR LIMIT-LIST " := [ " LIMIT-LIST " ]"
+.B limit
+.I LIMIT
+
+.ti -8
+.IR LIMIT " :="
+.RB "{ " time-soft " | " time-hard " | " time-use-soft " | " time-use-hard " }"
+.IR "SECONDS" " |"
+.br
+.RB "{ " byte-soft " | " byte-hard " }"
+.IR SIZE " |"
+.br
+.RB "{ " packet-soft " | " packet-hard " }"
+.I COUNT
+
+.ti -8
+.IR TMPL-LIST " := [ " TMPL-LIST " ]"
+.B tmpl
+.I TMPL
+
+.ti -8
+.IR TMPL " := " ID
+.RB "[ " mode
+.IR MODE " ]"
+.RB "[ " reqid
+.IR REQID " ]"
+.RB "[ " level
+.IR LEVEL " ]"
+
+.ti -8
+.IR ID " :="
+.RB "[ " src
+.IR ADDR " ]"
+.RB "[ " dst
+.IR ADDR " ]"
+.RB "[ " proto
+.IR XFRM-PROTO " ]"
+.RB "[ " spi
+.IR SPI " ]"
+
+.ti -8
+.IR XFRM-PROTO " :="
+.BR esp " | " ah " | " comp " | " route2 " | " hao
+
+.ti -8
+.IR MODE " := "
+.BR transport " | " tunnel " | " ro " | " in_trigger " | " beet
+
+.ti -8
+.IR LEVEL " :="
+.BR required " | " use
+
+.ti -8
+.BR "ip xfrm monitor" " [ " all " |"
+.IR LISTofXFRM-OBJECTS " ]"
+
+.in -8
+.ad b
+
+.SH DESCRIPTION
+
+xfrm is an IP framework for transforming packets (such as encrypting
+their payloads). This framework is used to implement the IPsec protocol
+suite (with the
+.B state
+object operating on the Security Association Database, and the
+.B policy
+object operating on the Security Policy Database). It is also used for
+the IP Payload Compression Protocol and features of Mobile IPv6.
+
+.SS ip xfrm state add - add new state into xfrm
+
+.SS ip xfrm state update - update existing state in xfrm
+
+.SS ip xfrm state allocspi - allocate an SPI value
+
+.SS ip xfrm state delete - delete existing state in xfrm
+
+.SS ip xfrm state get - get existing state in xfrm
+
+.SS ip xfrm state deleteall - delete all existing state in xfrm
+
+.SS ip xfrm state list - print out the list of existing state in xfrm
+
+.SS ip xfrm state flush - flush all state in xfrm
+
+.SS ip xfrm state count - count all existing state in xfrm
+
+.TP
+.IR ID
+is specified by a source address, destination address,
+.RI "transform protocol " XFRM-PROTO ","
+and/or Security Parameter Index
+.IR SPI "."
+
+.TP
+.I XFRM-PROTO
+specifies a transform protocol:
+.RB "IPsec Encapsulating Security Payload (" esp "),"
+.RB "IPsec Authentication Header (" ah "),"
+.RB "IP Payload Compression (" comp "),"
+.RB "Mobile IPv6 Type 2 Routing Header (" route2 "), or"
+.RB "Mobile IPv6 Home Address Option (" hao ")."
+
+.TP
+.I ALGO-LIST
+specifies one or more algorithms
+.IR ALGO
+to use. Algorithm types include
+.RB "encryption (" enc "),"
+.RB "authentication (" auth "),"
+.RB "authentication with a specified truncation length (" auth-trunc "),"
+.RB "authenticated encryption with associated data (" aead "), and"
+.RB "compression (" comp ")."
+For each algorithm used, the algorithm type, the algorithm name
+.IR ALGO-NAME ","
+and the key
+.I ALGO-KEY
+must be specified. For
+.BR aead ","
+the Integrity Check Value length
+.I ALGO-ICV-LEN
+must additionally be specified.
+For
+.BR auth-trunc ","
+the signature truncation length
+.I ALGO-TRUNC-LEN
+must additionally be specified.
+
+.TP
+.I MODE
+specifies a mode of operation:
+.RB "IPsec transport mode (" transport "), "
+.RB "IPsec tunnel mode (" tunnel "), "
+.RB "Mobile IPv6 route optimization mode (" ro "), "
+.RB "Mobile IPv6 inbound trigger mode (" in_trigger "), or "
+.RB "IPsec ESP Bound End-to-End Tunnel Mode (" beet ")."
+
+.TP
+.I FLAG-LIST
+contains one or more of the following optional flags:
+.BR noecn ", " decap-dscp ", " nopmtudisc ", " wildrecv ", " icmp ", "
+.BR af-unspec ", or " align4 "."
+
+.TP
+.IR SELECTOR
+selects the traffic that will be controlled by the policy, based on the source
+address, the destination address, the network device, and/or
+.IR UPSPEC "."
+
+.TP
+.IR UPSPEC
+selects traffic by protocol. For the
+.BR tcp ", " udp ", " sctp ", or " dccp
+protocols, the source and destination port can optionally be specified.
+For the
+.BR icmp ", " ipv6-icmp ", or " mobility-header
+protocols, the type and code numbers can optionally be specified.
+For the
+.B gre
+protocol, the key can optionally be specified as a dotted-quad or number.
+Other protocols can be selected by name or number
+.IR PROTO "."
+
+.TP
+.I LIMIT-LIST
+sets limits in seconds, bytes, or numbers of packets.
+
+.TP
+.I ENCAP
+encapsulates packets with protocol
+.BR espinudp " or " espinudp-nonike ","
+.RI "using source port " SPORT ", destination port "  DPORT
+.RI ", and original address " OADDR "."
+
+.SS ip xfrm policy add - add a new policy
+
+.SS ip xfrm policy update - update an existing policy
+
+.SS ip xfrm policy delete - delete an existing policy
+
+.SS ip xfrm policy get - get an existing policy
+
+.SS ip xfrm policy deleteall - delete all existing xfrm policies
+
+.SS ip xfrm policy list - print out the list of xfrm policies
+
+.SS ip xfrm policy flush - flush policies
+
+.SS ip xfrm policy count - count existing policies
+
+.TP
+.IR SELECTOR
+selects the traffic that will be controlled by the policy, based on the source
+address, the destination address, the network device, and/or
+.IR UPSPEC "."
+
+.TP
+.IR UPSPEC
+selects traffic by protocol. For the
+.BR tcp ", " udp ", " sctp ", or " dccp
+protocols, the source and destination port can optionally be specified.
+For the
+.BR icmp ", " ipv6-icmp ", or " mobility-header
+protocols, the type and code numbers can optionally be specified.
+For the
+.B gre
+protocol, the key can optionally be specified as a dotted-quad or number.
+Other protocols can be selected by name or number
+.IR PROTO "."
+
+.TP
+.I DIR
+selects the policy direction as
+.BR in ", " out ", or " fwd "."
+
+.TP
+.I CTX
+sets the security context.
+
+.TP
+.I PTYPE
+can be
+.BR main " (default) or " sub "."
+
+.TP
+.I ACTION
+can be
+.BR allow " (default) or " block "."
+
+.TP
+.I PRIORITY
+is a number that defaults to zero.
+
+.TP
+.I FLAG-LIST
+contains one or both of the following optional flags:
+.BR local " or " icmp "."
+
+.TP
+.I LIMIT-LIST
+sets limits in seconds, bytes, or numbers of packets.
+
+.TP
+.I TMPL-LIST
+is a template list specified using
+.IR ID ", " MODE ", " REQID ", and/or " LEVEL ". "
+
+.TP
+.IR ID
+is specified by a source address, destination address,
+.RI "transform protocol " XFRM-PROTO ","
+and/or Security Parameter Index
+.IR SPI "."
+
+.TP
+.I XFRM-PROTO
+specifies a transform protocol:
+.RB "IPsec Encapsulating Security Payload (" esp "),"
+.RB "IPsec Authentication Header (" ah "),"
+.RB "IP Payload Compression (" comp "),"
+.RB "Mobile IPv6 Type 2 Routing Header (" route2 "), or"
+.RB "Mobile IPv6 Home Address Option (" hao ")."
+
+.TP
+.I MODE
+specifies a mode of operation:
+.RB "IPsec transport mode (" transport "), "
+.RB "IPsec tunnel mode (" tunnel "), "
+.RB "Mobile IPv6 route optimization mode (" ro "), "
+.RB "Mobile IPv6 inbound trigger mode (" in_trigger "), or "
+.RB "IPsec ESP Bound End-to-End Tunnel Mode (" beet ")."
+
+.TP
+.I LEVEL
+can be
+.BR required " (default) or " use "."
+
+.SS ip xfrm monitor - state monitoring for xfrm objects
+The xfrm objects to monitor can be optionally specified.
+
+.SH AUTHOR
+Manpage by David Ward
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index 1a73efa..ede3d12 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -1,4 +1,4 @@
-.TH IP 8 "17 January 2002" "iproute2" "Linux"
+.TH IP 8 "20 Dec 2011" "iproute2" "Linux"
 .SH NAME
 ip \- show / manipulate routing, devices, policy routing and tunnels
 .SH SYNOPSIS
@@ -14,7 +14,8 @@
 .ti -8
 .IR OBJECT " := { "
 .BR link " | " addr " | " addrlabel " | " route " | " rule " | " neigh " | "\
- tunnel " | " maddr " | "  mroute " | " monitor " }"
+ ntable " | " tunnel " | " tuntap " | " maddr " | "  mroute " | " mrule " | "\
+ monitor " | " xfrm " | " netns " | "  l2tp " }"
 .sp
 
 .ti -8
@@ -26,695 +27,6 @@
 .BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | "
 \fB\-o\fR[\fIneline\fR] }
 
-.ti -8
-.BI "ip link add link " DEVICE
-.RB "[ " name " ]"
-.I NAME
-.br
-.RB "[ " txqueuelen 
-.IR PACKETS " ]"
-.br
-.RB "[ " address
-.IR LLADDR " ]"
-.RB "[ " broadcast
-.IR LLADDR " ]"
-.br
-.RB "[ " mtu
-.IR MTU " ]"
-.br
-.BR type TYPE
-.RI "[ " ARGS " ]"
-
-.ti -8
-.IR TYPE " := [ "
-.BR vlan " | " maclan " | " can " ]"
-
-.ti -8
-.BI "ip link delete " DEVICE
-.BI type TYPE
-.RI "[ " ARGS " ]"
-
-.ti -8
-.BI "ip link set " DEVICE
-.RB "{ " up " | " down " | " arp " { " on " | " off " } |"
-.br
-.BR promisc " { " on " | " off " } |"
-.br
-.BR allmulticast " { " 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 " |"
-.br
-.B  netns
-.IR PID " |"
-.br
-.B alias
-.IR NAME  " |"
-.br
-.B vf
-.IR NUM " ["
-.B  mac
-.IR LLADDR " ] ["
-.B vlan
-.IR VLANID " [ "
-.B qos
-.IR VLAN-QOS " ] ] ["
-.B rate
-.IR TXRATE " ]"
-
-.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 " | " dadfailed " | " temporary " ]"
-
-.ti -8
-.BR "ip addrlabel" " { " add " | " del " } " prefix
-.BR PREFIX " [ "
-.B dev
-.IR DEV " ] [ "
-.B label
-.IR NUMBER " ]"
-
-.ti -8
-.BR "ip addrlabel" " { " list " | " flush " }"
-
-.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 TIME " ] [ "
-.B  rttvar
-.IR TIME " ] [ "
-.B  window
-.IR NUMBER " ] [ "
-.B  cwnd
-.IR NUMBER " ] [ "
-.B  ssthresh
-.IR REALM " ] [ "
-.B  realms
-.IR REALM " ] [ "
-.B  rto_min
-.IR TIME " ] [ "
-.B  initcwnd
-.IR NUMBER " ] [ "
-.B  initrwnd
-.IR NUMBER " ]"
-
-.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 NHFLAGS " := [ "
-.BR onlink " | " pervasive " ]"
-
-.ti -8
-.IR RTPROTO " := [ "
-.BR kernel " | " boot " | " static " |"
-.IR NUMBER " ]"
-
-.ti -8
-.B  ip rule
-.RB " [ " list " | " add " | " del " | " flush " ]"
-.I  SELECTOR ACTION
-
-.ti -8
-.IR SELECTOR " := [ "
-.B  from
-.IR PREFIX " ] [ "
-.B  to
-.IR PREFIX " ] [ "
-.B  tos
-.IR TOS " ] [ "
-.B  fwmark
-.IR FWMARK[/MASK] " ] [ "
-.B  iif
-.IR STRING " ] [ "
-.B  oif
-.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 " | " prl " }"
-.RI "[ " NAME " ]"
-.br
-.RB "[ " mode
-.IR MODE " ] [ "
-.B remote
-.IR ADDR " ] [ "
-.B  local
-.IR ADDR " ]"
-.br
-.RB "[ [" i "|" o "]" seq " ] [ [" i "|" o "]" key
-.IR KEY " ] [ "
-.RB "[" i "|" o "]" csum " ] ]"
-.br
-.RB "[ " encaplimit
-.IR ELIM " ]"
-.RB "[ " ttl
-.IR TTL " ]"
-.br
-.RB "[ " tos
-.IR TOS " ] [ "
-.B flowlabel
-.IR FLOWLABEL " ]"
-.br
-.RB "[ " prl-default
-.IR ADDR " ] [ "
-.B prl-nodefault
-.IR ADDR " ] [ "
-.B prl-delete
-.IR ADDR " ]"
-.br
-.RB "[ [" no "]" pmtudisc " ]"
-.RB "[ " dev
-.IR PHYS_DEV " ]"
-.RB "[ " "dscp inherit" " ]"
-
-.ti -8
-.IR MODE " := "
-.RB " { " ipip " | " gre " | " sit " | " isatap " | " ip6ip6 " | " ipip6 " | " any " }"
-
-.ti -8
-.IR ADDR " := { " IP_ADDRESS " |"
-.BR any " }"
-
-.ti -8
-.IR TOS " := { " NUMBER " |"
-.BR inherit " }"
-
-.ti -8
-.IR ELIM " := {
-.BR none " | "
-.IR 0 ".." 255 " }"
-
-.ti -8
-.ti -8
-.IR TTL " := { " 1 ".." 255 " | "
-.BR inherit " }"
-
-.ti -8
-.IR KEY " := { " DOTTED_QUAD " | " NUMBER " }"
-
-.ti -8
-.IR TIME " := " NUMBER "[s|ms|us|ns|j]"
-
-.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 " ]"
-
-.ti -8
-.BR "ip xfrm"
-.IR XFRM_OBJECT " { " COMMAND " }"
-
-.ti -8
-.IR XFRM_OBJECT " := { " state " | " policy " | " monitor " } "
-
-.ti -8
-.BR "ip xfrm state " { " add " | " update " } "
-.IR ID " [ "
-.IR XFRM_OPT " ] "
-.RB " [ " mode
-.IR MODE " ] "
-.br
-.RB " [ " reqid
-.IR REQID " ] "
-.RB " [ " seq
-.IR SEQ " ] "
-.RB " [ " replay-window
-.IR SIZE " ] "
-.br
-.RB " [ " flag
-.IR FLAG-LIST " ] "
-.RB " [ " encap
-.IR ENCAP " ] "
-.RB " [ " sel
-.IR SELECTOR " ] "
-.br
-.RB " [ "
-.IR LIMIT-LIST " ] "
-
-.ti -8
-.BR "ip xfrm state allocspi "
-.IR ID
-.RB " [ " mode
-.IR MODE " ] "
-.RB " [ " reqid
-.IR REQID " ] "
-.RB " [ " seq
-.IR SEQ " ] "
-.RB " [ " min
-.IR SPI
-.B max
-.IR SPI " ] "
-
-.ti -8
-.BR "ip xfrm state" " { " delete " | " get " } "
-.IR ID
-
-.ti -8
-.BR "ip xfrm state" " { " deleteall " | " list " } [ "
-.IR ID " ] "
-.RB " [ " mode
-.IR MODE " ] "
-.br
-.RB " [ " reqid
-.IR REQID " ] "
-.RB " [ " flag
-.IR FLAG_LIST " ] "
-
-.ti -8
-.BR "ip xfrm state flush" " [ " proto
-.IR XFRM_PROTO " ] "
-
-.ti -8
-.BR "ip xfrm state count"
-
-.ti -8
-.IR ID " := "
-.RB " [ " src
-.IR ADDR " ] "
-.RB " [ " dst
-.IR ADDR " ] "
-.RB " [ " proto
-.IR XFRM_PROTO " ] "
-.RB " [ " spi
-.IR SPI " ] "
-
-.ti -8
-.IR XFRM_PROTO " := "
-.RB " [ " esp " | " ah " | " comp " | " route2 " | " hao " ] "
-
-.ti -8
-.IR MODE " := "
-.RB " [ " transport " | " tunnel " | " ro " | " beet " ] "
-.B (default=transport)
-
-.ti -8
-.IR FLAG-LIST " := "
-.RI " [ " FLAG-LIST " ] " FLAG
-
-.ti -8
-.IR FLAG " := "
-.RB " [ " noecn " | " decap-dscp " | " wildrecv " ] "
-
-.ti -8
-.IR ENCAP " := " ENCAP-TYPE " " SPORT " " DPORT " " OADDR
-
-.ti -8
-.IR ENCAP-TYPE " := "
-.B espinudp
-.RB " | "
-.B espinudp-nonike
-
-.ti -8
-.IR ALGO-LIST " := [ "
-.IR ALGO-LIST " ] | [ "
-.IR ALGO " ] "
-
-.ti -8
-.IR ALGO " := "
-.IR ALGO_TYPE
-.IR ALGO_NAME
-.IR ALGO_KEY
-
-.ti -8
-.IR ALGO_TYPE " := "
-.RB " [ " enc " | " auth " | " comp " ] "
-
-.ti -8
-.IR SELECTOR " := "
-.B src
-.IR ADDR "[/" PLEN "]"
-.B dst
-.IR ADDR "[/" PLEN "]"
-.RI " [ " UPSPEC " ] "
-.RB " [ " dev
-.IR DEV " ] "
-
-.ti -8
-.IR UPSPEC " := "
-.B proto
-.IR PROTO " [[ "
-.B sport
-.IR PORT " ] "
-.RB " [ " dport
-.IR PORT " ] | "
-.br
-.RB " [ " type
-.IR NUMBER " ] "
-.RB " [ " code
-.IR NUMBER " ]] "
-
-.ti -8
-.IR LIMIT-LIST " := [ " LIMIT-LIST " ] |"
-.RB " [ "limit
-.IR LIMIT " ] "
-
-.ti -8
-.IR LIMIT " := "
-.RB " [ [" time-soft "|" time-hard "|" time-use-soft "|" time-use-hard "]"
-.IR SECONDS " ] | "
-.RB "[ ["byte-soft "|" byte-hard "]"
-.IR SIZE " ] | "
-.br
-.RB " [ ["packet-soft "|" packet-hard "]"
-.IR COUNT " ] "
-
-.ti -8
-.BR "ip xfrm policy" " { " add " | " update " } " " dir "
-.IR DIR
-.IR SELECTOR " [ "
-.BR index
-.IR INDEX " ] "
-.br
-.RB " [ " ptype
-.IR PTYPE " ] "
-.RB " [ " action
-.IR ACTION " ] "
-.RB " [ " priority
-.IR PRIORITY " ] "
-.br
-.RI " [ " LIMIT-LIST " ] [ "
-.IR TMPL-LIST " ] "
-
-.ti -8
-.BR "ip xfrm policy" " { " delete " | " get " } " " dir "
-.IR DIR " [ " SELECTOR " | "
-.BR index
-.IR INDEX
-.RB " ] "
-.br
-.RB " [ " ptype
-.IR PTYPE " ] "
-
-.ti -8
-.BR "ip xfrm policy" " { " deleteall " | " list " } "
-.RB " [ " dir
-.IR DIR " ] [ "
-.IR SELECTOR " ] "
-.br
-.RB " [ " index
-.IR INDEX " ] "
-.RB " [ " action
-.IR ACTION " ] "
-.RB " [ " priority
-.IR PRIORITY " ] "
-
-.ti -8
-.B "ip xfrm policy flush"
-.RB " [ " ptype
-.IR PTYPE " ] "
-
-.ti -8
-.B "ip xfrm count"
-
-.ti -8
-.IR PTYPE " := "
-.RB " [ " main " | " sub " ] "
-.B (default=main)
-
-.ti -8
-.IR DIR " := "
-.RB " [ " in " | " out " | " fwd " ] "
-
-.ti -8
-.IR SELECTOR " := "
-.B src
-.IR ADDR "[/" PLEN "]"
-.B dst
-.IR ADDR "[/" PLEN] " [ " UPSPEC
-.RB " ] [ " dev
-.IR DEV " ] "
-
-.ti -8
-.IR UPSPEC " := "
-.B proto
-.IR PROTO " [ "
-.RB " [ " sport
-.IR PORT " ] "
-.RB " [ " dport
-.IR PORT " ] | "
-.br
-.RB " [ " type
-.IR NUMBER " ] "
-.RB " [ " code
-.IR NUMBER " ] ] "
-
-.ti -8
-.IR ACTION " := "
-.RB " [ " allow " | " block " ]"
-.B (default=allow)
-
-.ti -8
-.IR LIMIT-LIST " := "
-.RB " [ "
-.IR LIMIT-LIST " ] | "
-.RB " [ " limit
-.IR LIMIT " ] "
-
-.ti -8
-.IR LIMIT " := "
-.RB " [ [" time-soft "|" time-hard "|" time-use-soft "|" time-use-hard "]"
-.IR SECONDS " ] | "
-.RB " [ [" byte-soft "|" byte-hard "]"
-.IR SIZE " ] | "
-.br [ "
-.RB "[" packet-soft "|" packet-hard "]"
-.IR NUMBER " ] "
-
-.ti -8
-.IR TMPL-LIST " := "
-.B " [ "
-.IR TMPL-LIST " ] | "
-.RB " [ " tmpl
-.IR TMPL " ] "
-
-.ti -8
-.IR TMPL " := "
-.IR ID " [ "
-.B mode
-.IR MODE " ] "
-.RB " [ " reqid
-.IR REQID " ] "
-.RB " [ " level
-.IR LEVEL " ] "
-
-.ti -8
-.IR ID " := "
-.RB " [ " src
-.IR ADDR " ] "
-.RB " [ " dst
-.IR ADDR " ] "
-.RB " [ " proto
-.IR XFRM_PROTO " ] "
-.RB " [ " spi
-.IR SPI " ] "
-
-.ti -8
-.IR XFRM_PROTO " := "
-.RB " [ " esp " | " ah " | " comp " | " route2 " | " hao " ] "
-
-.ti -8
-.IR MODE " := "
-.RB " [ " transport " | " tunnel " | " beet " ] "
-.B (default=transport)
-
-.ti -8
-.IR LEVEL " := "
-.RB " [ " required " | " use " ] "
-.B (default=required)
-
-.ti -8
-.BR "ip xfrm monitor" " [ " all " | "
-.IR LISTofOBJECTS " ] "
-
-.in -8
-.ad b
-
 .SH OPTIONS
 
 .TP
@@ -730,6 +42,12 @@
 As a rule, the information is statistics or some time values.
 
 .TP
+.BR "\-l" , " \-loops"
+Specify maximum number of loops the 'ip addr flush' logic
+will attempt before giving up.  The default is 10.
+Zero (0) means loop until all addresses are removed.
+
+.TP
 .BR "\-f" , " \-family"
 followed by protocol family identifier:
 .BR "inet" , " inet6"
@@ -786,10 +104,6 @@
 .I OBJECT
 
 .TP
-.B link
-- network device.
-
-.TP
 .B address
 - protocol (IP or IPv6) address on a device.
 
@@ -798,8 +112,40 @@
 - label configuration for protocol address selection.
 
 .TP
+.B l2tp
+- tunnel ethernet over IP (L2TPv3).
+
+.TP
+.B link
+- network device.
+
+.TP
+.B maddress
+- multicast address.
+
+.TP
+.B monitor
+- watch for netlink messages.
+
+.TP
+.B mroute
+- multicast routing cache entry.
+
+.TP
+.B mrule
+- rule in multicast routing policy database.
+
+.TP
 .B neighbour
-- ARP or NDISC cache entry.
+- manage ARP or NDISC cache entries.
+
+.TP
+.B netns
+- manage network namespaces.
+
+.TP
+.B ntable
+- manage the neighbor cache's operation.
 
 .TP
 .B route
@@ -810,20 +156,16 @@
 - rule in routing policy database.
 
 .TP
-.B maddress
-- multicast address.
-
-.TP
-.B mroute
-- multicast routing cache entry.
-
-.TP
 .B tunnel
 - tunnel over IP.
 
 .TP
+.B tuntap
+- manage TUN/TAP devices.
+
+.TP
 .B xfrm
-- framework for IPsec protocol.
+- manage IPSec policies.
 
 .PP
 The names of all objects may be written in full or
@@ -857,1727 +199,31 @@
 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 add - add virtual link
-
-.TP
-.BI link " DEVICE "
-specifies the physical device to act operate on.
-
-.I NAME
-specifies the name of the new virtual device.
-
-.I TYPE
-specifies the type of the new device.
-.sp
-Link types:
-
-.in +8
-.B vlan
-- 802.1q tagged virrtual LAN interface
-.sp
-.B macvlan
-- virtual interface base on link layer address (MAC)
-.sp
-.B can
-- Controller Area Network interface
-.in -8
-
-.SS ip link delete - delete virtual link
-.I DEVICE
-specifies the virtual  device to act operate on.
-.I TYPE
-specifies the type of the device.
-
-
-.TP
-.BI dev " DEVICE "
-specifies the physical device to act operate on.
-
-.SS ip link set - change device attributes
-
-.TP
-.BI dev " DEVICE "
-.I DEVICE
-specifies network device to operate on. When configuring SR-IOV Virtual Fuction
-(VF) devices, this keyword should specify the associated Physical Function (PF)
-device.
-
-.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" .
-
-.TP
-.BI netns " PID"
-move the device to the network namespace associated with the process
-.IR "PID".
-
-.TP
-.BI alias " NAME"
-give the device a symbolic name for easy reference.
-
-.TP
-.BI vf " NUM"
-specify a Virtual Function device to be configured. The associated PF device
-must be specified using the
-.B dev
-parameter.
-
-.in +8
-.BI mac " LLADDRESS"
-- change the station address for the specified VF. The
-.B vf
-parameter must be specified.
-
-.sp
-.BI vlan " VLANID"
-- change the assigned VLAN for the specified VF. When specified, all traffic
-sent from the VF will be tagged with the specified VLAN ID. Incoming traffic
-will be filtered for the specified VLAN ID, and will have all VLAN tags
-stripped before being passed to the VF. Setting this parameter to 0 disables
-VLAN tagging and filtering. The
-.B vf
-parameter must be specified.
-
-.sp
-.BI qos " VLAN-QOS"
-- assign VLAN QOS (priority) bits for the VLAN tag. When specified, all VLAN
-tags transmitted by the VF will include the specified priority bits in the
-VLAN tag. If not specified, the value is assumed to be 0. Both the
-.B vf
-and
-.B vlan
-parameters must be specified. Setting both
-.B vlan
-and
-.B qos
-as 0 disables VLAN tagging and filtering for the VF.
-
-.sp
-.BI rate " TXRATE"
-- change the allowed transmit bandwidth, in Mbps, for the specified VF.
-Setting this parameter to 0 disables rate limiting. The
-.B vf
-parameter must be specified.
-.in -8
-
-.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 have not yet passed duplicate
-address detection.
-
-.TP
-.B deprecated
-(IPv6 only) only list deprecated addresses.
-
-.TP
-.B dadfailed
-(IPv6 only) only list addresses which have failed duplicate
-address detection.
-
-.TP
-.B temporary
-(IPv6 only) only list temporary 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 addrlabel - protocol address label management.
-
-IPv6 address label is used for address selection
-described in RFC 3484.  Precedence is managed by userspace,
-and only label is stored in kernel.
-
-.SS ip addrlabel add - add an address label
-the command adds an address label entry to the kernel.
-.TP
-.BI prefix " PREFIX"
-.TP
-.BI dev " DEV"
-the outgoing interface.
-.TP
-.BI label " NUMBER"
-the label for the prefix.
-0xffffffff is reserved.
-.SS ip addrlabel del - delete an address label
-the command deletes an address label entry in the kernel.
-.B Arguments:
-coincide with the arguments of
-.B ip addrlabel add
-but label is not required.
-.SS ip addrlabel list - list address labels
-the command show contents of address labels.
-.SS ip addrlabel flush - flush address labels
-the command flushes the contents of address labels and it does not restore default settings.
-.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 2^31 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.
-Values (0, 253, 254, and 255) are reserved for built-in use.
-
-.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 " TIME"
-the initial RTT ('Round Trip Time') estimate. If no suffix is
-specified the units are raw values passed directly to the
-routing code to maintain compatability with previous releases.
-Otherwise if a suffix of s, sec or secs is used to specify
-seconds; ms, msec or msecs to specify milliseconds; us, usec
-or usecs to specify microseconds; ns, nsec or nsecs to specify
-nanoseconds; j, hz or jiffies to specify jiffies, the value is
-converted to what the routing code expects.
-
-
-.TP
-.BI rttvar " TIME " "(2.3.15+ only)"
-the initial RTT variance estimate. Values are specified as with
-.BI rtt
-above.
-
-.TP
-.BI rto_min " TIME " "(2.6.23+ only)"
-the minimum TCP Retransmission TimeOut to use when communicating with this
-destination.  Values are specified as with
-.BI rtt
-above.
-
-.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 initcwnd " NUMBER " "(2.5.70+ only)"
-the initial congestion window size for connections to this destination.
-Actual window size is this value multiplied by the MSS
-(``Maximal Segment Size'') for same connection. The default is
-zero, meaning to use the values specified in RFC2414.
-
-.TP
-.BI initrwnd " NUMBER " "(2.6.33+ only)"
-the initial receive window size for connections to this destination.
-Actual window size is this value multiplied by the MSS of the connection.
-The default value is zero, meaning to use Slow Start value.
-
-.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.
-
-.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 oif " NAME"
-select the outgoing device to match.  The outgoing interface is only
-available for packets originating from local sockets that are bound to
-a device.
-
-.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.
-The options preference and order are synonyms with priority.
-
-.TP
-.BI table " TABLEID"
-the routing table identifier to lookup if the rule selector matches.
-It is also possible to use lookup instead of table.
-
-.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.
-Using map-to instead of nat means the same thing.
-
-.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 flush - also dumps all the deleted rules.
-This command has no arguments.
-
-.SS ip rule show - list rules
-This command has no arguments.
-The options list or lst are synonyms with show.
-
-.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 IP packets and then
-sending them over the IP infrastructure.
-The encapulating (or outer) address family is specified by the
-.B -f
-option.  The default is IPv4.
-
-.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. Available modes depend on the encapsulating address family.
-.br
-Modes for IPv4 encapsulation available:
-.BR ipip ", " sit ", " isatap " and " gre "."
-.br
-Modes for IPv6 encapsulation available:
-.BR ip6ip6 ", " ipip6 " and " any "."
-
-.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 for IPv4 tunnels is:
-.BR "inherit" .
-The default value for IPv6 tunnels is:
-.BR "64" .
-
-
-.TP
-.BI tos " T"
-.TP
-.BI dsfield " T"
-.TP
-.BI tclass " T"
-set a fixed TOS (or traffic class in IPv6)
-.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.
-
-.TP
-.BR "dscp inherit"
-.RB ( " only IPv6 tunnels " )
-Inherit DS field between inner and outer header.
-
-.TP
-.BI encaplim " ELIM"
-.RB ( " only IPv6 tunnels " )
-set a fixed encapsulation limit.  Default is 4.
-
-.TP
-.BI flowlabel " FLOWLABEL"
-.RB ( " only IPv6 tunnels " )
-set a fixed flowlabel.
-
-.SS ip tunnel prl - potential router list (ISATAP only)
-
-.TP
-.BI dev " NAME"
-mandatory device name.
-
-.TP
-.BI prl-default " ADDR"
-.TP
-.BI prl-nodefault " ADDR"
-.TP
-.BI prl-delete " ADDR"
-.RB "Add or delete " ADDR
-as a potential router or default router.
-
-.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 ip xfrm - setting xfrm
-xfrm is an IP framework, which can transform format of the datagrams,
-.br
-i.e. encrypt the packets with some algorithm. xfrm policy and xfrm state
-are associated through templates
-.IR TMPL_LIST "."
-This framework is used as a part of IPsec protocol.
-
-.SS ip xfrm state add - add new state into xfrm
-
-.SS ip xfrm state update - update existing xfrm state
-
-.SS ip xfrm state allocspi - allocate SPI value
-
-.TP
-.I MODE
-is set as default to
-.BR transport ","
-but it could be set to
-.BR tunnel "," ro " or " beet "."
-
-.TP
-.I FLAG-LIST
-contains one or more flags.
-
-.TP
-.I FLAG
-could be set to
-.BR noecn ", " decap-dscp " or " wildrecv "."
-
-.TP
-.I ENCAP
-encapsulation is set to encapsulation type
-.IR ENCAP-TYPE ", source port " SPORT ", destination port "  DPORT " and " OADDR "."
-
-.TP
-.I ENCAP-TYPE
-could be set to
-.BR espinudp " or " espinudp-nonike "."
-
-.TP
-.I ALGO-LIST
-contains one or more algorithms
-.I ALGO
-which depend on the type of algorithm set by
-.IR ALGO_TYPE "."
-It can be used these algoritms
-.BR enc ", " auth " or " comp "."
-
-.SS ip xfrm policy add - add a new policy
-
-.SS ip xfrm policy update - update an existing policy
-
-.SS ip xfrm policy delete - delete existing policy
-
-.SS ip xfrm policy get - get existing policy
-
-.SS ip xfrm policy deleteall - delete all existing xfrm policy
-
-.SS ip xfrm policy list - print out the list of xfrm policy
-
-.SS ip xfrm policy flush - flush policies
-It can be flush
-.BR all
-policies or only those specified with
-.BR ptype "."
-
-.TP
-.BI dir " DIR "
-directory could be one of these:
-.BR "inp", " out " or " fwd".
-
-.TP
-.IR SELECTOR
-selects for which addresses will be set up the policy. The selector
-is defined by source and destination address.
-
-.TP
-.IR UPSPEC
-is defined by source port
-.BR sport ", "
-destination port
-.BR dport ", " type
-as number and
-.B code
-also number.
-
-.TP
-.BI dev " DEV "
-specify network device.
-
-.TP
-.BI index " INDEX "
-the number of indexed policy.
-
-.TP
-.BI ptype " PTYPE "
-type is set as default on
-.BR "main" ,
-could be switch on
-.BR "sub" .
-
-.TP
-.BI action " ACTION "
-is set as default on
-.BR "allow".
-It could be switch on
-.BR "block".
-
-.TP
-.BI priority " PRIORITY "
-priority is a number. Default priority is set on zero.
-
-.TP
-.IR LIMIT-LIST
-limits are set in seconds, bytes or numbers of packets.
-
-.TP
-.IR TMPL-LIST
-template list is based on
-.IR ID ","
-.BR mode ", " reqid " and " level ". "
-
-.TP
-.IR ID
-is specified by source address, destination address,
-.I proto
-and value of
-.IR spi "."
-
-.TP
-.IR XFRM_PROTO
-values:
-.BR esp ", " ah ", " comp ", " route2 " or " hao "."
-
-.TP
-.IR MODE
-is set as default on
-.BR transport ","
-but it could be set on
-.BR tunnel " or " beet "."
-
-.TP
-.IR LEVEL
-is set as default on
-.BR required
-and the other choice is
-.BR use "."
-
-.TP
-.IR UPSPEC
-is specified by
-.BR sport ", "
-.BR dport ", " type
-and
-.B code
-(NUMBER).
-
-.SS ip xfrm monitor - is used for listing all objects or defined group of them.
-The
-.B xfrm monitor
-can monitor the policies for all objects or defined group of them.
-
 .SH HISTORY
 .B ip
 was written by Alexey N. Kuznetsov and added in Linux 2.2.
 .SH SEE ALSO
-.BR tc (8)
+.BR ip-address (8),
+.BR ip-addrlabel (8),
+.BR ip-l2tp (8),
+.BR ip-link (8),
+.BR ip-maddress (8),
+.BR ip-monitor (8),
+.BR ip-mroute (8),
+.BR ip-neighbour (8),
+.BR ip-netns (8),
+.BR ip-ntable (8),
+.BR ip-route (8),
+.BR ip-rule (8),
+.BR ip-tunnel (8),
+.BR ip-xfrm (8)
 .br
 .RB "IP Command reference " ip-cref.ps
-.br
-.RB "IP tunnels " ip-cref.ps
-.br
-.RB "User documentation at " http://lartc.org/ ", but please direct bugreports and patches to: " <netdev@vger.kernel.org>
+.SH REPORTING BUGS
+Report bug to the Network Developers mailing list
+.B <netdev@vger.kernel.org>
+where the development and maintenance is primarily done.
+You do not have to be subscribed to the list to send a message there.
 
 .SH AUTHOR
-Original Manpage  by Michail Litvak <mci@owl.openwall.com>
+Original Manpage by Michail Litvak <mci@owl.openwall.com>
diff --git a/man/man8/nstat.8 b/man/man8/nstat.8
new file mode 100644
index 0000000..c703cc8
--- /dev/null
+++ b/man/man8/nstat.8
@@ -0,0 +1 @@
+.so man8/rtacct.8
diff --git a/man/man8/routef.8 b/man/man8/routef.8
new file mode 100644
index 0000000..c2a7059
--- /dev/null
+++ b/man/man8/routef.8
@@ -0,0 +1 @@
+.so man8/routel.8
diff --git a/man/man8/rtstat.8 b/man/man8/rtstat.8
new file mode 100644
index 0000000..080e2b2
--- /dev/null
+++ b/man/man8/rtstat.8
@@ -0,0 +1 @@
+.so man8/lnstat.8
diff --git a/man/man8/ss.8 b/man/man8/ss.8
index 015feb3..0b9a8c4 100644
--- a/man/man8/ss.8
+++ b/man/man8/ss.8
@@ -12,6 +12,9 @@
 It can display more TCP and state informations than other tools.
 
 .SH OPTIONS
+When no option is used ss displays a list of 
+open non-listening TCP sockets that have established connection.
+.TP
 These programs follow the usual GNU command line syntax, with long
 options starting with two dashes (`-').
 A summary of options is included below.
@@ -23,16 +26,16 @@
 Output version information.
 .TP
 .B \-n, \-\-numeric
-Do now try to resolve service names.
+Do not try to resolve service names.
 .TP
 .B \-r, \-\-resolve
 Try to resolve numeric address/ports.
 .TP
 .B \-a, \-\-all
-Display all sockets.
+Display both listening and non-listening (for TCP this means established connections) sockets.
 .TP
 .B \-l, \-\-listening
-Display listening sockets.
+Display only listening sockets (these are omitted by default).
 .TP
 .B \-o, \-\-options
 Show timer information.
@@ -61,33 +64,33 @@
 Display only IP version 6 sockets (alias for -f inet6).
 .TP
 .B \-0, \-\-packet
-Display PACKET sockets.
+Display PACKET sockets (alias for -f link).
 .TP
 .B \-t, \-\-tcp
-Display only TCP sockets.
+Display TCP sockets.
 .TP
 .B \-u, \-\-udp
-Display only UDP sockets.
+Display UDP sockets.
 .TP
 .B \-d, \-\-dccp
-Display only DCCP sockets.
+Display DCCP sockets.
 .TP
 .B \-w, \-\-raw
-Display only RAW sockets.
+Display RAW sockets.
 .TP
 .B \-x, \-\-unix
-Display only Unix domain sockets.
+Display Unix domain sockets (alias for -f unix).
 .TP
 .B \-f FAMILY, \-\-family=FAMILY
 Display sockets of type FAMILY.
 Currently the following families are supported: unix, inet, inet6, link, netlink.
 .TP
-.B \-A QUERY, \-\-query=QUERY
+.B \-A QUERY, \-\-query=QUERY, \-\-socket=QUERY
 List of socket tables to dump, separated by commas. The following identifiers
 are understood: all, inet, tcp, udp, raw, unix, packet, netlink, unix_dgram,
 unix_stream, packet_raw, packet_dgram.
 .TP
-.B \-D FILE
+.B \-D FILE, \-\-diag=FILE
 Do not display anything, just dump raw information about TCP sockets to FILE after applying filters. If FILE is - stdout is used.
 .TP
 .B \-F FILE, \-\-filter=FILE
diff --git a/man/man8/tc-choke.8 b/man/man8/tc-choke.8
new file mode 100644
index 0000000..620c7f6
--- /dev/null
+++ b/man/man8/tc-choke.8
@@ -0,0 +1,63 @@
+.TH TC 8 "August 2011" "iproute2" "Linux"
+.SH NAME
+choke \- choose and keep scheduler
+.SH SYNOPSIS
+.B tc qdisc ... choke
+.B limit
+packets
+.B min
+packets
+.B max
+packets
+.B avpkt
+bytes
+.B burst
+packets
+.B [ ecn ] [ bandwidth
+rate
+.B ] probability
+chance
+
+.SH DESCRIPTION
+
+CHOKe (CHOose and Keep for responsive flows, CHOose and Kill for unresponsive flows)
+is a classless qdisc designed to both identify and penalize flows that monopolize the
+queue.  CHOKe is a variation of RED, and the configuration is similar to RED.
+
+.SH ALGORITHM
+Once the queue hits a certain average length, a random packet is drawn from the
+queue.  If both the to-be-queued and the drawn packet belong to the same flow,
+both packets are dropped.  Otherwise, if the queue length is still below the maximum length,
+the new packet has a configurable chance of being marked (which may mean dropped).
+If the queue length exceeds
+.B max
+, the new packet will always be marked (or dropped).
+If the queue length exceeds
+.B limit
+, the new packet is always dropped.
+
+The marking probability computation is the same as used by the RED qdisc.
+
+.SH PARAMETERS
+The parameters are the same as for RED, except that RED uses bytes whereas choke
+counts packets. See
+.BR tc-red (8)
+for a description.
+
+.SH SOURCE
+.TP
+o
+R. Pan, B. Prabhakar, and K. Psounis, "CHOKe, A Stateless
+Active Queue Management Scheme for Approximating Fair Bandwidth Allocation",
+IEEE INFOCOM, 2000.
+.TP
+o
+A. Tang, J. Wang, S. Low, "Understanding CHOKe: Throughput and Spatial
+Characteristics", IEEE/ACM Transactions on Networking, 2004
+
+.SH SEE ALSO
+.BR tc (8),
+.BR tc-red (8)
+
+.SH AUTHOR
+sched_choke was contributed by Stephen Hemminger.
diff --git a/man/man8/tc-hfsc.8 b/man/man8/tc-hfsc.8
new file mode 100644
index 0000000..c5ff331
--- /dev/null
+++ b/man/man8/tc-hfsc.8
@@ -0,0 +1,61 @@
+.TH HFSC 8 "31 October 2011" iproute2 Linux
+.
+.SH NAME
+HFSC \- Hierarchical Fair Service Curve's control under linux
+.
+.SH SYNOPSIS
+.nf
+tc qdisc add ... hfsc [ \fBdefault\fR CLASSID ]
+
+tc class add ... hfsc [ [ \fBrt\fR SC ] [ \fBls\fR SC ] | [ \fBsc\fR SC ] ] [ \fBul\fR SC ]
+
+\fBrt\fR : realtime service curve
+\fBls\fR : linkshare service curve
+\fBsc\fR : rt+ls service curve
+\fBul\fR : upperlimit service curve
+
+\(bu at least one of \fBrt\fR, \fBls\fR or \fBsc\fR must be specified
+\(bu \fBul\fR can only be specified with \fBls\fR or \fBsc\fR
+.
+.IP "SC := [ [ \fBm1\fR BPS ] \fBd\fR SEC ] \fBm2\fR BPS"
+\fBm1\fR : slope of the first segment
+\fBd\fR  : x\-coordinate of intersection
+\fBm2\fR : slope of the second segment
+.PP
+.IP "SC := [ [ \fBumax\fR BYTE ] \fBdmax\fR SEC ] \fBrate\fR BPS"
+\fBumax\fR : maximum unit of work
+\fBdmax\fR : maximum delay
+\fBrate\fR : rate
+.PP
+.fi
+For description of BYTE, BPS and SEC \- please see \fBUNITS\fR
+section of \fBtc\fR(8).
+.
+.SH DESCRIPTION (qdisc)
+HFSC qdisc has only one optional parameter \- \fBdefault\fR.  CLASSID specifies
+the minor part of the default classid, where packets not classified by other
+means (e.g. u32 filter, CLASSIFY target of iptables) will be enqueued. If
+\fBdefault\fR is not specified, unclassified packets will be dropped.
+.
+.SH DESCRIPTION (class)
+HFSC class is used to create a class hierarchy for HFSC scheduler. For
+explanation of the algorithm, and the meaning behind \fBrt\fR, \fBls\fR,
+\fBsc\fR and \fBul\fR service curves \- please refer to \fBtc\-hfsc\fR(7).
+
+As you can see in \fBSYNOPSIS\fR, service curve (SC) can be specified in two
+ways. Either as maximum delay for certain amount of work, or as a bandwidth
+assigned for certain amount of time. Obviously, \fBm1\fR is simply
+\fBumax\fR/\fBdmax\fR.
+
+Both \fBm2\fR and \fBrate\fR are mandatory. If you omit other
+parameters, you will specify linear service curve.
+.
+.SH "SEE ALSO"
+.
+\fBtc\fR(8), \fBtc\-hfsc\fR(7), \fBtc\-stab\fR(8)
+
+Please direct bugreports and patches to: <net...@vger.kernel.org>
+.
+.SH "AUTHOR"
+.
+Manpage created by Michal Soltys (sol...@ziu.info)
diff --git a/man/man8/tc-netem.8 b/man/man8/tc-netem.8
new file mode 100644
index 0000000..39f8454
--- /dev/null
+++ b/man/man8/tc-netem.8
@@ -0,0 +1,194 @@
+.TH NETEM 8 "25 November 2011" "iproute2" "Linux"
+.SH NAME
+NetEm \- Network Emulator
+.SH SYNOPSIS
+.B "tc qdisc ... dev" 
+.IR DEVICE " ] "
+.BR "add netem" 
+.I OPTIONS
+
+.IR OPTIONS " := [ " LIMIT " ] [ " DELAY " ] [ " LOSS \
+" ] [ " CORRUPT " ] [ " DUPLICATION " ] [ " REORDERING " ][ " RATE " ]"
+
+.IR LIMIT " := "
+.B limit
+.I packets
+
+.IR DELAY " := "
+.BI delay 
+.IR TIME " [ " JITTER " [ " CORRELATION " ]]]"
+.br
+       [ 
+.BR distribution " { "uniform " | " normal " | " pareto " |  " paretonormal " } ]"
+
+.IR LOSS " := "
+.BR loss " { "
+.BI random 
+.IR PERCENT " [ " CORRELATION " ]  |"
+.br
+.RB "               " state
+.IR p13 " [ " p31 " [ " p32 " [ " p23 " [ " p14 "]]]] |"
+.br
+.RB "               " gemodel
+.IR p " [ " r " [ " 1-h " [ " 1-k " ]]]"
+.BR " }"
+
+.IR CORRUPT " := "
+.B corrupt
+.IR PERCENT " [ " CORRELATION " ]]"
+
+.IR DUPLICATION " := "
+.B duplicate
+.IR PERCENT " [ " CORRELATION " ]]"
+
+.IR REORDERING " := "
+.B reorder
+.IR PERCENT " [ " CORRELATION " ] [ "
+.B gap 
+.IR DISTANCE " ]"
+
+.IR RATE " := "
+.B rate
+.IR RATE " [ " PACKETOVERHEAD " [ " CELLSIZE " [ " CELLOVERHEAD " ]]]]"
+	
+
+.SH DESCRIPTION
+NetEm is an enhancement of the Linux traffic control facilities
+that allow to add delay, packet loss, duplication and more other
+characteristics to packets outgoing from a selected network
+interface. NetEm is built using the existing Quality Of Service (QOS)
+and Differentiated Services (diffserv) facilities in the Linux
+kernel.
+
+.SH netem OPTIONS
+netem has the following options:
+
+.SS limit packets
+
+limits the effect of selected options to the indicated number of next packets.
+
+.SS delay
+adds the chosen delay to the packets outgoing to chosen network interface. The
+optional parameters allows to introduce a delay variation and a correlation.
+Delay and jitter values are expressed in ms while correlation is percentage.
+
+.SS distribution
+allow the user to choose the delay distribution. If not specified, the default
+distribution is Normal. Additional parameters allow to consider situations in
+which network has variable delays depending on traffic flows concurring on the
+same path, that causes several delay peaks and a tail.
+
+.SS loss random
+adds an independent loss probability to the packets outgoing from the chosen
+network interface. It is also possible to add a correlation, but this option
+is now deprecated due to the noticed bad behavior.
+
+.SS loss state
+adds packet losses according to the 4-state Markov using the transition
+probabilities as input parameters. The parameter p13 is mandatory and if used
+alone corresponds to the Bernoulli model. The optional parameters allows to
+extend the model to 2-state (p31), 3-state (p23 and p32) and 4-state (p14).
+State 1 corresponds to good reception, State 4 to independent losses, State 3
+to burst losses and State 2 to good reception within a burst.
+
+.SS loss gemodel
+adds packet losses according to the Gilbert-Elliot loss model or its special
+cases (Gilbert, Simple Gilbert and Bernoulli). To use the Bernoulli model, the
+only needed parameter is p while the others will be set to the default
+values r=1-p, 1-h=1 and 1-k=0. The parameters needed for the Simple Gilbert
+model are two (p and r), while three parameters (p, r, 1-h) are needed for the
+Gilbert model and four (p, r, 1-h and 1-k) are needed for the Gilbert-Elliot
+model. As known, p and r are the transition probabilities between the bad and
+the good states, 1-h is the loss probability in the bad state and 1-k is the
+loss probability in the good state.
+
+.SS corrupt
+allows the emulation of random noise introducing an error in a random position
+for a chosen percent of packets. It is also possible to add a correlation
+through the proper parameter.
+
+.SS duplicate
+using this option the chosen percent of packets is duplicated before queuing
+them. It is also possible to add a correlation through the proper parameter.
+
+.SS reorder
+to use reordering, a delay option must be specified. There are two ways to use
+this option (assuming 'delay 10ms' in the options list).
+
+.B "reorder "
+.I 25% 50%
+.B "gap"
+.I 5
+.br
+in this first example, the first 4 (gap - 1) packets are delayed by 10ms and
+subsequent packets are sent immediately with a probability of 0.25 (with
+correlation of 50% ) or delayed with a probability of 0.75. After a packet is
+reordered, the process restarts i.e. the next 4 packets are delayed and
+subsequent packets are sent immediately or delayed based on reordering
+probability. To cause a repeatable pattern where every 5th packet is reordered
+reliably, a reorder probability of 100% can be used.
+
+.B reorder
+.I 25% 50%
+.br
+in this second example 25% of packets are sent immediately (with correlation of
+50%) while the others are delayed by 10 ms.
+
+.SS rate
+delay packets based on packet size and is a replacement for 
+.IR TBF .
+Rate can be
+specified in common units (e.g. 100kbit). Optional 
+.I PACKETOVERHEAD 
+(in bytes) specify an per packet overhead and can be negative. A positive value can be
+used to simulate additional link layer headers. A negative value can be used to
+artificial strip the Ethernet header (e.g. -14) and/or simulate a link layer
+header compression scheme. The third parameter - an unsigned value - specify
+the cellsize. Cellsize can be used to simulate link layer schemes. ATM for
+example has an payload cellsize of 48 bytes and 5 byte per cell header. If a
+packet is 50 byte then ATM must use two cells: 2 * 48 bytes payload including 2
+* 5 byte header, thus consume 106 byte on the wire.  The last optional value
+.I CELLOVERHEAD 
+can be used to specify per cell overhead - for our ATM example 5.
+.I CELLOVERHEAD
+can be negative, but use negative values with caution.
+
+Note that rate throttling is limited by several factors: the kernel clock
+granularity avoid a perfect shaping at a specific level. This will show up in
+an artificial packet compression (bursts). Another influence factor are network
+adapter buffers which can also add artificial delay.
+
+.SH LIMITATIONS
+The main known limitation of Netem are related to timer granularity, since
+Linux is not a real-time operating system.
+
+.SH EXAMPLES
+.PP
+tc qdisc add dev eth0 root netem rate 5kbit 20 100 5
+.RS 4
+delay all outgoing packets on device eth0 with a rate of 5kbit, a per packet
+overhead of 20 byte, a cellsize of 100 byte and a per celloverhead of 5 byte:
+.RE
+
+.SH SOURCES
+.IP " 1. " 4
+Hemminger S. , "Network Emulation with NetEm", Open Source Development Lab,
+April 2005
+(http://devresources.linux-foundation.org/shemminger/netem/LCA2005_paper.pdf)
+
+.IP " 2. " 4
+Netem page from Linux foundation, (http://www.linuxfoundation.org/en/Net:Netem)
+
+.IP " 3. " 4
+Salsano S., Ludovici F., Ordine A., "Definition of a general and intuitive loss
+model for packet networks and its implementation in the Netem module in the
+Linux kernel", available at http://netgroup.uniroma2.it/NetemCLG
+
+.SH SEE ALSO
+.BR tc (8),
+.BR tc-tbf (8)
+
+.SH AUTHOR
+Netem was written by Stephen Hemminger at Linux foundation and is based on NISTnet.
+This manpage was created by Fabio Ludovici <fabio.ludovici at yahoo dot it> and
+Hagen Paul Pfeifer <hagen@jauu.net>
diff --git a/man/man8/tc-pfifo.8 b/man/man8/tc-pfifo.8
deleted file mode 120000
index 70a54af..0000000
--- a/man/man8/tc-pfifo.8
+++ /dev/null
@@ -1 +0,0 @@
-tc-bfifo.8
\ No newline at end of file
diff --git a/man/man8/tc-pfifo.8 b/man/man8/tc-pfifo.8
new file mode 100644
index 0000000..ed23850
--- /dev/null
+++ b/man/man8/tc-pfifo.8
@@ -0,0 +1 @@
+.so man8/tc-bfifo.8
diff --git a/man/man8/tc-red.8 b/man/man8/tc-red.8
index d02b411..f410d15 100644
--- a/man/man8/tc-red.8
+++ b/man/man8/tc-red.8
@@ -5,18 +5,19 @@
 .B tc qdisc ... red
 .B limit 
 bytes
-.B min 
+.B [ min 
 bytes 
-.B max 
+.B ] [ max 
 bytes 
-.B avpkt
+.B ] avpkt
 bytes
-.B burst 
+.B [ burst 
 packets
-.B [ ecn ] [ bandwidth
+.B ] [ ecn ] [ harddrop] [ bandwidth
 rate
-.B ] probability
+.B ] [ probability
 chance
+.B ] [ adaptive ]
 
 .SH DESCRIPTION
 Random Early Detection is a classless qdisc which manages its queue size
@@ -34,7 +35,7 @@
 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
+The goal is to 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.
 
@@ -66,7 +67,10 @@
 .SH PARAMETERS
 .TP 
 min
-Average queue size at which marking becomes a possibility.
+Average queue size at which marking becomes a possibility. Defaults to
+.B max
+/3
+
 .TP 
 max
 At this average queue size, the marking probability is maximal. Should be at
@@ -74,11 +78,14 @@
 .B min
 to prevent synchronous retransmits, higher for low 
 .B min.
+Default to 
+.B limit
+/4
 .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).
+respectively). Default : 0.02
 .TP 
 limit
 Hard limit on the real (not average) queue size in bytes. Further packets
@@ -99,7 +106,7 @@
 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.
+that RED will shape for you! Optional. Default : 10Mbit
 .TP
 ecn
 As mentioned before, RED can either 'mark' or 'drop'. Explicit Congestion
@@ -109,10 +116,30 @@
 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.
+bytes. Recommended.
+.TP
+harddrop
+If average flow queue size is above
+.B max
+bytes, this parameter forces a drop instead of ecn marking.
+.TP
+adaptive
+(Added in linux-3.3) Sets RED in adaptive mode as described in http://icir.org/floyd/papers/adaptiveRed.pdf
+.nf
+Goal of Adaptive RED is to make 'probability' dynamic value between 1% and 50% to reach the target average queue : 
+.B (max - min) / 2
+.fi
+
+.SH EXAMPLE
+
+.P
+# tc qdisc add dev eth0 parent 1:1 handle 10: red 
+ limit 400000 min 30000 max 90000 avpkt 1000 
+ burst 55 ecn adaptive bandwidth 10Mbit
 
 .SH SEE ALSO
-.BR tc (8)
+.BR tc (8),
+.BR tc-choke (8)
 
 .SH SOURCES
 .TP 
@@ -122,10 +149,14 @@
 .TP 
 o
 Some changes to the algorithm by Alexey N. Kuznetsov.
+.TP
+o
+Adaptive RED  : http://icir.org/floyd/papers/adaptiveRed.pdf
 
 .SH AUTHORS
 Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>,  Alexey Makarenko
-<makar@phoenix.kharkov.ua>, J Hadi Salim <hadi@nortelnetworks.com>.  
+<makar@phoenix.kharkov.ua>, J Hadi Salim <hadi@nortelnetworks.com>,
+Eric Dumazet <eric.dumazet@gmail.com>.  
 This manpage maintained by bert hubert <ahu@ds9a.nl>
 
 
diff --git a/man/man8/tc-sfb.8 b/man/man8/tc-sfb.8
new file mode 100644
index 0000000..e7634d2
--- /dev/null
+++ b/man/man8/tc-sfb.8
@@ -0,0 +1,213 @@
+.TH SFB 8 "August 2011" "iproute2" "Linux"
+.SH NAME
+sfb \- Stochastic Fair Blue
+.SH SYNOPSIS
+.B tc qdisc ... blue
+.B rehash
+milliseconds
+.B db
+milliseconds
+.B limit
+packets
+.B max
+packets
+.B target
+packets
+.B increment
+float
+.B decrement
+float
+.B penalty_rate
+packets per second
+.B penalty_burst
+packets
+
+.SH DESCRIPTION
+Stochastic Fair Blue is a classless qdisc to manage congestion based on
+packet loss and link utilization history while trying to prevent
+non-responsive flows (i.e. flows that do not react to congestion marking
+or dropped packets) from impacting performance of responsive flows.
+Unlike RED, where the marking probability has to be configured, BLUE
+tries to determine the ideal marking probability automatically.
+
+.SH ALGORITHM
+
+The
+.B BLUE
+algorithm maintains a probability which is used to mark or drop packets
+that are to be queued.  If the queue overflows, the mark/drop probability
+is increased. If the queue becomes empty, the probability is decreased. The
+.B Stochastic Fair Blue
+(SFB) algorithm is designed to protect TCP flows against non-responsive flows.
+
+This SFB implementation maintains 8 levels of 16 bins each for accounting.
+Each flow is mapped into a bin of each level using a per-level hash value.
+
+Every bin maintains a marking probability, which gets increased or decreased
+based on bin occupancy.  If the number of packets exceeds the size of that
+bin, the marking probability is increased.  If the number drops to zero, it
+is decreased.
+
+The marking probability is based on the minimum value of all bins a flow is
+mapped into, thus, when a flow does not respond to marking or gradual packet
+drops, the marking probability quickly reaches one.
+
+In this case, the flow is rate-limited to
+.B penalty_rate
+packets per second.
+
+.SH LIMITATIONS
+
+Due to SFBs nature, it is possible for responsive flows to share all of its bins
+with a non-responsive flow, causing the responsive flow to be misidentified as
+being non-responsive.
+
+The probability of a responsive flow to be misidentified is dependent on
+the number of non-responsive flows, M.  It is (1 - (1 - (1 / 16.0)) ** M) **8,
+so for example with 10 non-responsive flows approximately 0.2% of responsive flows
+will be misidentified.
+
+To mitigate this, SFB performs performs periodic re-hashing to avoid
+misclassification for prolonged periods of time.
+
+The default hashing method will use source and destination ip addresses and port numbers
+if possible, and also supports tunneling protocols.
+Alternatively, an external classifier can be configured, too.
+
+.SH PARAMETERS
+.TP
+rehash
+Time interval in milliseconds when queue perturbation occurs to avoid erroneously
+detecting unrelated, responsive flows as being part of a non-responsive flow for
+prolonged periods of time.
+Defaults to 10 minutes.
+.TP
+db
+Double buffering warmup wait time, in milliseconds.
+To avoid destroying the probability history when rehashing is performed, this
+implementation maintains a second set of levels/bins as described in section
+4.4 of the SFB reference.
+While one set is used to manage the queue, a second set is warmed up:
+Whenever a flow is then determined to be non-responsive, the marking
+probabilities in the second set are updated.  When the rehashing
+happens, these bins will be used to manage the queue and all non-responsive
+flows can be rate-limited immediately.
+This value determines how much time has to pass before the 2nd set
+will start to be warmed up.
+Defaults to one minute, should be lower than
+.B
+rehash.
+.TP
+limit
+Hard limit on the real (not average) total queue size in packets.
+Further packets are dropped.  Defaults to the transmit queue length of the
+device the qdisc is attached to.
+.TP
+max
+Maximum length of a buckets queue, in packets, before packets start being
+dropped.  Should be sightly larger than
+.B target
+, but should not be set to values exceeding 1.5 times that of
+.B target .
+Defaults to 25.
+.TP
+target
+The desired average bin length.  If the bin queue length reaches this value,
+the marking probability is increased by
+.B increment.
+The default value depends on the
+.B max
+setting, with max set to 25
+.B target
+will default to 20.
+.TP
+increment
+A value used to increase the marking probability when the queue appears
+to be over-used.  Must be between 0 and 1.0.  Defaults to 0.00050.
+.TP
+decrement
+Value used to decrease the marking probability when the queue is found
+to be empty.  Must be between 0 and 1.0.
+Defaults to 0.00005.
+.TP
+penalty_rate
+The maximum number of packets belonging to flows identified as being
+non-responsive that can be enqueued per second. Once this number has been
+reached, further packets of such non-responsive flows are dropped.
+Set this to a reasonable fraction of your uplink throughput; the
+default value of 10 packets is probably too small.
+.TP
+penalty_burst
+The number of packets a flow is permitted to exceed the penalty rate before packets
+start being dropped.
+Defaults to 20 packets.
+
+.SH STATISTICS
+
+This qdisc exposes additional statistics via 'tc -s qdisc' output.
+These are:
+.TP
+earlydrop
+The number of packets dropped before a per-flow queue was full.
+.TP
+ratedrop
+The number of packets dropped because of rate-limiting.
+If this value is high, there are many non-reactive flows being
+sent through sfb.  In such cases, it might be better to
+embed sfb within a classful qdisc to better control such
+flows using a different, shaping qdisc.
+.TP
+bucketdrop
+The number of packets dropped because a per-flow queue was full.
+High bucketdrop may point to a high number of aggressive, short-lived
+flows.
+.TP
+queuedrop
+The number of packets dropped due to reaching limit.  This should normally be 0.
+.TP
+marked
+The number of packets marked with ECN.
+.TP
+maxqlen
+The length of the current longest per-flow (virtual) queue.
+.TP
+maxprob
+The maximum per-flow drop probability.  1 means that some
+flows have been detected as non-reactive.
+
+.SH NOTES
+
+SFB automatically enables use of Explicit Congestion Notification (ECN).
+Also, this SFB implementation does not queue packets itself.
+Rather, packets are enqueued to the inner qdisc (defaults to pfifo).
+Because sfb maintains virtual queue states, the inner qdisc must not
+drop a packet previously queued.
+Furthermore, if a buckets queue has a very high marking rate,
+this implementation will start dropping packets instead of
+marking them, as such a situation points to either bad congestion, or an
+unresponsive flow.
+
+.SH EXAMPLE & USAGE
+
+To attach to interface $DEV, using default options:
+.P
+# tc qdisc add dev $DEV handle 1: root sfb
+
+Only use destination ip addresses for assigning packets to bins, perturbing
+hash results every 10 minutes:
+.P
+# tc filter add dev $DEV parent 1: handle 1 flow hash keys dst perturb 600
+
+.SH SEE ALSO
+.BR tc (8),
+.BR tc-red (8),
+.BR tc-sfq (8)
+.SH SOURCES
+.TP
+o
+W. Feng, D. Kandlur, D. Saha, K. Shin, BLUE: A New Class of Active Queue Management Algorithms,
+U. Michigan CSE-TR-387-99, April 1999.
+
+.SH AUTHORS
+
+This SFB implementation was contributed by Juliusz Chroboczek and Eric Dumazet.
diff --git a/man/man8/tc-sfq.8 b/man/man8/tc-sfq.8
index 8f2b433..5a651ff 100644
--- a/man/man8/tc-sfq.8
+++ b/man/man8/tc-sfq.8
@@ -1,12 +1,35 @@
-.TH TC 8 "8 December 2001" "iproute2" "Linux"
+.TH TC 8 "24 January 2012" "iproute2" "Linux"
 .SH NAME
 sfq \- Stochastic Fairness Queueing
 .SH SYNOPSIS
-.B tc qdisc ... perturb
+.B tc qdisc ...
+.B [ divisor
+hashtablesize
+.B ] [ limit
+packets
+.B ] [ perturb
 seconds
-.B quantum
+.B ] [ quantum
 bytes
-
+.B ] [ flows
+number
+.B ] [ depth
+number
+.B ] [ headdrop
+.B ] [ redflowlimit
+bytes
+.B ] [ min
+bytes
+.B ] [ max
+bytes
+.B ] [ avpkt
+bytes
+.B ] [ burst
+packets
+.B ] [ probability
+P
+.B ] [ ecn
+.B ] [ harddrop ]
 .SH DESCRIPTION
 
 Stochastic Fairness Queueing is a classless queueing discipline available for
@@ -22,7 +45,13 @@
 
 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
+On enqueueing, each packet is assigned to a hash bucket, based on the packets hash value.
+This hash value is either obtained from an external flow classifier (use
+.B
+tc filter
+to set them), or a default internal classifier if no external classifier has been configured.
+
+When the internal classifier is used, sfq uses
 .TP
 (i)
 Source address
@@ -31,43 +60,111 @@
 Destination address
 .TP
 (iii)
-Source port
+Source and Destination 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.
+destination and source. 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 
+get hashed to the same bucket, sfqs internal hashing algorithm may be perturbed at configurable 
 intervals so that the unfairness lasts only for a short while. Perturbation may 
-however cause some inadvertent packet reordering to occur.
+however cause some inadvertent packet reordering to occur. After linux-3.3, there is
+no packet reordering problem, but possible packet drops if rehashing hits one limit
+(number of flows or packets per flow)
 
 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
+Before linux-3.3, 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.
 
+After linux-3.3, maximum length of SFQ is 65535 packets, and divisor limit is 65536.
+In case of overflow, tail-drop is performed on the fullest bucket, unless headdrop was requested.
+
 .SH PARAMETERS
+.TP
+divisor
+Can be used to set a different hash table size, available from kernel 2.6.39 onwards.
+The specified divisor must be a power of two and cannot be larger than 65536.
+Default value: 1024.
 .TP 
 limit
-Upper limit of the SFQ. Can be used to reduce the default length of 128 packets.
+Upper limit of the SFQ. Can be used to reduce the default length of 127 packets.
+After linux-3.3, it can be raised.
+.TP
+depth
+Limit of packets per flow (after linux-3.3). Default to 127 and can be lowered.
 .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
+reordering or losses. Advised value: 60
+This value has no effect when external flow classification is used.
+Its better to increase divisor value to lower risk of hash collisions.
 .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.
-
+.TP
+flows
+After linux-3.3, it is possible to change the default limit of flows.
+Default value is 127
+.TP
+headdrop
+Default SFQ behavior is to perform tail-drop of packets from a flow.
+You can ask a headdrop instead, as this is known to provide a better feedback for TCP flows.
+.TP
+redflowlimit
+Configure the optional RED module on top of each SFQ flow.
+Random Early Detection principle is to perform packet marks or drops in a probabilistic way.
+(man tc-red for details about RED)
+.nf
+redflowlimit configures the hard limit on the real (not average) queue size per SFQ flow in bytes.
+.fi
+.TP
+min
+Average queue size at which marking becomes a possibility. Defaults to
+.B max
+/3
+.TP
+max
+At this average queue size, the marking probability is maximal. Defaults to
+.B redflowlimit
+/4
+.TP
+probability
+Maximum  probability  for  marking, specified as a floating point number from 0.0 to 1.0. Default value is 0.02
+.TP
+avpkt
+Specified in bytes. Used with burst to determine the time constant for average queue size calculations. Default value is 1000
+.TP
+burst
+Used for determining how fast the average queue size is influenced by the real queue size.
+.nf
+Default value is : 
+.B (2 * min + max) / (3 * avpkt)
+.fi
+.TP
+ecn
+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 depth
+packets.
+.TP
+harddrop
+If average flow queue size is above
+.B max
+bytes, this parameter forces a drop instead of ecn marking.
 .SH EXAMPLE & USAGE
 
 To attach to device ppp0:
 .P
-# tc qdisc add dev ppp0 root sfq perturb 10
+# tc qdisc add dev ppp0 root sfq
 .P
 Please note that SFQ, like all non-shaping (work-conserving) qdiscs, is only useful 
 if it owns the queue.
@@ -83,6 +180,20 @@
 .P
 Embed SFQ in a classful qdisc to make sure it owns the queue.
 
+It is possible to use external classifiers with sfq, for example to hash traffic based only
+on source/destination ip addresses:
+.P
+# tc filter add ... flow hash keys src,dst perturb 30 divisor 1024
+.P
+Note that the given divisor should match the one used by sfq. If you have
+changed the sfq default of 1024, use the same value for the flow hash filter, too.
+
+.P
+Example of sfq with optional RED mode :
+.P
+# tc qdisc add dev eth0 parent 1:1 handle 10: sfq limit 3000 flows 512 divisor 16384 
+  redflowlimit 100000 min 8000 max 60000 probability 0.20 ecn headdrop
+
 .SH SOURCE
 .TP 
 o
@@ -101,10 +212,13 @@
 Queuing using Deficit Round Robin", Proc. SIGCOMM 95.
 
 .SH SEE ALSO
-.BR tc (8)
+.BR tc (8),
+.BR tc-red (8)
 
-.SH AUTHOR
-Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by
-bert hubert <ahu@ds9a.nl>
+.SH AUTHORS
+Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>,
+Eric Dumazet <eric.dumazet@gmail.com>.
+.P
+This manpage maintained by bert hubert <ahu@ds9a.nl>
 
 
diff --git a/man/man8/tc-stab.8 b/man/man8/tc-stab.8
new file mode 100644
index 0000000..522ea00
--- /dev/null
+++ b/man/man8/tc-stab.8
@@ -0,0 +1,163 @@
+.TH STAB 8 "31 October 2011" iproute2 Linux
+.
+.SH NAME
+tc\-stab \- Generic size table manipulations
+.
+.SH SYNOPSIS
+.nf
+tc qdisc add ... stab \\
+.RS 4
+[ \fBmtu\fR BYTES ] [ \fBtsize\fR SLOTS ] \\
+[ \fBmpu\fR BYTES ] [ \fBoverhead\fR BYTES ] [ \fBlinklayer\fR TYPE ] ...
+.RE
+
+TYPE := adsl | atm | ethernet
+.fi
+
+For the description of BYTES \- please refer to the \fBUNITS\fR
+section of \fBtc\fR(8).
+
+.IP \fBmtu\fR 4
+.br
+maximum packet size we create size table for, assumed 2048 if not specified explicitly
+.IP \fBtsize\fR
+.br
+required table size, assumed 512 if not specified explicitly
+.IP \fBmpu\fR
+.br
+minimum packet size used in computations
+.IP \fBoverhead\fR
+.br
+per\-packet size overhead (can be negative) used in computations
+.IP \fBlinklayer\fR
+.br
+required linklayer adaptation.
+.PP
+.
+.SH DESCRIPTION
+.
+Size tables allow manipulation of packet size, as seen by whole scheduler
+framework (of course, the actual packet size remains the same). Adjusted packet
+size is calculated only once \- when a qdisc enqueues the packet. Initial root
+enqueue initializes it to the real packet's size.
+
+Each qdisc can use different size table, but the adjusted size is stored in
+area shared by whole qdisc hierarchy attached to the interface. The effect is,
+that if you have such setup, the last qdisc with a stab in a chain "wins". For
+example, consider HFSC with simple pfifo attached to one of its leaf classes.
+If that pfifo qdisc has stab defined, it will override lengths calculated
+during HFSC's enqueue, and in turn, whenever HFSC tries to dequeue a packet, it
+will use potentially invalid size in its calculations. Normal setups will
+usually include stab defined only on root qdisc, but further overriding gives
+extra flexibility for less usual setups.
+
+Initial size table is calculated by \fBtc\fR tool using \fBmtu\fR and
+\fBtsize\fR parameters. The algorithm sets each slot's size to the smallest
+power of 2 value, so the whole \fBmtu\fR is covered by the size table. Neither
+\fBtsize\fR, nor \fBmtu\fR have to be power of 2 value, so the size
+table will usually support more than is required by \fBmtu\fR.
+
+For example, with \fBmtu\fR\~=\~1500 and \fBtsize\fR\~=\~128, a table with 128
+slots will be created, where slot 0 will correspond to sizes 0\-16, slot 1 to
+17\~\-\~32, \&..., slot 127 to 2033\~\-\~2048. Sizes assigned to each slot
+depend on \fBlinklayer\fR parameter.
+
+Stab calculation is also safe for an unusual case, when a size assigned to a
+slot would be larger than 2^16\-1 (you will lose the accuracy though).
+
+During kernel part of packet size adjustment, \fBoverhead\fR will be added to
+original size, and then slot will be calculated. If the size would cause
+overflow, more than 1 slot will be used to get the final size. It of course
+will affect accuracy, but it's only a guard against unusual situations.
+
+Currently there're two methods of creating values stored in the size table \-
+ethernet and atm (adsl):
+
+.IP ethernet 4
+.br
+This is basically 1\-1 mapping, so following our example from above
+(disregarding \fBmpu\fR for a moment) slot 0 would have 8, slot 1 would have 16
+and so on, up to slot 127 with 2048. Note, that \fBmpu\fR\~>\~0 must be
+specified, and slots that would get less than specified by \fBmpu\fR, will get
+\fBmpu\fR instead. If you don't specify \fBmpu\fR, the size table will not be
+created at all (it wouldn't make any difference), although any \fBoverhead\fR
+value will be respected during calculations.
+.IP "atm, adsl"
+.br
+ATM linklayer consists of 53 byte cells, where each of them provides 48 bytes
+for payload. Also all the cells must be fully utilized, thus the last one is
+padded if/as necessary.
+
+When size table is calculated, adjusted size that fits properly into lowest
+amount of cells is assigned to a slot. For example, a 100 byte long packet
+requires three 48\-byte payloads, so the final size would require 3 ATM cells
+\- 159 bytes.
+
+For ATM size tables, 16\~bytes sized slots are perfectly enough. The default
+values of \fBmtu\fR and \fBtsize\fR create 4\~bytes sized slots.
+.PP
+.
+.SH "TYPICAL OVERHEADS"
+The following values are typical for different adsl scenarios (based on
+\fB[1]\fR and \fB[2]\fR):
+
+.nf
+LLC based:
+.RS 4
+PPPoA \- 14 (PPP \- 2, ATM \- 12)
+PPPoE \- 40+ (PPPoE \- 8, ATM \- 18, ethernet 14, possibly FCS \- 4+padding)
+Bridged \- 32 (ATM \- 18, ethernet 14, possibly FCS \- 4+padding)
+IPoA \- 16 (ATM \- 16)
+.RE
+
+VC Mux based:
+.RS 4
+PPPoA \- 10 (PPP \- 2, ATM \- 8)
+PPPoE \- 32+ (PPPoE \- 8, ATM \- 10, ethernet 14, possibly FCS \- 4+padding)
+Bridged \- 24+ (ATM \- 10, ethernet 14, possibly FCS \- 4+padding)
+IPoA \- 8 (ATM \- 8)
+.RE
+.fi
+\p There're few important things regarding the above overheads:
+.
+.IP \(bu 4
+IPoA in LLC case requires SNAP, instead of LLC\-NLPID (see rfc2684) \- this is
+the reason, why it actually takes more space than PPPoA.
+.IP \(bu
+In rare cases, FCS might be preserved on protocols that include ethernet frame
+(Bridged and PPPoE). In such situation, any ethernet specific padding
+guaranteeing 64 bytes long frame size has to be included as well (see rfc2684).
+In the other words, it also guarantees that any packet you send will take
+minimum 2 atm cells. You should set \fBmpu\fR accordingly for that.
+.IP \(bu
+When size table is consulted, and you're shaping traffic for the sake of
+another modem/router, ethernet header (without padding) will already be added
+to initial packet's length. You should compensate for that by subtracting 14
+from the above overheads in such case. If you're shaping directly on the router
+(for example, with speedtouch usb modem) using ppp daemon, you're using raw ip
+interface without underlying layer2, so nothing will be added.
+
+For more thorough explanations, please see \fB[1]\fR and \fB[2]\fR.
+.
+.SH "ETHERNET CARDS CONSIDERATIONS"
+.
+It's often forgotten, that modern network cards (even cheap ones on desktop
+motherboards) and/or their drivers often support different offloading
+mechanisms. In context of traffic shaping, 'tso' and 'gso' might cause
+undesirable effects, due to massive tcp segments being considered during
+traffic shaping (including stab calculations). For slow uplink interfaces,
+it's good to use \fBethtool\fR to turn off offloading features.
+.
+.SH "SEE ALSO"
+.
+\fBtc\fR(8), \fBtc\-hfsc\fR(7), \fBtc\-hfsc\fR(8),
+.br
+\fB[1]\fR http://ace\-host.stuart.id.au/russell/files/tc/tc\-atm/
+.br
+\fB[2]\fR http://www.faqs.org/rfcs/rfc2684.html
+
+Please direct bugreports and patches to: <net...@vger.kernel.org>
+.
+.SH "AUTHOR"
+.
+Manpage created by Michal Soltys (sol...@ziu.info)
diff --git a/man/man8/tc.8 b/man/man8/tc.8
index e17ce68..fc8095e 100644
--- a/man/man8/tc.8
+++ b/man/man8/tc.8
@@ -367,14 +367,19 @@
 was written by Alexey N. Kuznetsov and added in Linux 2.2.
 .SH SEE ALSO
 .BR tc-cbq (8),
+.BR tc-choke (8),
 .BR tc-drr (8),
 .BR tc-htb (8),
+.BR tc-hfsc (8),
+.BR tc-hfsc (7),
+.BR tc-sfb (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-stab (8),
 .br
 .RB "User documentation at " http://lartc.org/ ", but please direct bugreports and patches to: " <netdev@vger.kernel.org>
 
diff --git a/misc/Makefile b/misc/Makefile
index 8c25381..a59ff87 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -7,7 +7,7 @@
 
 all: $(TARGETS)
 
-ss: $(SSOBJ) $(LIBUTIL)
+ss: $(SSOBJ)
 
 nstat: nstat.c
 	$(CC) $(CFLAGS) $(LDFLAGS) -o nstat nstat.c -lm
diff --git a/misc/arpd.c b/misc/arpd.c
index 128c49d..dd1de80 100644
--- a/misc/arpd.c
+++ b/misc/arpd.c
@@ -90,11 +90,13 @@
 int no_kernel_broadcasts;
 int broadcast_rate = 1000;
 int broadcast_burst = 3000;
+int poll_timeout = 30000;
 
 void usage(void)
 {
 	fprintf(stderr,
-"Usage: arpd [ -lk ] [ -a N ] [ -b dbase ] [ -f file ] [ interfaces ]\n");
+		"Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ]"
+		" [ -f file ] [ -n time ] [-p interval ] [ -R rate ] [ interfaces ]\n");
 	exit(1);
 }
 
@@ -281,7 +283,7 @@
 
 	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;
+	return rtnl_send(&rth, &req, req.n.nlmsg_len) <= 0;
 }
 
 void prepare_neg_entry(__u8 *ndata, __u32 stamp)
@@ -591,7 +593,7 @@
 	int do_list = 0;
 	char *do_load = NULL;
 
-	while ((opt = getopt(argc, argv, "h?b:lf:a:n:kR:B:")) != EOF) {
+	while ((opt = getopt(argc, argv, "h?b:lf:a:n:p:kR:B:")) != EOF) {
 		switch (opt) {
 	        case 'b':
 			dbname = optarg;
@@ -615,6 +617,12 @@
 		case 'k':
 			no_kernel_broadcasts = 1;
 			break;
+		case 'p':
+			if ((poll_timeout = 1000 * strtod(optarg, NULL)) < 100) {
+				fprintf(stderr,"Invalid poll timeout\n");
+				exit(-1);
+			}
+			break;
 		case 'R':
 			if ((broadcast_rate = atoi(optarg)) <= 0 ||
 			    (broadcast_rate = 1000/broadcast_rate) <= 0) {
@@ -807,7 +815,7 @@
 		}
 		if (do_stats)
 			send_stats();
-		if (poll(pset, 2, 30000) > 0) {
+		if (poll(pset, 2, poll_timeout) > 0) {
 			in_poll = 0;
 			if (pset[0].revents&EVENTS)
 				get_arp_pkt();
diff --git a/misc/ifstat.c b/misc/ifstat.c
index 0ce8c92..e7fbaa8 100644
--- a/misc/ifstat.c
+++ b/misc/ifstat.c
@@ -29,7 +29,8 @@
 #include <getopt.h>
 
 #include <libnetlink.h>
-#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
 
 #include <SNAPSHOT.h>
 
@@ -48,7 +49,7 @@
 char info_source[128];
 int source_mismatch;
 
-#define MAXS (sizeof(struct net_device_stats)/sizeof(unsigned long))
+#define MAXS (sizeof(struct rtnl_link_stats)/sizeof(__u32))
 
 struct ifstat_ent
 {
@@ -57,7 +58,7 @@
 	int			ifindex;
 	unsigned long long	val[MAXS];
 	double			rate[MAXS];
-	unsigned long		ival[MAXS];
+	__u32			ival[MAXS];
 };
 
 struct ifstat_ent *kern_db;
@@ -127,7 +128,7 @@
 		exit(1);
 	}
 
-	if (rtnl_dump_filter(&rth, get_nlmsg, NULL, NULL, NULL) < 0) {
+	if (rtnl_dump_filter(&rth, get_nlmsg, NULL) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
@@ -186,7 +187,7 @@
 			*next++ = 0;
 			if (sscanf(p, "%llu", n->val+i) != 1)
 				abort();
-			n->ival[i] = (unsigned long)n->val[i];
+			n->ival[i] = (__u32)n->val[i];
 			p = next;
 			if (!(next = strchr(p, ' ')))
 				abort();
@@ -375,8 +376,7 @@
 
 void dump_kern_db(FILE *fp)
 {
-	struct ifstat_ent *n, *h;
-	h = hist_db;
+	struct ifstat_ent *n;
 
 	print_head(fp);
 
@@ -562,8 +562,6 @@
 "   -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);
@@ -580,8 +578,6 @@
 	{ "interval", 1, 0, 't' },
 	{ "version", 0, 0, 'V' },
 	{ "zeros", 0, 0, 'z' },
-	{ "errors", 0, 0, 'e' },
-	{ "zeros", 0, 0, 'z' },
 	{ 0 }
 };
 
@@ -677,9 +673,11 @@
 	npatterns = argc;
 
 	if (getenv("IFSTAT_HISTORY"))
-		snprintf(hist_name, sizeof(hist_name), getenv("IFSTAT_HISTORY"));
+		snprintf(hist_name, sizeof(hist_name),
+			 "%s", getenv("IFSTAT_HISTORY"));
 	else
-		sprintf(hist_name, "%s/.ifstat.u%d", P_tmpdir, getuid());
+		snprintf(hist_name, sizeof(hist_name),
+			 "%s/.ifstat.u%d", P_tmpdir, getuid());
 
 	if (reset_history)
 		unlink(hist_name);
@@ -710,7 +708,7 @@
 		}
 		if (!ignore_history) {
 			FILE *tfp;
-			long uptime;
+			long uptime = -1;
 			if ((tfp = fopen("/proc/uptime", "r")) != NULL) {
 				if (fscanf(tfp, "%ld", &uptime) != 1)
 					uptime = -1;
diff --git a/misc/lnstat.c b/misc/lnstat.c
index 32ab6a4..bd19cc1 100644
--- a/misc/lnstat.c
+++ b/misc/lnstat.c
@@ -45,7 +45,7 @@
 	{ "file", 1, NULL, 'f' },
 	{ "help", 0, NULL, 'h' },
 	{ "interval", 1, NULL, 'i' },
-	{ "key", 1, NULL, 'k' },
+	{ "keys", 1, NULL, 'k' },
 	{ "subject", 1, NULL, 's' },
 	{ "width", 1, NULL, 'w' },
 };
@@ -61,7 +61,7 @@
 	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"
+	fprintf(stderr, "\t-d --dump\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");
diff --git a/misc/nstat.c b/misc/nstat.c
index 4f73c62..2f06ffd 100644
--- a/misc/nstat.c
+++ b/misc/nstat.c
@@ -560,7 +560,7 @@
 		}
 		if (!ignore_history) {
 			FILE *tfp;
-			long uptime;
+			long uptime = -1;
 			if ((tfp = fopen("/proc/uptime", "r")) != NULL) {
 				if (fscanf(tfp, "%ld", &uptime) != 1)
 					uptime = -1;
diff --git a/misc/rtacct.c b/misc/rtacct.c
index a247dfd..49168bd 100644
--- a/misc/rtacct.c
+++ b/misc/rtacct.c
@@ -535,7 +535,7 @@
 	}
 
 	if (getenv("RTACCT_HISTORY"))
-		snprintf(hist_name, sizeof(hist_name), getenv("RTACCT_HISTORY"));
+		snprintf(hist_name, sizeof(hist_name), "%s", getenv("RTACCT_HISTORY"));
 	else
 		sprintf(hist_name, "/tmp/.rtacct.u%d", getuid());
 
@@ -563,7 +563,10 @@
 			exit(-1);
 		}
 		if (stb.st_size != sizeof(*hist_db))
-			write(fd, kern_db, sizeof(*hist_db));
+			if (write(fd, kern_db, sizeof(*hist_db)) < 0) {
+				perror("rtacct: write history file");
+				exit(-1);
+			}
 
 		hist_db = mmap(NULL, sizeof(*hist_db),
 			       PROT_READ|PROT_WRITE,
@@ -577,7 +580,7 @@
 
 		if (!ignore_history) {
 			FILE *tfp;
-			long uptime;
+			long uptime = -1;
 			if ((tfp = fopen("/proc/uptime", "r")) != NULL) {
 				if (fscanf(tfp, "%ld", &uptime) != 1)
 					uptime = -1;
diff --git a/misc/ss.c b/misc/ss.c
index 482b6bb..cf529ef 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -34,7 +34,9 @@
 #include "SNAPSHOT.h"
 
 #include <netinet/tcp.h>
+#include <linux/sock_diag.h>
 #include <linux/inet_diag.h>
+#include <linux/unix_diag.h>
 
 int resolve_hosts = 0;
 int resolve_services = 1;
@@ -272,13 +274,19 @@
 			const char *pattern = "socket:[";
 			unsigned int ino;
 			char lnk[64];
-			int fd, n;
+			int fd;
+			ssize_t link_len;
 
 			if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1)
 				continue;
 
 			sprintf(name+pos, "%d", fd);
-			n = readlink(name, lnk, sizeof(lnk)-1);
+
+			link_len = readlink(name, lnk, sizeof(lnk)-1);
+			if (link_len == -1)
+				continue;
+			lnk[link_len] = '\0';
+
 			if (strncmp(lnk, pattern, strlen(pattern)))
 				continue;
 
@@ -518,6 +526,7 @@
 				}
 			}
 		}
+		pclose(fp);
 	}
 }
 
@@ -803,7 +812,7 @@
 	{
 		if (!(*bytecode=malloc(4))) abort();
 		((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_AUTO, 4, 8 };
-		return 8;
+		return 4;
 	}
 		case SSF_DCOND:
 		case SSF_SCOND:
@@ -1327,7 +1336,17 @@
 	parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr*)(r+1),
 		     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
-	if (tb[INET_DIAG_MEMINFO]) {
+	if (tb[INET_DIAG_SKMEMINFO]) {
+		const __u32 *skmeminfo =  RTA_DATA(tb[INET_DIAG_SKMEMINFO]);
+		printf(" skmem:(r%u,rb%u,t%u,tb%u,f%u,w%u,o%u)",
+			skmeminfo[SK_MEMINFO_RMEM_ALLOC],
+			skmeminfo[SK_MEMINFO_RCVBUF],
+			skmeminfo[SK_MEMINFO_WMEM_ALLOC],
+			skmeminfo[SK_MEMINFO_SNDBUF],
+			skmeminfo[SK_MEMINFO_FWD_ALLOC],
+			skmeminfo[SK_MEMINFO_WMEM_QUEUED],
+			skmeminfo[SK_MEMINFO_OPTMEM]);
+	}else if (tb[INET_DIAG_MEMINFO]) {
 		const struct inet_diag_meminfo *minfo
 			= RTA_DATA(tb[INET_DIAG_MEMINFO]);
 		printf(" mem:(r%u,w%u,f%u,t%u)",
@@ -1356,10 +1375,12 @@
 				printf(" sack");
 			if (info->tcpi_options & TCPI_OPT_ECN)
 				printf(" ecn");
+			if (info->tcpi_options & TCPI_OPT_ECN_SEEN)
+				printf(" ecnseen");
 		}
 
 		if (tb[INET_DIAG_CONG])
-			printf(" %s", (char *) RTA_DATA(tb[INET_DIAG_CONG]));
+			printf(" %s", rta_getattr_str(tb[INET_DIAG_CONG]));
 
 		if (info->tcpi_options & TCPI_OPT_WSCALE)
 			printf(" wscale:%d,%d", info->tcpi_snd_wscale,
@@ -1494,8 +1515,10 @@
 	memset(&req.r, 0, sizeof(req.r));
 	req.r.idiag_family = AF_INET;
 	req.r.idiag_states = f->states;
-	if (show_mem)
+	if (show_mem) {
 		req.r.idiag_ext |= (1<<(INET_DIAG_MEMINFO-1));
+		req.r.idiag_ext |= (1<<(INET_DIAG_SKMEMINFO-1));
+	}
 
 	if (show_tcpinfo) {
 		req.r.idiag_ext |= (1<<(INET_DIAG_INFO-1));
@@ -1523,8 +1546,10 @@
 		.msg_iovlen = f->f ? 3 : 1,
 	};
 
-	if (sendmsg(fd, &msg, 0) < 0)
+	if (sendmsg(fd, &msg, 0) < 0) {
+		close(fd);
 		return -1;
+	}
 
 	iov[0] = (struct iovec){
 		.iov_base = buf,
@@ -1552,6 +1577,7 @@
 		}
 		if (status == 0) {
 			fprintf(stderr, "EOF on netlink\n");
+			close(fd);
 			return 0;
 		}
 
@@ -1567,16 +1593,23 @@
 			    h->nlmsg_seq != 123456)
 				goto skip_it;
 
-			if (h->nlmsg_type == NLMSG_DONE)
+			if (h->nlmsg_type == NLMSG_DONE) {
+				close(fd);
 				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;
+					if (errno == EOPNOTSUPP) {
+						close(fd);
+						return -1;
+					}
 					perror("TCPDIAG answers");
 				}
+				close(fd);
 				return 0;
 			}
 			if (!dump_fp) {
@@ -1585,8 +1618,10 @@
 					continue;
 				}
 				err = tcp_show_sock(h, NULL);
-				if (err < 0)
+				if (err < 0) {
+					close(fd);
 					return err;
+				}
 			}
 
 skip_it:
@@ -1601,6 +1636,7 @@
 			exit(1);
 		}
 	}
+	close(fd);
 	return 0;
 }
 
@@ -1984,6 +2020,157 @@
 	}
 }
 
+static int unix_show_sock(struct nlmsghdr *nlh, struct filter *f)
+{
+	struct unix_diag_msg *r = NLMSG_DATA(nlh);
+	struct rtattr *tb[UNIX_DIAG_MAX+1];
+	char name[128];
+	int peer_ino;
+	int rqlen;
+
+	parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr*)(r+1),
+		     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+	if (netid_width)
+		printf("%-*s ", netid_width,
+				r->udiag_type == SOCK_STREAM ? "u_str" : "u_dgr");
+	if (state_width)
+		printf("%-*s ", state_width, sstate_name[r->udiag_state]);
+
+	if (tb[UNIX_DIAG_RQLEN])
+		rqlen = *(int *)RTA_DATA(tb[UNIX_DIAG_RQLEN]);
+	else
+		rqlen = 0;
+
+	printf("%-6d %-6d ", rqlen, 0);
+
+	if (tb[UNIX_DIAG_NAME]) {
+		int len = RTA_PAYLOAD(tb[UNIX_DIAG_NAME]);
+
+		memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len);
+		name[len] = '\0';
+		if (name[0] == '\0')
+			name[0] = '@';
+	} else
+		sprintf(name, "*");
+
+	if (tb[UNIX_DIAG_PEER])
+		peer_ino = *(int *)RTA_DATA(tb[UNIX_DIAG_PEER]);
+	else
+		peer_ino = 0;
+
+	printf("%*s %-*d %*s %-*d",
+			addr_width, name,
+			serv_width, r->udiag_ino,
+			addr_width, "*", /* FIXME */
+			serv_width, peer_ino);
+
+	if (show_users) {
+		char ubuf[4096];
+		if (find_users(r->udiag_ino, ubuf, sizeof(ubuf)) > 0)
+			printf(" users:(%s)", ubuf);
+	}
+
+	printf("\n");
+
+	return 0;
+}
+
+static int unix_show_netlink(struct filter *f, FILE *dump_fp)
+{
+	int fd;
+	struct {
+		struct nlmsghdr nlh;
+		struct unix_diag_req r;
+	} req;
+	char	buf[8192];
+
+	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0)
+		return -1;
+
+	memset(&req, 0, sizeof(req));
+	req.nlh.nlmsg_len = sizeof(req);
+	req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.nlh.nlmsg_seq = 123456;
+
+	req.r.sdiag_family = AF_UNIX;
+	req.r.udiag_states = f->states;
+	req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN;
+
+	if (send(fd, &req, sizeof(req), 0) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	while (1) {
+		ssize_t status;
+		struct nlmsghdr *h;
+		struct sockaddr_nl nladdr;
+		socklen_t slen = sizeof(nladdr);
+
+		status = recvfrom(fd, buf, sizeof(buf), 0,
+				  (struct sockaddr *) &nladdr, &slen);
+		if (status < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("OVERRUN");
+			continue;
+		}
+		if (status == 0) {
+			fprintf(stderr, "EOF on netlink\n");
+			goto close_it;
+		}
+
+		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)
+				goto close_it;
+
+			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;
+					if (errno != ENOENT)
+						fprintf(stderr, "UDIAG answers %d\n", errno);
+				}
+				close(fd);
+				return -1;
+			}
+			if (!dump_fp) {
+				err = unix_show_sock(h, f);
+				if (err < 0) {
+					close(fd);
+					return err;
+				}
+			}
+
+skip_it:
+			h = NLMSG_NEXT(h, status);
+		}
+
+		if (status) {
+			fprintf(stderr, "!!!Remnant of size %zd\n", status);
+			exit(1);
+		}
+	}
+
+close_it:
+	close(fd);
+	return 0;
+}
+
 int unix_show(struct filter *f)
 {
 	FILE *fp;
@@ -1993,6 +2180,10 @@
 	int  cnt;
 	struct unixstat *list = NULL;
 
+	if (!getenv("PROC_NET_UNIX") && !getenv("PROC_ROOT")
+	    && unix_show_netlink(f, NULL) == 0)
+		return 0;
+
 	if ((fp = net_unix_open()) == NULL)
 		return -1;
 	fgets(buf, sizeof(buf)-1, fp);
@@ -2053,7 +2244,7 @@
 			cnt = 0;
 		}
 	}
-
+	fclose(fp);
 	if (list) {
 		unix_list_print(list, f);
 		unix_list_free(list);
@@ -2416,9 +2607,10 @@
 "   -x, --unix		display only Unix domain sockets\n"
 "   -f, --family=FAMILY display sockets of type FAMILY\n"
 "\n"
-"   -A, --query=QUERY\n"
+"   -A, --query=QUERY, --socket=QUERY\n"
 "       QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]\n"
 "\n"
+"   -D, --diag=FILE     Dump raw information about TCP sockets to FILE\n"
 "   -F, --filter=FILE   read filter information from FILE\n"
 "       FILTER := [ state TCP-STATE ] [ EXPRESSION ]\n"
 		);
@@ -2486,8 +2678,9 @@
 	{ "packet", 0, 0, '0' },
 	{ "family", 1, 0, 'f' },
 	{ "socket", 1, 0, 'A' },
+	{ "query", 1, 0, 'A' },
 	{ "summary", 0, 0, 's' },
-	{ "diag", 0, 0, 'D' },
+	{ "diag", 1, 0, 'D' },
 	{ "filter", 1, 0, 'F' },
 	{ "version", 0, 0, 'V' },
 	{ "help", 0, 0, 'h' },
@@ -2559,7 +2752,7 @@
 			current_filter.states = SS_ALL;
 			break;
 		case 'l':
-			current_filter.states = (1<<SS_LISTEN);
+			current_filter.states = (1<<SS_LISTEN) | (1<<SS_CLOSE);
 			break;
 		case '4':
 			preferred_family = AF_INET;
diff --git a/tc/Makefile b/tc/Makefile
index 3aa9f26..be8cd5a 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -15,6 +15,8 @@
 TCMODULES += q_rr.o
 TCMODULES += q_multiq.o
 TCMODULES += q_netem.o
+TCMODULES += q_choke.o
+TCMODULES += q_sfb.o
 TCMODULES += f_rsvp.o
 TCMODULES += f_u32.o
 TCMODULES += f_route.o
@@ -29,11 +31,13 @@
 TCMODULES += q_hfsc.o
 TCMODULES += q_htb.o
 TCMODULES += q_drr.o
+TCMODULES += q_qfq.o
 TCMODULES += m_gact.o
 TCMODULES += m_mirred.o
 TCMODULES += m_nat.o
 TCMODULES += m_pedit.o
 TCMODULES += m_skbedit.o
+TCMODULES += m_csum.o
 TCMODULES += p_ip.o
 TCMODULES += p_icmp.o
 TCMODULES += p_tcp.o
@@ -42,6 +46,7 @@
 TCMODULES += em_cmp.o
 TCMODULES += em_u32.o
 TCMODULES += em_meta.o
+TCMODULES += q_mqprio.o
 
 TCSO :=
 ifeq ($(TC_CONFIG_ATM),y)
@@ -84,8 +89,9 @@
 
 YACC := bison
 LEX := flex
+CFLAGS += -DYY_NO_INPUT
 
-MODDESTDIR := $(DESTDIR)$(patsubst /usr%,%,$(LIBDIR))/tc
+MODDESTDIR := $(DESTDIR)$(LIBDIR)/tc
 
 %.so: %.c
 	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic $< -o $@
@@ -93,7 +99,7 @@
 
 all: libtc.a tc $(TCSO)
 
-tc: $(TCOBJ) $(LIBNETLINK) $(LIBUTIL) $(TCLIB)
+tc: $(TCOBJ) $(TCLIB)
 
 libtc.a: $(TCLIB)
 	$(AR) rcs $@ $(TCLIB)
@@ -114,16 +120,16 @@
 
 clean:
 	rm -f $(TCOBJ) $(TCLIB) libtc.a tc *.so emp_ematch.yacc.h; \
-	rm -f emp_ematch.yacc.output
+	rm -f emp_ematch.yacc.*
 
 q_atm.so: q_atm.c
 	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o q_atm.so q_atm.c -latm
 
 m_xt.so: m_xt.c
-	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt.so m_xt.c -lxtables
+	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt.so m_xt.c $$(pkg-config xtables --cflags --libs)
 
 m_xt_old.so: m_xt_old.c
-	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt_old.so m_xt_old.c -lxtables
+	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt_old.so m_xt_old.c $$(pkg-config xtables --cflags --libs)
 
 %.yacc.c: %.y
 	$(YACC) $(YACCFLAGS) -o $@ $<
@@ -131,6 +137,11 @@
 %.lex.c: %.l
 	$(LEX) $(LEXFLAGS) -o$@ $<
 
+# our lexer includes the header from yacc, so make sure
+# we don't attempt to compile it before the header has
+# been generated as part of the yacc step.
+emp_ematch.lex.o: emp_ematch.yacc.c
+
 ifneq ($(SHARED_LIBS),y)
 
 tc: static-syms.o
diff --git a/tc/em_cmp.c b/tc/em_cmp.c
index 6addce0..3e6d00e 100644
--- a/tc/em_cmp.c
+++ b/tc/em_cmp.c
@@ -155,7 +155,7 @@
 		fprintf(fd, "u8 ");
 	else if (cmp->align == TCF_EM_ALIGN_U16)
 		fprintf(fd, "u16 ");
-	else if (cmp->align == TCF_EM_ALIGN_U16)
+	else if (cmp->align == TCF_EM_ALIGN_U32)
 		fprintf(fd, "u32 ");
 
 	fprintf(fd, "at %d layer %d ", cmp->off, cmp->layer);
diff --git a/tc/em_meta.c b/tc/em_meta.c
index 033e29f..fad6b12 100644
--- a/tc/em_meta.c
+++ b/tc/em_meta.c
@@ -448,7 +448,7 @@
 				    "size mismatch.\n");
 				return -1;
 			}
-			fprintf(fd, "%d", *(__u32 *) RTA_DATA(rta));
+			fprintf(fd, "%d", rta_getattr_u32(rta));
 			break;
 
 		case TCF_META_TYPE_VAR:
@@ -485,7 +485,7 @@
 					goto size_mismatch;
 
 				fprintf(fd, " mask 0x%08x",
-				    *(__u32*) RTA_DATA(rta));
+				    rta_getattr_u32(rta));
 			}
 			break;
 	}
diff --git a/tc/emp_ematch.y b/tc/emp_ematch.y
index e8d1671..7043a80 100644
--- a/tc/emp_ematch.y
+++ b/tc/emp_ematch.y
@@ -19,7 +19,7 @@
 
 %{
  extern int ematch_lex(void);
- extern void yyerror(char *s);
+ extern void yyerror(const char *s);
  extern struct ematch *ematch_root;
  extern char *ematch_err;
 %}
@@ -94,7 +94,7 @@
 	;
 %%
 
- void yyerror(char *s)
+ void yyerror(const char *s)
  {
 	 ematch_err = strdup(s);
  }
diff --git a/tc/f_basic.c b/tc/f_basic.c
index ad41633..67d26ec 100644
--- a/tc/f_basic.c
+++ b/tc/f_basic.c
@@ -122,7 +122,7 @@
 	if (tb[TCA_BASIC_CLASSID]) {
 		SPRINT_BUF(b1);
 		fprintf(f, "flowid %s ",
-			sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_BASIC_CLASSID]), b1));
+			sprint_tc_classid(rta_getattr_u32(tb[TCA_BASIC_CLASSID]), b1));
 	}
 
 	if (tb[TCA_BASIC_EMATCHES])
diff --git a/tc/f_cgroup.c b/tc/f_cgroup.c
index 192e697..4a4026e 100644
--- a/tc/f_cgroup.c
+++ b/tc/f_cgroup.c
@@ -74,7 +74,6 @@
 			explain();
 			return -1;
 		}
-		argc--; argv++;
 	}
 
 	tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail;
diff --git a/tc/f_flow.c b/tc/f_flow.c
index 84b45c9..7d4bb7a 100644
--- a/tc/f_flow.c
+++ b/tc/f_flow.c
@@ -33,7 +33,7 @@
 "KEY      := [ src | dst | proto | proto-src | proto-dst | iif | priority | \n"
 "              mark | nfct | nfct-src | nfct-dst | nfct-proto-src | \n"
 "              nfct-proto-dst | rt-classid | sk-uid | sk-gid |\n"
-"              vlan-tag ]\n"
+"              vlan-tag | rxhash ]\n"
 "OPS      := [ or NUM | and NUM | xor NUM | rshift NUM | addend NUM ]\n"
 "ID       := X:Y\n"
 	);
@@ -57,6 +57,7 @@
 	[FLOW_KEY_SKUID]		= "sk-uid",
 	[FLOW_KEY_SKGID]		= "sk-gid",
 	[FLOW_KEY_VLAN_TAG]		= "vlan-tag",
+	[FLOW_KEY_RXHASH]		= "rxhash",
 };
 
 static int flow_parse_keys(__u32 *keys, __u32 *nkeys, char *argv)
@@ -281,7 +282,7 @@
 	fprintf(f, "handle 0x%x ", handle);
 
 	if (tb[TCA_FLOW_MODE]) {
-		__u32 mode = *(__u32 *)RTA_DATA(tb[TCA_FLOW_MODE]);
+		__u32 mode = rta_getattr_u32(tb[TCA_FLOW_MODE]);
 
 		switch (mode) {
 		case FLOW_MODE_MAP:
@@ -294,7 +295,7 @@
 	}
 
 	if (tb[TCA_FLOW_KEYS]) {
-		__u32 keymask = *(__u32 *)RTA_DATA(tb[TCA_FLOW_KEYS]);
+		__u32 keymask = rta_getattr_u32(tb[TCA_FLOW_KEYS]);
 		char *sep = "";
 
 		fprintf(f, "keys ");
@@ -308,9 +309,9 @@
 	}
 
 	if (tb[TCA_FLOW_MASK])
-		mask = *(__u32 *)RTA_DATA(tb[TCA_FLOW_MASK]);
+		mask = rta_getattr_u32(tb[TCA_FLOW_MASK]);
 	if (tb[TCA_FLOW_XOR])
-		val = *(__u32 *)RTA_DATA(tb[TCA_FLOW_XOR]);
+		val = rta_getattr_u32(tb[TCA_FLOW_XOR]);
 
 	if (mask != ~0 || val != 0) {
 		__u32 or = (mask & val) ^ val;
@@ -326,21 +327,21 @@
 
 	if (tb[TCA_FLOW_RSHIFT])
 		fprintf(f, "rshift %u ",
-			*(__u32 *)RTA_DATA(tb[TCA_FLOW_RSHIFT]));
+			rta_getattr_u32(tb[TCA_FLOW_RSHIFT]));
 	if (tb[TCA_FLOW_ADDEND])
 		fprintf(f, "addend 0x%x ",
-			*(__u32 *)RTA_DATA(tb[TCA_FLOW_ADDEND]));
+			rta_getattr_u32(tb[TCA_FLOW_ADDEND]));
 
 	if (tb[TCA_FLOW_DIVISOR])
 		fprintf(f, "divisor %u ",
-			*(__u32 *)RTA_DATA(tb[TCA_FLOW_DIVISOR]));
+			rta_getattr_u32(tb[TCA_FLOW_DIVISOR]));
 	if (tb[TCA_FLOW_BASECLASS])
 		fprintf(f, "baseclass %s ",
-			sprint_tc_classid(*(__u32 *)RTA_DATA(tb[TCA_FLOW_BASECLASS]), b1));
+			sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOW_BASECLASS]), b1));
 
 	if (tb[TCA_FLOW_PERTURB])
 		fprintf(f, "perturb %usec ",
-			*(__u32 *)RTA_DATA(tb[TCA_FLOW_PERTURB]));
+			rta_getattr_u32(tb[TCA_FLOW_PERTURB]));
 
 	if (tb[TCA_FLOW_EMATCHES])
 		print_ematch(f, tb[TCA_FLOW_EMATCHES]);
diff --git a/tc/f_fw.c b/tc/f_fw.c
index 219b404..161e2f7 100644
--- a/tc/f_fw.c
+++ b/tc/f_fw.c
@@ -130,7 +130,7 @@
 		if(handle)
 			mark = handle;
 		if(tb[TCA_FW_MASK] &&
-		    (mask = *(__u32*)RTA_DATA(tb[TCA_FW_MASK])) != 0xFFFFFFFF)
+		    (mask = rta_getattr_u32(tb[TCA_FW_MASK])) != 0xFFFFFFFF)
 			fprintf(f, "handle 0x%x/0x%x ", mark, mask);
 		else
 			fprintf(f, "handle 0x%x ", handle);
@@ -138,14 +138,14 @@
 
 	if (tb[TCA_FW_CLASSID]) {
 		SPRINT_BUF(b1);
-		fprintf(f, "classid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_FW_CLASSID]), b1));
+		fprintf(f, "classid %s ", sprint_tc_classid(rta_getattr_u32(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));
+		fprintf(f, "input dev %s ",rta_getattr_str(idev));
 	}
 
 	if (tb[TCA_FW_ACT]) {
diff --git a/tc/f_route.c b/tc/f_route.c
index eccf924..649e0ec 100644
--- a/tc/f_route.c
+++ b/tc/f_route.c
@@ -148,12 +148,12 @@
 
 	if (tb[TCA_ROUTE4_CLASSID]) {
 		SPRINT_BUF(b1);
-		fprintf(f, "flowid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID]), b1));
+		fprintf(f, "flowid %s ", sprint_tc_classid(rta_getattr_u32(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)));
+		fprintf(f, "to %s ", rtnl_rtrealm_n2a(rta_getattr_u32(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)));
+		fprintf(f, "from %s ", rtnl_rtrealm_n2a(rta_getattr_u32(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])
diff --git a/tc/f_rsvp.c b/tc/f_rsvp.c
index 808310d..8eaf85d 100644
--- a/tc/f_rsvp.c
+++ b/tc/f_rsvp.c
@@ -336,9 +336,9 @@
 	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));
+			fprintf(f, "flowid %s ", sprint_tc_classid(rta_getattr_u32(tb[TCA_RSVP_CLASSID]), b1));
 		else
-			fprintf(f, "tunnel %d skip %d ", *(__u32*)RTA_DATA(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr);
+			fprintf(f, "tunnel %d skip %d ", rta_getattr_u32(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr);
 	} else if (pinfo && pinfo->tunnelhdr)
 		fprintf(f, "tunnel [BAD] skip %d ", pinfo->tunnelhdr);
 
diff --git a/tc/f_tcindex.c b/tc/f_tcindex.c
index cb6f854..590c0a6 100644
--- a/tc/f_tcindex.c
+++ b/tc/f_tcindex.c
@@ -134,7 +134,7 @@
 
 		if (RTA_PAYLOAD(tb[TCA_TCINDEX_HASH]) < sizeof(hash))
 			return -1;
-		hash = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_HASH]);
+		hash = rta_getattr_u16(tb[TCA_TCINDEX_HASH]);
 		fprintf(f,"hash %d ",hash);
 	}
 	if (tb[TCA_TCINDEX_MASK]) {
@@ -142,7 +142,7 @@
 
 		if (RTA_PAYLOAD(tb[TCA_TCINDEX_MASK]) < sizeof(mask))
 			return -1;
-		mask = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_MASK]);
+		mask = rta_getattr_u16(tb[TCA_TCINDEX_MASK]);
 		fprintf(f,"mask 0x%04x ",mask);
 	}
 	if (tb[TCA_TCINDEX_SHIFT]) {
diff --git a/tc/f_u32.c b/tc/f_u32.c
index d9d4091..975c0b5 100644
--- a/tc/f_u32.c
+++ b/tc/f_u32.c
@@ -432,7 +432,8 @@
 	key = htonl(key);
 	mask = htonl(mask);
 
-	if (res = pack_key(sel, key, mask, off, offmask) < 0)
+	res = pack_key(sel, key, mask, off, offmask);
+	if (res < 0)
 		return -1;
 
 	*argc_p = argc;
@@ -859,15 +860,15 @@
 	case 20:
 		switch (ntohl(key->mask)) {
 		case 0x0000ffff:
-			fprintf(f, "\n  match sport %u",
+			fprintf(f, "\n  match dport %u",
 				ntohl(key->val) & 0xffff);
 			return;
 		case 0xffff0000:
-			fprintf(f, "\n  match dport %u",
+			fprintf(f, "\n  match sport %u",
 				ntohl(key->val) >> 16);
 			return;
 		case 0xffffffff:
-			fprintf(f, "\n  match sport %u, match dport %u",
+			fprintf(f, "\n  match dport %u, match sport %u",
 				ntohl(key->val) & 0xffff,
 				ntohl(key->val) >> 16);
 
@@ -1197,9 +1198,9 @@
 	}
 
 	if (tb[TCA_U32_DIVISOR]) {
-		fprintf(f, "ht divisor %d ", *(__u32*)RTA_DATA(tb[TCA_U32_DIVISOR]));
+		fprintf(f, "ht divisor %d ", rta_getattr_u32(tb[TCA_U32_DIVISOR]));
 	} else if (tb[TCA_U32_HASH]) {
-		__u32 htid = *(__u32*)RTA_DATA(tb[TCA_U32_HASH]);
+		__u32 htid = rta_getattr_u32(tb[TCA_U32_HASH]);
 		fprintf(f, "key ht %x bkt %x ", TC_U32_USERHTID(htid),
 			TC_U32_HASH(htid));
 	} else {
@@ -1209,14 +1210,14 @@
 		SPRINT_BUF(b1);
 		fprintf(f, "%sflowid %s ",
 			!sel || !(sel->flags&TC_U32_TERMINAL) ? "*" : "",
-			sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_U32_CLASSID]), b1));
+			sprint_tc_classid(rta_getattr_u32(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));
+			sprint_u32_handle(rta_getattr_u32(tb[TCA_U32_LINK]), b1));
 	}
 
 	if (tb[TCA_U32_PCNT]) {
@@ -1277,7 +1278,7 @@
 	}
 	if (tb[TCA_U32_INDEV]) {
 		struct rtattr *idev = tb[TCA_U32_INDEV];
-		fprintf(f, "\n  input dev %s\n", (char *) RTA_DATA(idev));
+		fprintf(f, "\n  input dev %s\n", rta_getattr_str(idev));
 	}
 	if (tb[TCA_U32_ACT]) {
 		tc_print_action(f, tb[TCA_U32_ACT]);
diff --git a/tc/m_action.c b/tc/m_action.c
index 11814c9..b1dd26d 100644
--- a/tc/m_action.c
+++ b/tc/m_action.c
@@ -464,7 +464,7 @@
 	if (cmd == RTM_GETACTION)
 		ans = &req.n;
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, ans, NULL, NULL) < 0) {
+	if (rtnl_talk(&rth, &req.n, 0, 0, ans) < 0) {
 		fprintf(stderr, "We have an error talking to the kernel\n");
 		return 1;
 	}
@@ -509,7 +509,7 @@
 	}
 	tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) {
 		fprintf(stderr, "We have an error talking to the kernel\n");
 		ret = -1;
 	}
@@ -571,7 +571,7 @@
 			perror("Cannot send dump request");
 			return 1;
 		}
-		ret = rtnl_dump_filter(&rth, print_action, stdout, NULL, NULL);
+		ret = rtnl_dump_filter(&rth, print_action, stdout);
 	}
 
 	if (event == RTM_DELACTION) {
@@ -579,7 +579,7 @@
 		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) {
+		if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) {
 			fprintf(stderr, "We have an error flushing\n");
 			return 1;
 		}
diff --git a/tc/m_csum.c b/tc/m_csum.c
new file mode 100644
index 0000000..f7da6f0
--- /dev/null
+++ b/tc/m_csum.c
@@ -0,0 +1,246 @@
+/*
+ * m_csum.c	checksum updating action
+ *
+ *		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: Gregoire Baron <baronchon@n7mm.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/tc_act/tc_csum.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void
+explain(void)
+{
+	fprintf(stderr, "Usage: ... csum <UPDATE>\n"
+			"Where: UPDATE := <TARGET> [<UPDATE>]\n"
+			"       TARGET := { ip4h | icmp | igmp |"
+				" tcp | udp | udplite | <SWEETS> }\n"
+			"       SWEETS := { and | or | \'+\' }\n");
+}
+
+static void
+usage(void)
+{
+	explain();
+	exit(-1);
+}
+
+static int
+parse_csum_args(int *argc_p, char ***argv_p, struct tc_csum *sel)
+{
+	int argc = *argc_p;
+	char **argv = *argv_p;
+
+	if (argc <= 0)
+		return -1;
+
+	while(argc > 0) {
+		if ((matches(*argv, "iph") == 0) ||
+		    (matches(*argv, "ip4h") == 0) ||
+		    (matches(*argv, "ipv4h") == 0))
+			sel->update_flags |= TCA_CSUM_UPDATE_FLAG_IPV4HDR;
+
+		else if (matches(*argv, "icmp") == 0)
+			sel->update_flags |= TCA_CSUM_UPDATE_FLAG_ICMP;
+
+		else if (matches(*argv, "igmp") == 0)
+			sel->update_flags |= TCA_CSUM_UPDATE_FLAG_IGMP;
+
+		else if (matches(*argv, "tcp") == 0)
+			sel->update_flags |= TCA_CSUM_UPDATE_FLAG_TCP;
+
+		else if (matches(*argv, "udp") == 0)
+			sel->update_flags |= TCA_CSUM_UPDATE_FLAG_UDP;
+
+		else if (matches(*argv, "udplite") == 0)
+			sel->update_flags |= TCA_CSUM_UPDATE_FLAG_UDPLITE;
+
+		else if ((matches(*argv, "and") == 0) ||
+			 (matches(*argv, "or") == 0) ||
+			 (matches(*argv, "+") == 0))
+			; /* just ignore: ... csum iph and tcp or udp */
+		else
+			break;
+		argc--;
+		argv++;
+	}
+
+	*argc_p = argc;
+	*argv_p = argv;
+
+	return 0;
+}
+
+static int
+parse_csum(struct action_util *a, int *argc_p,
+	   char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+	struct tc_csum sel;
+
+	int argc = *argc_p;
+	char **argv = *argv_p;
+	int ok = 0;
+	struct rtattr *tail;
+
+	memset(&sel, 0, sizeof(sel));
+
+	while (argc > 0) {
+		if (matches(*argv, "csum") == 0) {
+			NEXT_ARG();
+			if (parse_csum_args(&argc, &argv, &sel)) {
+				fprintf(stderr, "Illegal csum construct (%s)\n",
+					*argv);
+				explain();
+				return -1;
+			}
+			ok++;
+			continue;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		}
+		else {
+			break;
+		}
+	}
+
+	if (!ok) {
+		explain();
+		return -1;
+	}
+
+	if (sel.update_flags == 0) {
+		fprintf(stderr, "Illegal csum construct, empty <UPDATE> list\n");
+		return -1;
+	}
+
+	if (argc) {
+		if (matches(*argv, "reclassify") == 0) {
+			sel.action = TC_ACT_RECLASSIFY;
+			argc--;
+			argv++;
+		} else if (matches(*argv, "pipe") == 0) {
+			sel.action = TC_ACT_PIPE;
+			argc--;
+			argv++;
+		} else if (matches(*argv, "drop") == 0 ||
+			matches(*argv, "shot") == 0) {
+			sel.action = TC_ACT_SHOT;
+			argc--;
+			argv++;
+		} else if (matches(*argv, "continue") == 0) {
+			sel.action = TC_ACT_UNSPEC;
+			argc--;
+			argv++;
+		} else if (matches(*argv, "pass") == 0) {
+			sel.action = TC_ACT_OK;
+			argc--;
+			argv++;
+		}
+	}
+
+	if (argc) {
+		if (matches(*argv, "index") == 0) {
+			NEXT_ARG();
+			if (get_u32(&sel.index, *argv, 10)) {
+				fprintf(stderr, "Illegal \"index\" (%s) <csum>\n",
+					*argv);
+				return -1;
+			}
+			argc--;
+			argv++;
+		}
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+	addattr_l(n, MAX_MSG, TCA_CSUM_PARMS, &sel, sizeof(sel));
+	tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail;
+
+	*argc_p = argc;
+	*argv_p = argv;
+
+	return 0;
+}
+
+static int
+print_csum(struct action_util *au, FILE * f, struct rtattr *arg)
+{
+	struct tc_csum *sel;
+
+	struct rtattr *tb[TCA_CSUM_MAX + 1];
+
+	char *uflag_1 = "";
+	char *uflag_2 = "";
+	char *uflag_3 = "";
+	char *uflag_4 = "";
+	char *uflag_5 = "";
+	char *uflag_6 = "";
+	SPRINT_BUF(action_buf);
+
+	int uflag_count = 0;
+
+	if (arg == NULL)
+		return -1;
+
+	parse_rtattr_nested(tb, TCA_CSUM_MAX, arg);
+
+	if (tb[TCA_CSUM_PARMS] == NULL) {
+		fprintf(f, "[NULL csum parameters]");
+		return -1;
+	}
+	sel = RTA_DATA(tb[TCA_CSUM_PARMS]);
+
+	if (sel->update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) {
+		uflag_1 = "iph";
+		uflag_count++;
+	}
+	#define CSUM_UFLAG_BUFFER(flag_buffer, flag_value, flag_string)	\
+		do {							\
+			if (sel->update_flags & flag_value) {		\
+				flag_buffer = uflag_count > 0 ?		\
+					", " flag_string : flag_string; \
+				uflag_count++;				\
+			}						\
+		} while(0)
+	CSUM_UFLAG_BUFFER(uflag_2, TCA_CSUM_UPDATE_FLAG_ICMP, "icmp");
+	CSUM_UFLAG_BUFFER(uflag_3, TCA_CSUM_UPDATE_FLAG_IGMP, "igmp");
+	CSUM_UFLAG_BUFFER(uflag_4, TCA_CSUM_UPDATE_FLAG_TCP, "tcp");
+	CSUM_UFLAG_BUFFER(uflag_5, TCA_CSUM_UPDATE_FLAG_UDP, "udp");
+	CSUM_UFLAG_BUFFER(uflag_6, TCA_CSUM_UPDATE_FLAG_UDPLITE, "udplite");
+	if (!uflag_count) {
+		uflag_1 = "?empty";
+	}
+
+	fprintf(f, "csum (%s%s%s%s%s%s) action %s\n",
+		uflag_1, uflag_2, uflag_3,
+		uflag_4, uflag_5, uflag_6,
+		action_n2a(sel->action, action_buf, sizeof(action_buf)));
+	fprintf(f, "\tindex %d ref %d bind %d", sel->index, sel->refcnt, sel->bindcnt);
+
+	if (show_stats) {
+		if (tb[TCA_CSUM_TM]) {
+			struct tcf_t *tm = RTA_DATA(tb[TCA_CSUM_TM]);
+			print_tm(f,tm);
+		}
+	}
+	fprintf(f, "\n");
+
+	return 0;
+}
+
+struct action_util csum_action_util = {
+	.id = "csum",
+	.parse_aopt = parse_csum,
+	.print_aopt = print_csum,
+};
diff --git a/tc/m_ipt.c b/tc/m_ipt.c
index 99d9965..dc2dedc 100644
--- a/tc/m_ipt.c
+++ b/tc/m_ipt.c
@@ -281,6 +281,7 @@
 			fputs(dlerror(), stderr);
 			printf("\n");
 			free(new_name);
+			free(lname);
 			return NULL;
 		}
 	}
@@ -297,6 +298,7 @@
 					fprintf(stderr, "\n");
 					dlclose(handle);
 					free(new_name);
+					free(lname);
 					return NULL;
 				}
 			}
@@ -304,6 +306,7 @@
 	}
 
 	free(new_name);
+	free(lname);
 	return m;
 }
 
@@ -393,11 +396,9 @@
 	char **argv = *argv_p;
 	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;
 
 	lib_dir = getenv("IPTABLES_LIB_DIR");
 	if (!lib_dir)
@@ -551,7 +552,7 @@
 		fprintf(f, "[NULL ipt table name ] assuming mangle ");
 	} else {
 		fprintf(f, "tablename: %s ",
-			(char *) RTA_DATA(tb[TCA_IPT_TABLE]));
+			rta_getattr_str(tb[TCA_IPT_TABLE]));
 	}
 
 	if (tb[TCA_IPT_HOOK] == NULL) {
@@ -559,7 +560,7 @@
 		return -1;
 	} else {
 		__u32 hook;
-		hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
+		hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
 		fprintf(f, " hook: %s \n", ipthooks[hook]);
 	}
 
@@ -590,7 +591,7 @@
 			fprintf(f, " [NULL ipt target index ]\n");
 		} else {
 			__u32 index;
-			index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
+			index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
 			fprintf(f, " \n\tindex %d", index);
 		}
 
diff --git a/tc/m_police.c b/tc/m_police.c
index ace43b5..c3869b6 100644
--- a/tc/m_police.c
+++ b/tc/m_police.c
@@ -353,7 +353,7 @@
 	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, "avrate %s ", sprint_rate(rta_getattr_u32(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)));
diff --git a/tc/m_skbedit.c b/tc/m_skbedit.c
index 990b9c7..c7eef44 100644
--- a/tc/m_skbedit.c
+++ b/tc/m_skbedit.c
@@ -160,7 +160,6 @@
 
 static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg)
 {
-	struct tc_skbedit *sel;
 	struct rtattr *tb[TCA_SKBEDIT_MAX + 1];
 	SPRINT_BUF(b1);
 	__u32 *priority;
@@ -177,8 +176,6 @@
 		return -1;
 	}
 
-	sel = RTA_DATA(tb[TCA_SKBEDIT_PARMS]);
-
 	fprintf(f, " skbedit");
 
 	if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
diff --git a/tc/m_xt.c b/tc/m_xt.c
index bfc4937..bcc4d75 100644
--- a/tc/m_xt.c
+++ b/tc/m_xt.c
@@ -31,7 +31,6 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <stdarg.h>
-#include <limits.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/wait.h>
@@ -88,7 +87,7 @@
 		target->t = xtables_calloc(1, size);
 		target->t->u.target_size = size;
 		strcpy(target->t->u.user.name, target->name);
-		xtables_set_revision(target->t->u.user.name, target->revision);
+		target->t->u.user.revision = target->revision;
 
 		if (target->init != NULL)
 			target->init(target->t);
@@ -124,11 +123,9 @@
 	char **argv = *argv_p;
 	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;
 
 	xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
 	set_lib_dir();
@@ -162,9 +159,13 @@
 					return -1;
 				}
 				tcipt_globals.opts =
-				    xtables_merge_options(tcipt_globals.opts,
-				                          m->extra_opts,
-				                          &m->option_offset);
+				    xtables_merge_options(
+#if (XTABLES_VERSION_CODE >= 6)
+				        tcipt_globals.orig_opts,
+#endif
+				        tcipt_globals.opts,
+				        m->extra_opts,
+				        &m->option_offset);
 			} else {
 				fprintf(stderr," failed to find target %s\n\n", optarg);
 				return -1;
@@ -251,13 +252,15 @@
 
 	optind = 0;
 	xtables_free_opts(1);
-	/* Clear flags if target will be used again */
-        m->tflags=0;
-        m->used=0;
-	/* Free allocated memory */
-        if (m->t)
-            free(m->t);
 
+	if (m) {
+		/* Clear flags if target will be used again */
+		m->tflags = 0;
+		m->used = 0;
+		/* Free allocated memory */
+		if (m->t)
+			free(m->t);
+	}
 
 	return 0;
 
@@ -281,7 +284,7 @@
 		fprintf(f, "[NULL ipt table name ] assuming mangle ");
 	} else {
 		fprintf(f, "tablename: %s ",
-			(char *) RTA_DATA(tb[TCA_IPT_TABLE]));
+			rta_getattr_str(tb[TCA_IPT_TABLE]));
 	}
 
 	if (tb[TCA_IPT_HOOK] == NULL) {
@@ -289,7 +292,7 @@
 		return -1;
 	} else {
 		__u32 hook;
-		hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
+		hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
 		fprintf(f, " hook: %s \n", ipthooks[hook]);
 	}
 
@@ -307,7 +310,11 @@
 			}
 
 			tcipt_globals.opts =
-			    xtables_merge_options(tcipt_globals.opts,
+			    xtables_merge_options(
+#if (XTABLES_VERSION_CODE >= 6)
+				                  tcipt_globals.orig_opts,
+#endif
+				                  tcipt_globals.opts,
 			                          m->extra_opts,
 			                          &m->option_offset);
 		} else {
@@ -321,7 +328,7 @@
 			fprintf(f, " [NULL ipt target index ]\n");
 		} else {
 			__u32 index;
-			index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
+			index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
 			fprintf(f, " \n\tindex %d", index);
 		}
 
@@ -343,8 +350,8 @@
 	return 0;
 }
 
-struct action_util ipt_action_util = {
-        .id = "ipt",
+struct action_util xt_action_util = {
+        .id = "xt",
         .parse_aopt = parse_ipt,
         .print_aopt = print_ipt,
 };
diff --git a/tc/m_xt_old.c b/tc/m_xt_old.c
index 3804d7f..554e4ed 100644
--- a/tc/m_xt_old.c
+++ b/tc/m_xt_old.c
@@ -215,11 +215,9 @@
 	char **argv = *argv_p;
 	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;
 
 	set_lib_dir();
 
@@ -369,7 +367,7 @@
 		fprintf(f, "[NULL ipt table name ] assuming mangle ");
 	} else {
 		fprintf(f, "tablename: %s ",
-			(char *) RTA_DATA(tb[TCA_IPT_TABLE]));
+			rta_getattr_str(tb[TCA_IPT_TABLE]));
 	}
 
 	if (tb[TCA_IPT_HOOK] == NULL) {
@@ -377,7 +375,7 @@
 		return -1;
 	} else {
 		__u32 hook;
-		hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
+		hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
 		fprintf(f, " hook: %s \n", ipthooks[hook]);
 	}
 
@@ -408,7 +406,7 @@
 			fprintf(f, " [NULL ipt target index ]\n");
 		} else {
 			__u32 index;
-			index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
+			index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
 			fprintf(f, " \n\tindex %d", index);
 		}
 
diff --git a/tc/q_atm.c b/tc/q_atm.c
index eec0d77..2598e29 100644
--- a/tc/q_atm.c
+++ b/tc/q_atm.c
@@ -210,11 +210,11 @@
 	}
 	if (tb[TCA_ATM_HDR]) {
 		int i;
+		const __u8 *hdr = RTA_DATA(tb[TCA_ATM_HDR]);
 
 		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]);
+			fprintf(f,"%c%02x", i ? '.' : ' ', hdr[i]);
 		if (!i) fprintf(f," .");
 		fprintf(f," ");
 	}
@@ -224,7 +224,7 @@
 		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]);
+			excess = rta_getattr_u32(tb[TCA_ATM_EXCESS]);
 			if (!excess) fprintf(f,"excess clp ");
 			else {
 				char buf[64];
diff --git a/tc/q_choke.c b/tc/q_choke.c
new file mode 100644
index 0000000..6fbcadf
--- /dev/null
+++ b/tc/q_choke.c
@@ -0,0 +1,227 @@
+/*
+ * q_choke.c		CHOKE.
+ *
+ *		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@vyatta.com>
+ */
+
+#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_red.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... choke limit PACKETS bandwidth KBPS [ecn]\n");
+	fprintf(stderr, "                 [ min PACKETS ] [ max PACKETS ] [ burst PACKETS ]\n");
+}
+
+static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			   struct nlmsghdr *n)
+{
+	struct tc_red_qopt opt;
+	unsigned burst = 0;
+	unsigned avpkt = 1000;
+	double probability = 0.02;
+	unsigned rate = 0;
+	int ecn_ok = 0;
+	int wlog;
+	__u8 sbuf[256];
+	__u32 max_P;
+	struct rtattr *tail;
+
+	memset(&opt, 0, sizeof(opt));
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&opt.limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "bandwidth") == 0) {
+			NEXT_ARG();
+			if (get_rate(&rate, *argv)) {
+				fprintf(stderr, "Illegal \"bandwidth\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn_ok = 1;
+		} else if (strcmp(*argv, "min") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&opt.qth_min, *argv, 0)) {
+				fprintf(stderr, "Illegal \"min\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "max") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&opt.qth_max, *argv, 0)) {
+				fprintf(stderr, "Illegal \"max\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "burst") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&burst, *argv, 0)) {
+				fprintf(stderr, "Illegal \"burst\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "avpkt") == 0) {
+			NEXT_ARG();
+			if (get_size(&avpkt, *argv)) {
+				fprintf(stderr, "Illegal \"avpkt\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "probability") == 0) {
+			NEXT_ARG();
+			if (sscanf(*argv, "%lg", &probability) != 1) {
+				fprintf(stderr, "Illegal \"probability\"\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++;
+	}
+
+	if (!rate || !opt.limit) {
+		fprintf(stderr, "Required parameter (bandwidth, limit) is missing\n");
+		return -1;
+	}
+
+	/* Compute default min/max thresholds based on 
+	   Sally Floyd's recommendations:
+	   http://www.icir.org/floyd/REDparameters.txt
+	*/
+	if (!opt.qth_max) 
+		opt.qth_max = opt.limit / 4;
+	if (!opt.qth_min)
+		opt.qth_min = opt.qth_max / 3;
+	if (!burst)
+		burst = (2 * opt.qth_min + opt.qth_max) / 3;
+
+	if (opt.qth_max > opt.limit) {
+		fprintf(stderr, "\"max\" is larger than \"limit\"\n");
+		return -1;
+	}
+
+	if (opt.qth_min >= opt.qth_max) {
+		fprintf(stderr, "\"min\" is not smaller than \"max\"\n");
+		return -1;
+	}
+
+	wlog = tc_red_eval_ewma(opt.qth_min*avpkt, burst, avpkt);
+	if (wlog < 0) {
+		fprintf(stderr, "CHOKE: failed to calculate EWMA constant.\n");
+		return -1;
+	}
+	if (wlog >= 10)
+		fprintf(stderr, "CHOKE: WARNING. Burst %d seems to be too large.\n", burst);
+	opt.Wlog = wlog;
+
+	wlog = tc_red_eval_P(opt.qth_min*avpkt, opt.qth_max*avpkt, probability);
+	if (wlog < 0) {
+		fprintf(stderr, "CHOKE: failed to calculate probability.\n");
+		return -1;
+	}
+	opt.Plog = wlog;
+
+	wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf);
+	if (wlog < 0) {
+		fprintf(stderr, "CHOKE: failed to calculate idle damping table.\n");
+		return -1;
+	}
+	opt.Scell_log = wlog;
+	if (ecn_ok)
+		opt.flags |= TC_RED_ECN;
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	addattr_l(n, 1024, TCA_CHOKE_PARMS, &opt, sizeof(opt));
+	addattr_l(n, 1024, TCA_CHOKE_STAB, sbuf, 256);
+	max_P = probability * pow(2, 32);
+	addattr_l(n, 1024, TCA_CHOKE_MAX_P, &max_P, sizeof(max_P));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int choke_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CHOKE_MAX+1];
+	const struct tc_red_qopt *qopt;
+	__u32 max_P = 0;
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CHOKE_MAX, opt);
+
+	if (tb[TCA_CHOKE_PARMS] == NULL)
+		return -1;
+	qopt = RTA_DATA(tb[TCA_CHOKE_PARMS]);
+	if (RTA_PAYLOAD(tb[TCA_CHOKE_PARMS])  < sizeof(*qopt))
+		return -1;
+	if (tb[TCA_CHOKE_MAX_P] &&
+	    RTA_PAYLOAD(tb[TCA_CHOKE_MAX_P]) >= sizeof(__u32))
+		max_P = rta_getattr_u32(tb[TCA_CHOKE_MAX_P]);
+
+	fprintf(f, "limit %up min %up max %up ",
+		qopt->limit, qopt->qth_min, qopt->qth_max);
+
+	if (qopt->flags & TC_RED_ECN)
+		fprintf(f, "ecn ");
+
+	if (show_details) {
+		fprintf(f, "ewma %u ", qopt->Wlog);
+		if (max_P)
+			fprintf(f, "probability %g ", max_P / pow(2, 32));
+		else
+			fprintf(f, "Plog %u ", qopt->Plog);
+		fprintf(f, "Scell_log %u", qopt->Scell_log);
+	}
+	return 0;
+}
+
+static int choke_print_xstats(struct qdisc_util *qu, FILE *f,
+			      struct rtattr *xstats)
+{
+	struct tc_choke_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 matched %u",
+		st->marked, st->early, st->pdrop, st->other, st->matched);
+	return 0;
+
+}
+
+struct qdisc_util choke_qdisc_util = {
+	.id		= "choke",
+	.parse_qopt	= choke_parse_opt,
+	.print_qopt	= choke_print_opt,
+	.print_xstats	= choke_print_xstats,
+};
diff --git a/tc/q_drr.c b/tc/q_drr.c
index 81de44d..746736d 100644
--- a/tc/q_drr.c
+++ b/tc/q_drr.c
@@ -36,7 +36,7 @@
 
 static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
 {
-	while (argc > 0) {
+	while (argc) {
 		if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -45,7 +45,6 @@
 			explain();
 			return -1;
 		}
-		argc--; argv++;
 	}
 	return 0;
 }
@@ -94,7 +93,7 @@
 
 	if (tb[TCA_DRR_QUANTUM])
 		fprintf(f, "quantum %s ",
-			sprint_size(*(__u32 *)RTA_DATA(tb[TCA_DRR_QUANTUM]), b1));
+			sprint_size(rta_getattr_u32(tb[TCA_DRR_QUANTUM]), b1));
 	return 0;
 }
 
diff --git a/tc/q_dsmark.c b/tc/q_dsmark.c
index cfbb335..05185c0 100644
--- a/tc/q_dsmark.c
+++ b/tc/q_dsmark.c
@@ -140,25 +140,25 @@
 		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]));
+			    rta_getattr_u8(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]));
+			    rta_getattr_u8(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]));
+			    rta_getattr_u16(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]));
+			    rta_getattr_u16(tb[TCA_DSMARK_DEFAULT_INDEX]));
 	}
 	if (tb[TCA_DSMARK_SET_TC_INDEX]) fprintf(f,"set_tc_index ");
 	return 0;
diff --git a/tc/q_gred.c b/tc/q_gred.c
index df4aa3d..a4df3a6 100644
--- a/tc/q_gred.c
+++ b/tc/q_gred.c
@@ -21,6 +21,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <string.h>
+#include <math.h>
 
 #include "utils.h"
 #include "tc_util.h"
@@ -51,7 +52,7 @@
 {
 
 	struct rtattr *tail;
-	struct tc_gred_sopt opt;
+	struct tc_gred_sopt opt = { 0 };
 	int dps = 0;
 	int def_dp = -1;
 
@@ -83,7 +84,7 @@
 				return -1;
 			}
 		} else if (strcmp(*argv, "grio") == 0) {
-			opt.grio=1;
+			opt.grio = 1;
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -100,7 +101,6 @@
 		return -1;
 	}
 
-	memset(&opt, 0, sizeof(struct tc_gred_sopt));
 	opt.DPs = dps;
 	opt.def_DP = def_dp;
 
@@ -126,6 +126,7 @@
 	int wlog;
 	__u8 sbuf[256];
 	struct rtattr *tail;
+	__u32 max_P;
 
 	memset(&opt, 0, sizeof(opt));
 
@@ -216,19 +217,23 @@
 	if (rate == 0)
 		get_rate(&rate, "10Mbit");
 
-	if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt ||
+	if (!opt.qth_min || !opt.qth_max || !opt.limit || !avpkt ||
 	    (opt.DP<0)) {
-		fprintf(stderr, "Required parameter (min, max, burst, limit, "
+		fprintf(stderr, "Required parameter (min, max, limit, "
 		    "avpkt, DP) is missing\n");
 		return -1;
 	}
+	if (!burst) {
+		burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt);
+		fprintf(stderr, "GRED: set burst to %u\n", burst);
+	}
 
 	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 "
+		fprintf(stderr, "GRED: WARNING. Burst %d seems to be too "
 		    "large.\n", burst);
 	opt.Wlog = wlog;
 	if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) {
@@ -248,14 +253,17 @@
 	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);
+	max_P = probability * pow(2, 32);
+	addattr32(n, 1024, TCA_GRED_MAX_P, max_P);
 	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 rtattr *tb[TCA_GRED_MAX + 1];
 	struct tc_gred_qopt *qopt;
+	__u32 *max_p = NULL;
 	int i;
 	SPRINT_BUF(b1);
 	SPRINT_BUF(b2);
@@ -266,11 +274,15 @@
 	if (opt == NULL)
 		return 0;
 
-	parse_rtattr_nested(tb, TCA_GRED_STAB, opt);
+	parse_rtattr_nested(tb, TCA_GRED_MAX, opt);
 
 	if (tb[TCA_GRED_PARMS] == NULL)
 		return -1;
 
+	if (tb[TCA_GRED_MAX_P] &&
+	    RTA_PAYLOAD(tb[TCA_GRED_MAX_P]) >= sizeof(__u32) * MAX_DPs)
+		max_p = RTA_DATA(tb[TCA_GRED_MAX_P]);
+
 	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");
@@ -299,8 +311,12 @@
 				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);
+		fprintf(f, "ewma %u ", qopt->Wlog);
+		if (max_p)
+			fprintf(f, "probability %lg ", max_p[i] / pow(2, 32));
+		else
+			fprintf(f, "Plog %u ", qopt->Plog);
+		fprintf(f, "Scell_log %u", qopt->Scell_log);
 	}
 	return 0;
 }
diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c
index b190c71..03539ec 100644
--- a/tc/q_hfsc.c
+++ b/tc/q_hfsc.c
@@ -43,7 +43,7 @@
 	fprintf(stderr,
 		"Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n"
 		"\n"
-		"SC := [ [ m1 BPS ] [ d SEC ] m2 BPS\n"
+		"SC := [ [ m1 BPS ] d SEC ] m2 BPS\n"
 		"\n"
 		" m1 : slope of first segment\n"
 		" d  : x-coordinate of intersection\n"
@@ -57,6 +57,10 @@
 		" dmax : maximum delay\n"
 		" rate : rate\n"
 		"\n"
+		"Remarks:\n"
+		" - at least one of 'rt', 'ls' or 'sc' must be specified\n"
+		" - 'ul' can only be specified with 'ls' or 'sc'\n"
+		"\n"
 	);
 }
 
diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c
new file mode 100644
index 0000000..b2d8c12
--- /dev/null
+++ b/tc/q_mqprio.c
@@ -0,0 +1,131 @@
+/*
+ * q_mqprio.c	MQ prio 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.
+ *
+ * Author:	John Fastabend, <john.r.fastabend@intel.com>
+ */
+
+#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: ... mqprio [num_tc NUMBER] [map P0 P1 ...]\n");
+	fprintf(stderr, "                  [queues count1@offset1 count2@offset2 ...] ");
+	fprintf(stderr, "[hw 1|0]\n");
+}
+
+static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
+			    char **argv, struct nlmsghdr *n)
+{
+	int idx;
+	struct tc_mqprio_qopt opt = {
+				     8,
+				     {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 1, 3, 3, 3, 3},
+				     1,
+				    };
+
+	while (argc > 0) {
+		idx = 0;
+		if (strcmp(*argv, "num_tc") == 0) {
+			NEXT_ARG();
+			if (get_u8(&opt.num_tc, *argv, 10)) {
+				fprintf(stderr, "Illegal \"num_tc\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "map") == 0) {
+			while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
+				NEXT_ARG();
+				if (get_u8(&opt.prio_tc_map[idx], *argv, 10)) {
+					PREV_ARG();
+					break;
+				}
+				idx++;
+			}
+			for ( ; idx < TC_QOPT_MAX_QUEUE; idx++)
+				opt.prio_tc_map[idx] = 0;
+		} else if (strcmp(*argv, "queues") == 0) {
+			char *tmp, *tok;
+
+			while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
+				NEXT_ARG();
+
+				tmp = strdup(*argv);
+				if (!tmp)
+					break;
+
+				tok = strtok(tmp, "@");
+				if (get_u16(&opt.count[idx], tok, 10)) {
+					free(tmp);
+					PREV_ARG();
+					break;
+				}
+				tok = strtok(NULL, "@");
+				if (get_u16(&opt.offset[idx], tok, 10)) {
+					free(tmp);
+					PREV_ARG();
+					break;
+				}
+				free(tmp);
+				idx++;
+			}
+		} else if (strcmp(*argv, "hw") == 0) {
+			NEXT_ARG();
+			if (get_u8(&opt.hw, *argv, 10)) {
+				fprintf(stderr, "Illegal \"hw\"\n");
+				return -1;
+			}
+			idx++;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "Unknown argument\n");
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+	return 0;
+}
+
+int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	int i;
+	struct tc_mqprio_qopt *qopt;
+
+	if (opt == NULL)
+		return 0;
+
+	qopt = RTA_DATA(opt);
+
+	fprintf(f, " tc %u map ", qopt->num_tc);
+	for (i = 0; i <= TC_PRIO_MAX; i++)
+		fprintf(f, "%u ", qopt->prio_tc_map[i]);
+	fprintf(f, "\n             queues:");
+	for (i = 0; i < qopt->num_tc; i++)
+		fprintf(f, "(%u:%u) ", qopt->offset[i],
+			qopt->offset[i] + qopt->count[i] - 1);
+	return 0;
+}
+
+struct qdisc_util mqprio_qdisc_util = {
+	.id		= "mqprio",
+	.parse_qopt	= mqprio_parse_opt,
+	.print_qopt	= mqprio_print_opt,
+};
diff --git a/tc/q_multiq.c b/tc/q_multiq.c
index fce5e44..7ff9e74 100644
--- a/tc/q_multiq.c
+++ b/tc/q_multiq.c
@@ -46,7 +46,7 @@
 {
 	struct tc_multiq_qopt opt;
 
-	if (argc > 0) {
+	if (argc) {
 		if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -55,7 +55,6 @@
 			explain();
 			return -1;
 		}
-		argc--; argv++;
 	}
 
 	addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
diff --git a/tc/q_netem.c b/tc/q_netem.c
index 6aaaded..360080c 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -6,12 +6,14 @@
  *		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>
+ * Authors:	Stephen Hemminger <shemminger@linux-foundation.org>
  *
  */
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
 #include <unistd.h>
 #include <syslog.h>
 #include <fcntl.h>
@@ -31,10 +33,13 @@
 "Usage: ... netem [ limit PACKETS ] \n" \
 "                 [ delay TIME [ JITTER [CORRELATION]]]\n" \
 "                 [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
-"                 [ drop PERCENT [CORRELATION]] \n" \
 "                 [ corrupt PERCENT [CORRELATION]] \n" \
 "                 [ duplicate PERCENT [CORRELATION]]\n" \
-"                 [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n");
+"                 [ loss random PERCENT [CORRELATION]]\n" \
+"                 [ loss state P13 [P31 [P32 [P23 P14]]]\n" \
+"                 [ loss gemodel PERCENT [R [1-H [1-K]]]\n" \
+"                 [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n" \
+"                 [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n");
 }
 
 static void explain1(const char *arg)
@@ -42,11 +47,56 @@
 	fprintf(stderr, "Illegal \"%s\"\n", arg);
 }
 
-/* Upper bound on size of distribution 
+/* Upper bound on size of distribution
  *  really (TCA_BUF_MAX - other headers) / sizeof (__s16)
  */
 #define MAX_DIST	(16*1024)
 
+static const double max_percent_value = 0xffffffff;
+
+/* scaled value used to percent of maximum. */
+static void set_percent(__u32 *percent, double per)
+{
+	*percent = (unsigned) rint(per * max_percent_value);
+}
+
+
+/* Parse either a fraction '.3' or percent '30%
+ * return: 0 = ok, -1 = error, 1 = out of range
+ */
+static int parse_percent(double *val, const char *str)
+{
+	char *p;
+
+	*val = strtod(str, &p) / 100.;
+	if (*p && strcmp(p, "%") )
+		return -1;
+
+	return 0;
+}
+
+static int get_percent(__u32 *percent, const char *str)
+{
+	double per;
+
+	if (parse_percent(&per, str))
+		return -1;
+
+	set_percent(percent, per);
+	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;
+}
+
 /*
  * Simplistic file parser for distrbution data.
  * Format is:
@@ -95,14 +145,7 @@
 	return n;
 }
 
-static int isnumber(const char *arg)
-{
-	char *p;
-
-	return strtod(arg, &p) != 0 || p != arg;
-}
-
-#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isnumber(argv[1]))
+#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
 
 /* Adjust for the fact that psched_ticks aren't always usecs
    (based on kernel PSCHED_CLOCK configuration */
@@ -125,23 +168,26 @@
 static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
 			   struct nlmsghdr *n)
 {
-	size_t dist_size = 0;
+	int dist_size = 0;
 	struct rtattr *tail;
-	struct tc_netem_qopt opt;
+	struct tc_netem_qopt opt = { .limit = 1000 };
 	struct tc_netem_corr cor;
 	struct tc_netem_reorder reorder;
 	struct tc_netem_corrupt corrupt;
+	struct tc_netem_gimodel gimodel;
+	struct tc_netem_gemodel gemodel;
+	struct tc_netem_rate rate;
 	__s16 *dist_data = NULL;
+	__u16 loss_type = NETEM_LOSS_UNSPEC;
 	int present[__TCA_NETEM_MAX];
 
-	memset(&opt, 0, sizeof(opt));
-	opt.limit = 1000;
 	memset(&cor, 0, sizeof(cor));
 	memset(&reorder, 0, sizeof(reorder));
 	memset(&corrupt, 0, sizeof(corrupt));
+	memset(&rate, 0, sizeof(rate));
 	memset(present, 0, sizeof(present));
 
-	while (argc > 0) {
+	for( ; argc > 0; --argc, ++argv) {
 		if (matches(*argv, "limit") == 0) {
 			NEXT_ARG();
 			if (get_size(&opt.limit, *argv)) {
@@ -166,7 +212,7 @@
 				if (NEXT_IS_NUMBER()) {
 					NEXT_ARG();
 					++present[TCA_NETEM_CORR];
-					if (get_percent(&cor.delay_corr,							*argv)) {
+					if (get_percent(&cor.delay_corr, *argv)) {
 						explain1("latency");
 						return -1;
 					}
@@ -174,18 +220,111 @@
 			}
 		} else if (matches(*argv, "loss") == 0 ||
 			   matches(*argv, "drop") == 0) {
-			NEXT_ARG();
-			if (get_percent(&opt.loss, *argv)) {
-				explain1("loss");
+			if (opt.loss > 0 || loss_type != NETEM_LOSS_UNSPEC) {
+				explain1("duplicate loss argument\n");
 				return -1;
 			}
-			if (NEXT_IS_NUMBER()) {
+
+			NEXT_ARG();
+			/* Old (deprecated) random loss model syntax */
+			if (isdigit(argv[0][0]))
+				goto random_loss_model;
+
+			if (!strcmp(*argv, "random")) {
 				NEXT_ARG();
-				++present[TCA_NETEM_CORR];
-				if (get_percent(&cor.loss_corr, *argv)) {
-					explain1("loss");
+	random_loss_model:
+				if (get_percent(&opt.loss, *argv)) {
+					explain1("loss percent");
 					return -1;
 				}
+				if (NEXT_IS_NUMBER()) {
+					NEXT_ARG();
+					++present[TCA_NETEM_CORR];
+					if (get_percent(&cor.loss_corr, *argv)) {
+						explain1("loss correllation");
+						return -1;
+					}
+				}
+			} else if (!strcmp(*argv, "state")) {
+				double p13;
+
+				NEXT_ARG();
+				if (parse_percent(&p13, *argv)) {
+					explain1("loss p13");
+					return -1;
+				}
+
+				/* set defaults */
+				set_percent(&gimodel.p13, p13);
+				set_percent(&gimodel.p31, 1. - p13);
+				set_percent(&gimodel.p32, 0);
+				set_percent(&gimodel.p23, 1.);
+				loss_type = NETEM_LOSS_GI;
+
+				if (!NEXT_IS_NUMBER())
+					continue;
+				NEXT_ARG();
+				if (get_percent(&gimodel.p31, *argv)) {
+					explain1("loss p31");
+					return -1;
+				}
+
+				if (!NEXT_IS_NUMBER())
+					continue;
+				NEXT_ARG();
+				if (get_percent(&gimodel.p32, *argv)) {
+					explain1("loss p32");
+					return -1;
+				}
+
+				if (!NEXT_IS_NUMBER())
+					continue;
+				NEXT_ARG();
+				if (get_percent(&gimodel.p23, *argv)) {
+					explain1("loss p23");
+					return -1;
+				}
+
+			} else if (!strcmp(*argv, "gemodel")) {
+				NEXT_ARG();
+				if (get_percent(&gemodel.p, *argv)) {
+					explain1("loss gemodel p");
+					return -1;
+				}
+
+				/* set defaults */
+				set_percent(&gemodel.r, 1.);
+				set_percent(&gemodel.h, 0);
+				set_percent(&gemodel.k1, 1.);
+				loss_type = NETEM_LOSS_GE;
+
+				if (!NEXT_IS_NUMBER())
+					continue;
+				NEXT_ARG();
+				if (get_percent(&gemodel.r, *argv)) {
+					explain1("loss gemodel r");
+					return -1;
+				}
+
+				if (!NEXT_IS_NUMBER())
+					continue;
+				NEXT_ARG();
+				if (get_percent(&gemodel.h, *argv)) {
+					explain1("loss gemodel h");
+					return -1;
+				}
+
+				if (!NEXT_IS_NUMBER())
+					continue;
+				NEXT_ARG();
+				if (get_percent(&gemodel.k1, *argv)) {
+					explain1("loss gemodel k");
+					return -1;
+				}
+			} else {
+				fprintf(stderr, "Unknown loss parameter: %s\n",
+					*argv);
+				return -1;
 			}
 		} else if (matches(*argv, "reorder") == 0) {
 			NEXT_ARG();
@@ -244,6 +383,34 @@
 				free(dist_data);
 				return -1;
 			}
+		} else if (matches(*argv, "rate") == 0) {
+			++present[TCA_NETEM_RATE];
+			NEXT_ARG();
+			if (get_rate(&rate.rate, *argv)) {
+				explain1("rate");
+				return -1;
+			}
+			if (NEXT_IS_NUMBER()) {
+				NEXT_ARG();
+				if (get_s32(&rate.packet_overhead, *argv, 0)) {
+					explain1("rate");
+					return -1;
+				}
+			}
+			if (NEXT_IS_NUMBER()) {
+				NEXT_ARG();
+				if (get_u32(&rate.cell_size, *argv, 0)) {
+					explain1("rate");
+					return -1;
+				}
+			}
+			if (NEXT_IS_NUMBER()) {
+				NEXT_ARG();
+				if (get_s32(&rate.cell_overhead, *argv, 0)) {
+					explain1("rate");
+					return -1;
+				}
+			}
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -252,7 +419,6 @@
 			explain();
 			return -1;
 		}
-		argc--; argv++;
 	}
 
 	tail = NLMSG_TAIL(n);
@@ -260,6 +426,8 @@
 	if (reorder.probability) {
 		if (opt.latency == 0) {
 			fprintf(stderr, "reordering not possible without specifying some delay\n");
+			explain();
+			return -1;
 		}
 		if (opt.gap == 0)
 			opt.gap = 1;
@@ -282,7 +450,7 @@
 	    addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
 			return -1;
 
-	if (present[TCA_NETEM_REORDER] && 
+	if (present[TCA_NETEM_REORDER] &&
 	    addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
 		return -1;
 
@@ -290,6 +458,30 @@
 	    addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
 		return -1;
 
+	if (loss_type != NETEM_LOSS_UNSPEC) {
+		struct rtattr *start;
+
+		start = addattr_nest(n, 1024, TCA_NETEM_LOSS | NLA_F_NESTED);
+		if (loss_type == NETEM_LOSS_GI) {
+			if (addattr_l(n, 1024, NETEM_LOSS_GI,
+				      &gimodel, sizeof(gimodel)) < 0)
+			    return -1;
+		} else if (loss_type == NETEM_LOSS_GE) {
+			if (addattr_l(n, 1024, NETEM_LOSS_GE,
+				      &gemodel, sizeof(gemodel)) < 0)
+			    return -1;
+		} else {
+			fprintf(stderr, "loss in the weeds!\n");
+			return -1;
+		}
+		
+		addattr_nest_end(n, start);
+	}
+
+	if (present[TCA_NETEM_RATE] &&
+	    addattr_l(n, 1024, TCA_NETEM_RATE, &rate, sizeof(rate)) < 0)
+		return -1;
+
 	if (dist_data) {
 		if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
 			      TCA_NETEM_DELAY_DIST,
@@ -306,7 +498,10 @@
 	const struct tc_netem_corr *cor = NULL;
 	const struct tc_netem_reorder *reorder = NULL;
 	const struct tc_netem_corrupt *corrupt = NULL;
+	const struct tc_netem_gimodel *gimodel = NULL;
+	const struct tc_netem_gemodel *gemodel = NULL;
 	struct tc_netem_qopt qopt;
+	const struct tc_netem_rate *rate = NULL;
 	int len = RTA_PAYLOAD(opt) - sizeof(qopt);
 	SPRINT_BUF(b1);
 
@@ -339,6 +534,20 @@
 				return -1;
 			corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
 		}
+		if (tb[TCA_NETEM_LOSS]) {
+			struct rtattr *lb[NETEM_LOSS_MAX + 1];
+
+			parse_rtattr_nested(lb, NETEM_LOSS_MAX, tb[TCA_NETEM_LOSS]);
+			if (lb[NETEM_LOSS_GI])
+				gemodel = RTA_DATA(lb[NETEM_LOSS_GI]);
+			if (lb[NETEM_LOSS_GE])
+				gemodel = RTA_DATA(lb[NETEM_LOSS_GE]);
+		}			
+		if (tb[TCA_NETEM_RATE]) {
+			if (RTA_PAYLOAD(tb[TCA_NETEM_RATE]) < sizeof(*rate))
+				return -1;
+			rate = RTA_DATA(tb[TCA_NETEM_RATE]);
+		}
 	}
 
 	fprintf(f, "limit %d", qopt.limit);
@@ -359,6 +568,22 @@
 			fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
 	}
 
+	if (gimodel) {
+		fprintf(f, " loss state p13 %s", sprint_percent(gimodel->p13, b1));
+		fprintf(f, " p31 %s", sprint_percent(gimodel->p31, b1));
+		fprintf(f, " p32 %s", sprint_percent(gimodel->p32, b1));
+		fprintf(f, " p23 %s", sprint_percent(gimodel->p23, b1));
+		fprintf(f, " p14 %s", sprint_percent(gimodel->p14, b1));
+	}
+
+	if (gemodel) {
+		fprintf(f, "loss gemodel p %s",
+			sprint_percent(gemodel->p, b1));
+		fprintf(f, " r %s", sprint_percent(gemodel->r, b1));
+		fprintf(f, " 1-h %s", sprint_percent(gemodel->h, b1));
+		fprintf(f, " 1-k %s", sprint_percent(gemodel->k1, b1));
+	}
+
 	if (qopt.duplicate) {
 		fprintf(f, " duplicate %s",
 			sprint_percent(qopt.duplicate, b1));
@@ -382,6 +607,16 @@
 				sprint_percent(corrupt->correlation, b1));
 	}
 
+	if (rate && rate->rate) {
+		fprintf(f, " rate %s", sprint_rate(rate->rate, b1));
+		if (rate->packet_overhead)
+			fprintf(f, " packetoverhead %d", rate->packet_overhead);
+		if (rate->cell_size)
+			fprintf(f, " cellsize %u", rate->cell_size);
+		if (rate->cell_overhead)
+			fprintf(f, " celloverhead %d", rate->cell_overhead);
+	}
+
 	if (qopt.gap)
 		fprintf(f, " gap %lu", (unsigned long)qopt.gap);
 
diff --git a/tc/q_prio.c b/tc/q_prio.c
index 2f54d55..79b4fd0 100644
--- a/tc/q_prio.c
+++ b/tc/q_prio.c
@@ -112,7 +112,7 @@
 
 	if (tb[TCA_PRIO_MQ])
 		fprintf(f, " multiqueue: %s ",
-		    *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "on" : "off");
+			rta_getattr_u8(tb[TCA_PRIO_MQ]) ? "on" : "off");
 
 	return 0;
 }
diff --git a/tc/q_qfq.c b/tc/q_qfq.c
new file mode 100644
index 0000000..05b4d84
--- /dev/null
+++ b/tc/q_qfq.c
@@ -0,0 +1,122 @@
+/*
+ * q_qfq.c	QFQ.
+ *
+ *		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@vyatta.com>
+ *		Fabio Checconi <fabio@gandalf.sssup.it>
+ *
+ */
+#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: ... qfq\n");
+}
+
+static void explain1(const char *arg)
+{
+	fprintf(stderr, "Illegal \"%s\"\n", arg);
+}
+
+static void explain_class(void)
+{
+	fprintf(stderr, "Usage: ... qfq weight NUMBER maxpkt BYTES\n");
+}
+
+static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			 struct nlmsghdr *n)
+{
+	while (argc > 0) {
+		if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	return 0;
+}
+
+static int qfq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
+			       struct nlmsghdr *n)
+{
+	struct rtattr *tail;
+	__u32 tmp;
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);
+
+	while (argc > 0) {
+		if (matches(*argv, "weight") == 0) {
+			NEXT_ARG();
+			if (get_u32(&tmp, *argv, 10)) {
+				explain1("weight"); return -1;
+			}
+			addattr32(n, 4096, TCA_QFQ_WEIGHT, tmp);
+		} else if (matches(*argv, "maxpkt") == 0) {
+			NEXT_ARG();
+			if (get_u32(&tmp, *argv, 10)) {
+				explain1("maxpkt"); return -1;
+			}
+			addattr32(n, 4096, TCA_QFQ_LMAX, tmp);
+		} else if (strcmp(*argv, "help") == 0) {
+			explain_class();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain_class();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
+
+	return 0;
+}
+
+static int qfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_QFQ_MAX + 1];
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_QFQ_MAX, opt);
+
+	if (tb[TCA_QFQ_WEIGHT]) {
+		fprintf(f, "weight %u ",
+			rta_getattr_u32(tb[TCA_QFQ_WEIGHT]));
+	}
+
+	if (tb[TCA_QFQ_LMAX]) {
+		fprintf(f, "maxpkt %u ",
+			rta_getattr_u32(tb[TCA_QFQ_LMAX]));
+	}
+
+	return 0;
+}
+
+struct qdisc_util qfq_qdisc_util = {
+	.id		= "qfq",
+	.parse_qopt	= qfq_parse_opt,
+	.print_qopt	= qfq_print_opt,
+	.parse_copt	= qfq_parse_class_opt,
+	.print_copt	= qfq_print_opt,
+};
diff --git a/tc/q_red.c b/tc/q_red.c
index 4b1b889..89e7320 100644
--- a/tc/q_red.c
+++ b/tc/q_red.c
@@ -19,6 +19,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <string.h>
+#include <math.h>
 
 #include "utils.h"
 #include "tc_util.h"
@@ -27,8 +28,9 @@
 
 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");
+	fprintf(stderr, "Usage: ... red limit BYTES [min BYTES] [max BYTES] avpkt BYTES [burst PACKETS]\n");
+	fprintf(stderr, "               [adaptive] [probability PROBABILITY] bandwidth KBPS\n");
+	fprintf(stderr, "               [ecn] [harddrop]\n");
 }
 
 static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
@@ -38,9 +40,9 @@
 	unsigned avpkt = 0;
 	double probability = 0.02;
 	unsigned rate = 0;
-	int ecn_ok = 0;
 	int wlog;
 	__u8 sbuf[256];
+	__u32 max_P;
 	struct rtattr *tail;
 
 	memset(&opt, 0, sizeof(opt));
@@ -89,7 +91,13 @@
 				return -1;
 			}
 		} else if (strcmp(*argv, "ecn") == 0) {
-			ecn_ok = 1;
+			opt.flags |= TC_RED_ECN;
+		} else if (strcmp(*argv, "harddrop") == 0) {
+			opt.flags |= TC_RED_HARDDROP;
+		} else if (strcmp(*argv, "adaptative") == 0) {
+			opt.flags |= TC_RED_ADAPTATIVE;
+		} else if (strcmp(*argv, "adaptive") == 0) {
+			opt.flags |= TC_RED_ADAPTATIVE;
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -104,17 +112,26 @@
 	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, avpkt) is missing\n");
+	if (!opt.limit || !avpkt) {
+		fprintf(stderr, "RED: Required parameter (limit, avpkt) is missing\n");
 		return -1;
 	}
-
+	/* Compute default min/max thresholds based on
+	 * Sally Floyd's recommendations:
+	 * http://www.icir.org/floyd/REDparameters.txt
+	 */
+	if (!opt.qth_max)
+		opt.qth_max = opt.qth_min ? opt.qth_min * 3 : opt.limit / 4;
+	if (!opt.qth_min)
+		opt.qth_min = opt.qth_max / 3;
+	if (!burst)
+		burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt);
 	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);
+		fprintf(stderr, "RED: WARNING. Burst %d seems to be too 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");
@@ -126,27 +143,22 @@
 		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);
+	max_P = probability * pow(2, 32);
+	addattr_l(n, 1024, TCA_RED_MAX_P, &max_P, sizeof(max_P));
 	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 rtattr *tb[TCA_RED_MAX + 1];
 	struct tc_red_qopt *qopt;
+	__u32 max_P = 0;
 	SPRINT_BUF(b1);
 	SPRINT_BUF(b2);
 	SPRINT_BUF(b3);
@@ -154,24 +166,35 @@
 	if (opt == NULL)
 		return 0;
 
-	parse_rtattr_nested(tb, TCA_RED_STAB, opt);
+	parse_rtattr_nested(tb, TCA_RED_MAX, 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;
+
+	if (tb[TCA_RED_MAX_P] &&
+	    RTA_PAYLOAD(tb[TCA_RED_MAX_P]) >= sizeof(__u32))
+		max_P = rta_getattr_u32(tb[TCA_RED_MAX_P]);
+
 	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 (qopt->flags & TC_RED_HARDDROP)
+		fprintf(f, "harddrop ");
+	if (qopt->flags & TC_RED_ADAPTATIVE)
+		fprintf(f, "adaptive ");
 	if (show_details) {
-		fprintf(f, "ewma %u Plog %u Scell_log %u",
-			qopt->Wlog, qopt->Plog, qopt->Scell_log);
+		fprintf(f, "ewma %u ", qopt->Wlog);
+		if (max_P)
+			fprintf(f, "probability %lg ", max_P / pow(2, 32));
+		else
+			fprintf(f, "Plog %u ", qopt->Plog);
+		fprintf(f, "Scell_log %u", qopt->Scell_log);
 	}
 	return 0;
 }
diff --git a/tc/q_rr.c b/tc/q_rr.c
index 1ff3ac9..79072ad 100644
--- a/tc/q_rr.c
+++ b/tc/q_rr.c
@@ -107,7 +107,7 @@
 
 	if (tb[TCA_PRIO_MQ])
 		fprintf(f, " multiqueue: %s ",
-		    *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "on" : "off");
+			rta_getattr_u8(tb[TCA_PRIO_MQ]) ? "on" : "off");
 
 	return 0;
 }
diff --git a/tc/q_sfb.c b/tc/q_sfb.c
new file mode 100644
index 0000000..f11c33a
--- /dev/null
+++ b/tc/q_sfb.c
@@ -0,0 +1,200 @@
+/*
+ * q_sfb.c	Stochastic Fair Blue.
+ *
+ *		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:	Juliusz Chroboczek <jch@pps.jussieu.fr>
+ *
+ */
+
+
+
+#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: ... sfb [ rehash SECS ] [ db SECS ]\n"
+		"	    [ limit PACKETS ] [ max PACKETS ] [ target PACKETS ]\n"
+		"	    [ increment FLOAT ] [ decrement FLOAT ]\n"
+		"	    [ penalty_rate PPS ] [ penalty_burst PACKETS ]\n");
+}
+
+static int get_prob(__u32 *val, const char *arg)
+{
+	double d;
+	char *ptr;
+
+	if (!arg || !*arg)
+		return -1;
+	d = strtod(arg, &ptr);
+	if (!ptr || ptr == arg || d < 0.0 || d > 1.0)
+		return -1;
+	*val = (__u32)(d * SFB_MAX_PROB + 0.5);
+	return 0;
+}
+
+static int sfb_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			 struct nlmsghdr *n)
+{
+	struct tc_sfb_qopt opt;
+	struct rtattr *tail;
+
+	memset(&opt, 0, sizeof(opt));
+	opt.rehash_interval = 600*1000;
+	opt.warmup_time = 60*1000;
+	opt.penalty_rate = 10;
+	opt.penalty_burst = 20;
+	opt.increment = (SFB_MAX_PROB + 1000) / 2000;
+	opt.decrement = (SFB_MAX_PROB + 10000) / 20000;
+
+	while (argc > 0) {
+	    if (strcmp(*argv, "rehash") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.rehash_interval, *argv, 0)) {
+				fprintf(stderr, "Illegal \"rehash\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "db") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.warmup_time, *argv, 0)) {
+				fprintf(stderr, "Illegal \"db\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "max") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.max, *argv, 0)) {
+				fprintf(stderr, "Illegal \"max\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.bin_size, *argv, 0)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "increment") == 0) {
+			NEXT_ARG();
+			if (get_prob(&opt.increment, *argv)) {
+				fprintf(stderr, "Illegal \"increment\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "decrement") == 0) {
+			NEXT_ARG();
+			if (get_prob(&opt.decrement, *argv)) {
+				fprintf(stderr, "Illegal \"decrement\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "penalty_rate") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.penalty_rate, *argv, 0)) {
+				fprintf(stderr, "Illegal \"penalty_rate\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "penalty_burst") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.penalty_burst, *argv, 0)) {
+				fprintf(stderr, "Illegal \"penalty_burst\"\n");
+				return -1;
+			}
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	if (opt.max == 0) {
+		if (opt.bin_size >= 1)
+			opt.max = (opt.bin_size * 5 + 1) / 4;
+		else
+			opt.max = 25;
+	}
+	if (opt.bin_size == 0)
+		opt.bin_size = (opt.max * 4 + 3) / 5;
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	addattr_l(n, 1024, TCA_SFB_PARMS, &opt, sizeof(opt));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int sfb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[__TCA_SFB_MAX];
+	struct tc_sfb_qopt *qopt;
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_SFB_MAX, opt);
+	if (tb[TCA_SFB_PARMS] == NULL)
+		return -1;
+	qopt = RTA_DATA(tb[TCA_SFB_PARMS]);
+	if (RTA_PAYLOAD(tb[TCA_SFB_PARMS]) < sizeof(*qopt))
+		return -1;
+
+	fprintf(f,
+		"limit %d max %d target %d\n"
+		"  increment %.5f decrement %.5f penalty rate %d burst %d "
+		"(%ums %ums)",
+		qopt->limit, qopt->max, qopt->bin_size,
+		(double)qopt->increment / SFB_MAX_PROB,
+		(double)qopt->decrement / SFB_MAX_PROB,
+		qopt->penalty_rate, qopt->penalty_burst,
+		qopt->rehash_interval, qopt->warmup_time);
+
+	return 0;
+}
+
+static int sfb_print_xstats(struct qdisc_util *qu, FILE *f,
+			    struct rtattr *xstats)
+{
+    struct tc_sfb_xstats *st;
+
+    if (xstats == NULL)
+	    return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+	    return -1;
+
+    st = RTA_DATA(xstats);
+    fprintf(f,
+	    "  earlydrop %u penaltydrop %u bucketdrop %u queuedrop %u childdrop %u marked %u\n"
+	    "  maxqlen %u maxprob %.5f avgprob %.5f ",
+	    st->earlydrop, st->penaltydrop, st->bucketdrop, st->queuedrop, st->childdrop,
+	    st->marked,
+	    st->maxqlen, (double)st->maxprob / SFB_MAX_PROB,
+		(double)st->avgprob / SFB_MAX_PROB);
+
+    return 0;
+}
+
+struct qdisc_util sfb_qdisc_util = {
+	.id		= "sfb",
+	.parse_qopt	= sfb_parse_opt,
+	.print_qopt	= sfb_print_opt,
+	.print_xstats	= sfb_print_xstats,
+};
diff --git a/tc/q_sfq.c b/tc/q_sfq.c
index 71a3c9a..96f63ff 100644
--- a/tc/q_sfq.c
+++ b/tc/q_sfq.c
@@ -19,48 +19,131 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <string.h>
+#include <math.h>
 
 #include "utils.h"
 #include "tc_util.h"
+#include "tc_red.h"
 
 static void explain(void)
 {
 	fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n");
+	fprintf(stderr, "               [ divisor NUMBER ] [ flows NUMBER] [ depth NUMBER ]\n");
+	fprintf(stderr, "               [ headdrop ]\n");
+	fprintf(stderr, "               [ redflowlimit BYTES ] [ min BYTES ] [ max BYTES ]\n");
+	fprintf(stderr, "               [ avpkt BYTES ] [ burst PACKETS ] [ probability P ]\n");
+	fprintf(stderr, "               [ ecn ] [ harddrop ]\n");
 }
 
 static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
 {
-	int ok=0;
-	struct tc_sfq_qopt opt;
+	int ok = 0, red = 0;
+	struct tc_sfq_qopt_v1 opt;
+	unsigned int burst = 0;
+	int wlog;
+	unsigned int avpkt = 1000;
+	double probability = 0.02;
 
 	memset(&opt, 0, sizeof(opt));
 
 	while (argc > 0) {
 		if (strcmp(*argv, "quantum") == 0) {
 			NEXT_ARG();
-			if (get_size(&opt.quantum, *argv)) {
+			if (get_size(&opt.v0.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)) {
+			if (get_integer(&opt.v0.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)) {
+			if (get_u32(&opt.v0.limit, *argv, 0)) {
 				fprintf(stderr, "Illegal \"limit\"\n");
 				return -1;
 			}
-			if (opt.limit < 2) {
+			if (opt.v0.limit < 2) {
 				fprintf(stderr, "Illegal \"limit\", must be > 1\n");
 				return -1;
 			}
 			ok++;
+		} else if (strcmp(*argv, "divisor") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.v0.divisor, *argv, 0)) {
+				fprintf(stderr, "Illegal \"divisor\"\n");
+				return -1;
+			}
+			ok++;
+		} else if (strcmp(*argv, "flows") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.v0.flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+			ok++;
+		} else if (strcmp(*argv, "depth") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.depth, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+			ok++;
+		} else if (strcmp(*argv, "headdrop") == 0) {
+			opt.headdrop = 1;
+			ok++;
+		} else if (strcmp(*argv, "redflowlimit") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"redflowlimit\"\n");
+				return -1;
+			}
+			red++;
+		} else if (strcmp(*argv, "min") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.qth_min, *argv, 0)) {
+				fprintf(stderr, "Illegal \"min\"\n");
+				return -1;
+			}
+			red++;
+		} else if (strcmp(*argv, "max") == 0) {
+			NEXT_ARG();
+			if (get_u32(&opt.qth_max, *argv, 0)) {
+				fprintf(stderr, "Illegal \"max\"\n");
+				return -1;
+			}
+			red++;
+		} else if (strcmp(*argv, "burst") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&burst, *argv, 0)) {
+				fprintf(stderr, "Illegal \"burst\"\n");
+				return -1;
+			}
+			red++;
+		} else if (strcmp(*argv, "avpkt") == 0) {
+			NEXT_ARG();
+			if (get_size(&avpkt, *argv)) {
+				fprintf(stderr, "Illegal \"avpkt\"\n");
+				return -1;
+			}
+			red++;
+		} else if (strcmp(*argv, "probability") == 0) {
+			NEXT_ARG();
+			if (sscanf(*argv, "%lg", &probability) != 1) {
+				fprintf(stderr, "Illegal \"probability\"\n");
+				return -1;
+			}
+			red++;
+		} else if (strcmp(*argv, "ecn") == 0) {
+			opt.flags |= TC_RED_ECN;
+			red++;
+		} else if (strcmp(*argv, "harddrop") == 0) {
+			opt.flags |= TC_RED_HARDDROP;
+			red++;
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -71,8 +154,51 @@
 		}
 		argc--; argv++;
 	}
+	if (red) {
+		if (!opt.limit) {
+			fprintf(stderr, "Required parameter (redflowlimit) is missing\n");
+			return -1;
+		}
+		/* Compute default min/max thresholds based on 
+		   Sally Floyd's recommendations:
+		   http://www.icir.org/floyd/REDparameters.txt
+		*/
+		if (!opt.qth_max) 
+			opt.qth_max = opt.limit / 4;
+		if (!opt.qth_min)
+			opt.qth_min = opt.qth_max / 3;
+		if (!burst)
+			burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt);
 
-	if (ok)
+		if (opt.qth_max > opt.limit) {
+			fprintf(stderr, "\"max\" is larger than \"limit\"\n");
+			return -1;
+		}
+
+		if (opt.qth_min >= opt.qth_max) {
+			fprintf(stderr, "\"min\" is not smaller than \"max\"\n");
+			return -1;
+		}
+
+		wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt);
+		if (wlog < 0) {
+			fprintf(stderr, "SFQ: failed to calculate EWMA constant.\n");
+			return -1;
+		}
+		if (wlog >= 10)
+			fprintf(stderr, "SFQ: WARNING. Burst %u seems to be too large.\n", burst);
+		opt.Wlog = wlog;
+
+		wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability);
+		if (wlog < 0) {
+			fprintf(stderr, "SFQ: failed to calculate probability.\n");
+			return -1;
+		}
+		opt.Plog = wlog;
+		opt.max_P = probability * pow(2, 32);
+	}
+
+	if (ok || red)
 		addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
 	return 0;
 }
@@ -80,21 +206,50 @@
 static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
 	struct tc_sfq_qopt *qopt;
+	struct tc_sfq_qopt_v1 *qopt_ext = NULL;
 	SPRINT_BUF(b1);
-
+	SPRINT_BUF(b2);
+	SPRINT_BUF(b3);
 	if (opt == NULL)
 		return 0;
 
 	if (RTA_PAYLOAD(opt)  < sizeof(*qopt))
 		return -1;
+	if (RTA_PAYLOAD(opt) >= sizeof(*qopt_ext))
+		qopt_ext = RTA_DATA(opt);
 	qopt = RTA_DATA(opt);
 	fprintf(f, "limit %up ", qopt->limit);
 	fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1));
+	if (qopt_ext && qopt_ext->depth)
+		fprintf(f, "depth %u ", qopt_ext->depth);
+	if (qopt_ext && qopt_ext->headdrop)
+		fprintf(f, "headdrop ");
+
 	if (show_details) {
 		fprintf(f, "flows %u/%u ", qopt->flows, qopt->divisor);
 	}
+	fprintf(f, "divisor %u ", qopt->divisor);
 	if (qopt->perturb_period)
 		fprintf(f, "perturb %dsec ", qopt->perturb_period);
+	if (qopt_ext && qopt_ext->qth_min) {
+		fprintf(f, "\n ewma %u ", qopt_ext->Wlog);
+		fprintf(f, "min %s max %s probability %g ",
+			sprint_size(qopt_ext->qth_min, b2),
+			sprint_size(qopt_ext->qth_max, b3),
+			qopt_ext->max_P / pow(2, 32));
+		if (qopt_ext->flags & TC_RED_ECN)
+			fprintf(f, "ecn ");
+		if (show_stats) {
+			fprintf(f, "\n prob_mark %u prob_mark_head %u prob_drop %u",
+				qopt_ext->stats.prob_mark,
+				qopt_ext->stats.prob_mark_head,
+				qopt_ext->stats.prob_drop);
+			fprintf(f, "\n forced_mark %u forced_mark_head %u forced_drop %u",
+				qopt_ext->stats.forced_mark,
+				qopt_ext->stats.forced_mark_head,
+				qopt_ext->stats.forced_drop);
+		}
+	}
 	return 0;
 }
 
diff --git a/tc/tc_class.c b/tc/tc_class.c
index 9d4eea5..de18fd1 100644
--- a/tc/tc_class.c
+++ b/tc/tc_class.c
@@ -138,7 +138,7 @@
 		}
 	}
 
-	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		return 2;
 
 	return 0;
@@ -191,7 +191,7 @@
 		else
 			print_tc_classid(abuf, sizeof(abuf), t->tcm_handle);
 	}
-	fprintf(fp, "class %s %s ", (char*)RTA_DATA(tb[TCA_KIND]), abuf);
+	fprintf(fp, "class %s %s ", rta_getattr_str(tb[TCA_KIND]), abuf);
 
 	if (filter_ifindex == 0)
 		fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex));
@@ -298,7 +298,7 @@
 		return 1;
 	}
 
- 	if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) {
+ 	if (rtnl_dump_filter(&rth, print_class, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
diff --git a/tc/tc_filter.c b/tc/tc_filter.c
index 919c57c..207302f 100644
--- a/tc/tc_filter.c
+++ b/tc/tc_filter.c
@@ -73,7 +73,7 @@
 	req.t.tcm_family = AF_UNSPEC;
 
 	if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)
-		protocol = ETH_P_ALL;
+		protocol = htons(ETH_P_ALL);
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -167,7 +167,7 @@
 		}
 	}
 
- 	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) {
 		fprintf(stderr, "We have an error talking to the kernel\n");
 		return 2;
 	}
@@ -240,7 +240,7 @@
 				fprintf(fp, "pref %u ", prio);
 		}
 	}
-	fprintf(fp, "%s ", (char*)RTA_DATA(tb[TCA_KIND]));
+	fprintf(fp, "%s ", rta_getattr_str(tb[TCA_KIND]));
 	q = get_filter_kind(RTA_DATA(tb[TCA_KIND]));
 	if (tb[TCA_OPTIONS]) {
 		if (q)
@@ -341,7 +341,7 @@
 		return 1;
 	}
 
- 	if (rtnl_dump_filter(&rth, print_filter, stdout, NULL, NULL) < 0) {
+ 	if (rtnl_dump_filter(&rth, print_filter, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c
index c7f2988..3f932a7 100644
--- a/tc/tc_qdisc.c
+++ b/tc/tc_qdisc.c
@@ -186,7 +186,7 @@
 		req.t.tcm_ifindex = idx;
 	}
 
- 	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
 		return 2;
 
 	return 0;
@@ -229,7 +229,7 @@
 	if (n->nlmsg_type == RTM_DELQDISC)
 		fprintf(fp, "deleted ");
 
-	fprintf(fp, "qdisc %s %x: ", (char*)RTA_DATA(tb[TCA_KIND]), t->tcm_handle>>16);
+	fprintf(fp, "qdisc %s %x: ", rta_getattr_str(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)
@@ -323,7 +323,7 @@
 		return 1;
 	}
 
- 	if (rtnl_dump_filter(&rth, print_qdisc, stdout, NULL, NULL) < 0) {
+ 	if (rtnl_dump_filter(&rth, print_qdisc, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
diff --git a/tc/tc_red.c b/tc/tc_red.c
index 8f9bde0..81a83bd 100644
--- a/tc/tc_red.c
+++ b/tc/tc_red.c
@@ -56,8 +56,11 @@
 	double W = 0.5;
 	double a = (double)burst + 1 - (double)qmin/avpkt;
 
-	if (a < 1.0)
+	if (a < 1.0) {
+		fprintf(stderr, "tc_red_eval_ewma() burst %u is too small ?"
+				" Try burst %u\n", burst, 1 + qmin/avpkt);
 		return -1;
+	}
 	for (wlog=1; wlog<32; wlog++, W /= 2) {
 		if (a <= (1 - pow(1-W, burst))/W)
 			return wlog;
@@ -76,9 +79,7 @@
 	double maxtime = 31/lW;
 	int clog;
 	int i;
-	double tmp;
 
-	tmp = maxtime;
 	for (clog=0; clog<32; clog++) {
 		if (maxtime/(1<<clog) < 512)
 			break;
diff --git a/tc/tc_util.c b/tc/tc_util.c
index fe2c7eb..926ed08 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -25,7 +25,7 @@
 #include "tc_util.h"
 
 #ifndef LIBDIR
-#define LIBDIR "/usr/lib/"
+#define LIBDIR "/usr/lib"
 #endif
 
 const char *get_tc_lib(void)
@@ -203,7 +203,7 @@
 
 	if (use_iec) {
 		if (tmp >= 1000.0*1024.0*1024.0)
-			snprintf(buf, len, "%.0fMibit", tmp/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
@@ -352,33 +352,6 @@
 	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);
diff --git a/tc/tc_util.h b/tc/tc_util.h
index 8f6b82d..cefd241 100644
--- a/tc/tc_util.h
+++ b/tc/tc_util.h
@@ -62,7 +62,6 @@
 
 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_time(unsigned *time, const char *str);
@@ -70,7 +69,6 @@
 
 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_time(char *buf, int len, __u32 time);
 extern void print_linklayer(char *buf, int len, unsigned linklayer);
@@ -80,7 +78,6 @@
 extern char * sprint_tc_classid(__u32 h, char *buf);
 extern char * sprint_time(__u32 time, char *buf);
 extern char * sprint_ticks(__u32 ticks, char *buf);
-extern char * sprint_percent(__u32 percent, char *buf);
 extern char * sprint_linklayer(unsigned linklayer, char *buf);
 
 extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats);